Frosted AT Protocol SDK for Dart with tasty lexicon fillings
poptart website content 8.primitive-validation.md
3.9 kB · 174 lines
Markdown
at main


title: Primitive Validation description: Validate handles, DIDs, AT URIs, and NSIDs before building records or sending requests. #

Use poptart_primitives at input boundaries. It gives you small, focused helpers for AT Protocol identifiers without pulling in the full client stack.

The umbrella poptart package exports common primitives, but libraries and validation-heavy tools can depend on poptart_primitives directly.

dart pub add poptart_primitives
import 'package:poptart_primitives/at_primitives.dart';

Handles #

Use handles when users type account names.

final normalized = normalizeAndEnsureValidHandle('Alice.Example.com');
print(normalized); // alice.example.com

If you want a boolean check for form validation, use isValidHandle:

String? validateHandleField(String value) {
  if (!isValidHandle(value)) {
    return 'Enter a valid handle, like alice.example.com';
  }

  return null;
}

Use ensureValidHandle when invalid input is exceptional:

void submitHandle(String handle) {
  ensureValidHandle(handle);
  // Safe to continue with a handle-shaped value.
}

DIDs #

Use DIDs when you already have a stable account identifier.

void validateDid(String did) {
  ensureValidDid(did);
}

The validator checks DID syntax. It does not resolve the DID or prove that the DID exists. Use poptart_did_plc when you need to resolve did:plc:* documents.

Accept A Handle Or DID #

Many AT Protocol APIs accept either a handle or a DID. Model that explicitly:

bool isHandleOrDid(String value) {
  return isValidHandle(value) || _isValidDid(value);
}

bool _isValidDid(String value) {
  try {
    ensureValidDid(value);
    return true;
  } on InvalidDidError {
    return false;
  }
}

Use this at CLI, form, and HTTP boundaries before constructing requests.

AT URIs #

AT URIs identify repos, records, and fragments inside records. Use ensureValidAtUri when validating text input:

void validateSubject(String uri) {
  ensureValidAtUri(uri);
}

Use AtUri.parse when you want structured access:

final uri = AtUri.parse(
  'at://did:plc:example/app.bsky.feed.post/3kabc123',
);

print(uri.hostname); // did:plc:example
print(uri.collection); // app.bsky.feed.post
print(uri.rkey); // 3kabc123

Use AtUri.make when you already have the pieces:

final uri = AtUri.make(
  'did:plc:example',
  'app.bsky.feed.post',
  '3kabc123',
);

print(uri);

NSIDs #

NSIDs name lexicon schemas and XRPC methods.

final method = NSID.parse('app.bsky.actor.getProfile');

print(method.authority); // bsky.app
print(method.name); // getProfile

Use NSID.create when you have a domain-style authority and a method name:

final method = NSID.create('bsky.app', 'actor.getProfile');
print(method); // app.bsky.actor.getProfile

Use ensureValidNsid for validation-only code:

void validateCollection(String collection) {
  ensureValidNsid(collection);
}

Validate Repo Writes #

Primitive validation is especially useful before writes. You can reject bad input before creating generated models or making network calls.

void validatePostWriteTarget(String repo, String collection) {
  if (!isValidHandle(repo) && !_isValidDid(repo)) {
    throw ArgumentError.value(repo, 'repo', 'Expected handle or DID');
  }

  final nsid = NSID.parse(collection);
  if (nsid.toString() != 'app.bsky.feed.post') {
    throw ArgumentError.value(
      collection,
      'collection',
      'Expected app.bsky.feed.post',
    );
  }
}

Validation Versus Resolution #

Validation only checks syntax. It does not prove that a handle resolves to a DID, a DID exists, or an AT URI points at a real record.

Use primitives before making a request. Use protocol clients or the PLC client when you need to resolve or fetch real network data.