···11/**
22 * @module
33 *
44- * `Uint8Array`s bring memory-efficient(ish) byte handling to browsers - they are similar to Node.js `Buffer`s but lack a lot of the utility methods present on that class.
55- * This module exports a number of function that let you do common operations - joining Uint8Arrays together, seeing if they have the same contents etc.
44+ * Simple `Uint8Array` utilities for AT Protocol.
65 *
76 * ## alloc(size)
87 *
+6
common/mod.ts
···11+/**
22+ * # AT Protocol Common Utilities
33+ *
44+ * Shared TypeScript code for other @atproto/* packages.
55+ * This package is oriented towards writing servers.
66+ */
17export * as check from "./check.ts";
28export * as util from "./util.ts";
39
+43
crypto/mod.ts
···11+/**
22+ * # AT Protocol Cryptographic Utilities
33+ *
44+ * This module provides cryptographic helpers for AT Protocol.
55+ *
66+ * 2 cryptographic systems are currently supported:
77+ * - P-256 elliptic curve: aka "NIST P-256", aka secp256r1 (note the r), aka prime256v1
88+ * - K-256 elliptic curve: aka "NIST K-256", aka secp256k1 (note the k)
99+ *
1010+ * The details of cryptography in atproto are described in {@link https://atproto.com/specs/cryptography | the specification.}
1111+ * This includes string encodings, validity of "low-S" signatures, byte representation
1212+ * "compression", hashing, and more.
1313+ *
1414+ * @example
1515+ * ```typescript
1616+ * import { verifySignature, Secp256k1Keypair, P256Keypair } from '@atp/crypto'
1717+ *
1818+ * // generate a new random K-256 private key
1919+ * const keypair = await Secp256k1Keypair.create({ exportable: true })
2020+ *
2121+ * // sign binary data, resulting signature bytes.
2222+ * // SHA-256 hash of data is what actually gets signed.
2323+ * // signature output is often base64-encoded.
2424+ * const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8])
2525+ * const sig = await keypair.sign(data)
2626+ *
2727+ * // serialize the public key as a did:key string, which includes key type metadata
2828+ * const pubDidKey = keypair.did()
2929+ * console.log(pubDidKey)
3030+ *
3131+ * // output would look something like: 'did:key:zQ3shVRtgqTRHC7Lj4DYScoDgReNpsDp3HBnuKBKt1FSXKQ38'
3232+ *
3333+ * // verify signature using public key
3434+ * const ok = verifySignature(pubDidKey, data, sig)
3535+ * if (!ok) {
3636+ * throw new Error('Uh oh, something is fishy')
3737+ * } else {
3838+ * console.log('Success')
3939+ * }
4040+ * ```
4141+ *
4242+ * @module
4343+ */
144export * from "./const.ts";
245export * from "./did.ts";
346export * from "./multibase.ts";
+31-2
lex-cli/mod.ts
···11-#!/usr/bin/env node
22-11+/**
22+ * # AT Protocol Lexicon CLI
33+ *
44+ * A command-line interface for generating docs, servers, and clients
55+ * from AT Protocol lexicon files.
66+ *
77+ * Turn lexicon files into:
88+ * - Markdown documentation
99+ * - Server implementation
1010+ * - TypeScript objects
1111+ * - Client implementation
1212+ *
1313+ * ## Installation
1414+ * ```bash
1515+ * deno install -g jsr:@atp/lex-cli@latest --name lex-cli
1616+ * ```
1717+ * Alternatively, you can use it without installation by referring to
1818+ * it as `jsr:@atp/lex-cli` instead of `lex-cli`.
1919+ *
2020+ * @example Generate Server
2121+ * ```bash
2222+ * lex-cli gen-server -i <path/to/lexicon/dir> -o <output/path>
2323+ * ```
2424+ *
2525+ * @example Generate Client
2626+ * ```bash
2727+ * lex-cli gen-api -i <path/to/lexicon/dir> -o <output/path>
2828+ * ```
2929+ *
3030+ * @module
3131+ */
332import { Command } from "@cliffy/command";
433import { genApi, genMd, genServer, genTsObj } from "./cmd/index.ts";
534import process from "node:process";
+31
lexicon/mod.ts
···11+/**
22+ * # AT Protocol Lexicon Validation Utility
33+ *
44+ * This module provides utilities for validating and working with AT Protocol lexicons
55+ * in TypeScript.
66+ *
77+ * @example Validate a lexicon
88+ * ```typescript
99+ * import { Lexicon } from "@atp/lexicon";
1010+ *
1111+ * // create your lexicons collection
1212+ * const lex = new Lexicons()
1313+ *
1414+ * // add your lexicons
1515+ * lex.add({
1616+ * lex: 1,
1717+ * id: 'com.example.post',
1818+ * defs: {
1919+ * // ...
2020+ * }
2121+ * })
2222+ *
2323+ * // validate
2424+ * lex.assertValidRecord('com.example.record', {$type: 'com.example.record', ...})
2525+ * lex.assertValidXrpcParams('com.example.query', {...})
2626+ * lex.assertValidXrpcInput('com.example.procedure', {...})
2727+ * lex.assertValidXrpcOutput('com.example.query', {...})
2828+ * ```
2929+ *
3030+ * @module
3131+ */
132export * from "./types.ts";
233export * from "./lexicons.ts";
334export * from "./blob-refs.ts";
+15
syntax/aturi.ts
···66// --path----- --query-- --hash--
77const RELATIVE_REGEX = /^(\/[^?#\s]*)?(\?[^#\s]+)?(#[^\s]+)?$/i;
8899+/**
1010+ * AT URI Validation and parsing class
1111+ *
1212+ * @example AT URIs
1313+ * ```typescript
1414+ * import { AtUri } from '@atp/syntax'
1515+ *
1616+ * const uri = new AtUri('at://bob.com/com.example.post/1234')
1717+ * uri.protocol // => 'at:'
1818+ * uri.origin // => 'at://bob.com'
1919+ * uri.hostname // => 'bob.com'
2020+ * uri.collection // => 'com.example.post'
2121+ * uri.rkey // => '1234'
2222+ * ```
2323+ */
924export class AtUri {
1025 hash: string;
1126 host: string;
+49-20
syntax/handle.ts
···1818 // "should" "never" actually resolve and get registered in production
1919];
20202121-// Handle constraints, in English:
2222-// - must be a possible domain name
2323-// - RFC-1035 is commonly referenced, but has been updated. eg, RFC-3696,
2424-// section 2. and RFC-3986, section 3. can now have leading numbers (eg,
2525-// 4chan.org)
2626-// - "labels" (sub-names) are made of ASCII letters, digits, hyphens
2727-// - can not start or end with a hyphen
2828-// - TLD (last component) should not start with a digit
2929-// - can't end with a hyphen (can end with digit)
3030-// - each segment must be between 1 and 63 characters (not including any periods)
3131-// - overall length can't be more than 253 characters
3232-// - separated by (ASCII) periods; does not start or end with period
3333-// - case insensitive
3434-// - domains (handles) are equal if they are the same lower-case
3535-// - punycode allowed for internationalization
3636-// - no whitespace, null bytes, joining chars, etc
3737-// - does not validate whether domain or TLD exists, or is a reserved or
3838-// special TLD (eg, .onion or .local)
3939-// - does not validate punycode
2121+/**
2222+ * Ensure a handle is valid
2323+ * @throws If handle is invalid
2424+ *
2525+ * Handle constraints, in English:
2626+ * - must be a possible domain name
2727+ * - RFC-1035 is commonly referenced, but has been updated. eg, RFC-3696,
2828+ * section 2. and RFC-3986, section 3. can now have leading numbers (eg,
2929+ * 4chan.org)
3030+ * - "labels" (sub-names) are made of ASCII letters, digits, hyphens
3131+ * - can not start or end with a hyphen
3232+ * - TLD (last component) should not start with a digit
3333+ * - can't end with a hyphen (can end with digit)
3434+ * - each segment must be between 1 and 63 characters (not including any periods)
3535+ * - overall length can't be more than 253 characters
3636+ * - separated by (ASCII) periods; does not start or end with period
3737+ * - case insensitive
3838+ * - domains (handles) are equal if they are the same lower-case
3939+ * - punycode allowed for internationalization
4040+ * - no whitespace, null bytes, joining chars, etc
4141+ * - does not validate whether domain or TLD exists, or is a reserved or
4242+ * special TLD (eg, .onion or .local)
4343+ * - does not validate punycode
4444+ */
4045export const ensureValidHandle = (handle: string): void => {
4146 // check that all chars are boring ASCII
4247 if (!/^[a-zA-Z0-9.-]*$/.test(handle)) {
···7378 }
7479};
75807676-// simple regex translation of above constraints
8181+/**
8282+ * Ensure a handle is valid using a regex pattern.
8383+ * @throws If handle is invalid
8484+ */
7785export const ensureValidHandleRegex = (handle: string): void => {
7886 if (
7987 !/^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/
···8896 }
8997};
90989999+/**
100100+ * Converts a handle to lowercase.
101101+ */
91102export const normalizeHandle = (handle: string): string => {
92103 return handle.toLowerCase();
93104};
94105106106+/**
107107+ * Converts a handle to lowercase and ensures it is valid.
108108+ * @returns The normalized handle if it is valid
109109+ * @throws If handle is invalid
110110+ */
95111export const normalizeAndEnsureValidHandle = (handle: string): string => {
96112 const normalized = normalizeHandle(handle);
97113 ensureValidHandle(normalized);
98114 return normalized;
99115};
100116117117+/**
118118+ * Checks if a handle is valid and returns a boolean.
119119+ *
120120+ * @returns True if handle is valid
121121+ */
101122export const isValidHandle = (handle: string): boolean => {
102123 try {
103124 ensureValidHandle(handle);
···111132 return true;
112133};
113134135135+/**
136136+ * Check if a TLD is valid.
137137+ *
138138+ * Disallowed TLDs: {@linkcode DISALLOWED_TLDS}
139139+ */
114140export const isValidTld = (handle: string): boolean => {
115141 return !DISALLOWED_TLDS.some((domain) => handle.endsWith(domain));
116142};
117143144144+/**
145145+ * Thrown when a handle is invalid.
146146+ */
118147export class InvalidHandleError extends Error {}
119148/** @deprecated Never used */
120149export class ReservedHandleError extends Error {}