Something went wrong. Try again.
WIP: A simple cli for daily tangled use cases and AI integration. This is for my personal use right now, but happy if others get mileage fro
Something went wrong. Try again.
5.2 kB · 188 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189import { z } from 'zod';
/** * Validation schema for AT Protocol handle * Supports standard Bluesky handles (user.bsky.social) and custom domains (example.com) */export const handleSchema: z.ZodString = z .string() .min(1, 'Handle cannot be empty') .regex( /^([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])?$/, 'Invalid handle format. Must be a valid domain (e.g., user.bsky.social or example.com)' );
/** * Validation schema for AT Protocol DID */export const didSchema: z.ZodString = z .string() .min(1, 'DID cannot be empty') .regex( /^did:[a-z]+:[a-zA-Z0-9._:%-]*[a-zA-Z0-9._-]$/, 'Invalid DID format. Must start with "did:" followed by method and identifier' );
/** * Validation schema for app password * AT Protocol app passwords are typically 19 characters with dashes */export const appPasswordSchema: z.ZodString = z .string() .min(1, 'Password cannot be empty') .max(1000, 'Password is too long');
/** * Validation schema for identifier (handle or DID) */export const identifierSchema: z.ZodUnion<[typeof handleSchema, typeof didSchema]> = z.union([ handleSchema, didSchema,]);
/** * Validate a handle * @throws {z.ZodError} if validation fails */export function validateHandle(handle: string): string { return handleSchema.parse(handle);}
/** * Validate a DID * @throws {z.ZodError} if validation fails */export function validateDid(did: string): string { return didSchema.parse(did);}
/** * Validate an identifier (handle or DID) * @throws {z.ZodError} if validation fails */export function validateIdentifier(identifier: string): string { return identifierSchema.parse(identifier);}
/** * Validate an app password * @throws {z.ZodError} if validation fails */export function validateAppPassword(password: string): string { return appPasswordSchema.parse(password);}
/** * Safe validation that returns success/error instead of throwing */export function safeValidateHandle( handle: string): { success: true; data: string } | { success: false; error: string } { const result = handleSchema.safeParse(handle); if (result.success) { return { success: true, data: result.data }; } return { success: false, error: result.error.issues[0]?.message ?? 'Validation failed' };}
/** * Safe validation that returns success/error instead of throwing */export function safeValidateDid( did: string): { success: true; data: string } | { success: false; error: string } { const result = didSchema.safeParse(did); if (result.success) { return { success: true, data: result.data }; } return { success: false, error: result.error.issues[0]?.message ?? 'Validation failed' };}
/** * Safe validation that returns success/error instead of throwing */export function safeValidateIdentifier( identifier: string): { success: true; data: string } | { success: false; error: string } { const result = identifierSchema.safeParse(identifier); if (result.success) { return { success: true, data: result.data }; } return { success: false, error: result.error.issues[0]?.message ?? 'Validation failed' };}
/** * Validation schema for Tangled-specific DID (did:plc: format only) */export const tangledDidSchema: z.ZodString = z .string() .regex(/^did:plc:[a-z0-9]+$/, 'Invalid Tangled DID format. Expected: did:plc:...');
/** * Check if a string is a valid AT Protocol handle * Returns true/false without throwing */export function isValidHandle(handle: string): boolean { return handleSchema.safeParse(handle).success;}
/** * Check if a string is a valid Tangled DID (did:plc: format) * Returns true/false without throwing */export function isValidTangledDid(did: string): boolean { return tangledDidSchema.safeParse(did).success;}
/** * Validation schema for issue title * Titles must be 1-256 characters */export const issueTitleSchema: z.ZodString = z .string() .min(1, 'Issue title cannot be empty') .max(256, 'Issue title must be 256 characters or less');
/** * Validation schema for issue body * Body is optional but limited to 50,000 characters */export const issueBodySchema: z.ZodOptional<z.ZodString> = z .string() .max(50000, 'Issue body must be 50,000 characters or less') .optional();
/** * Validation schema for AT-URI * Format: at://did:method:identifier/collection[/rkey] */export const atUriSchema: z.ZodString = z .string() .regex( /^at:\/\/did:[a-z]+:[a-zA-Z0-9._:%-]+\/[a-zA-Z0-9._-]+(?:\.[a-zA-Z0-9._-]+)*(?:\/[a-zA-Z0-9._-]+)?$/, 'Invalid AT-URI format. Expected: at://did:method:id/collection[/rkey]' );
/** * Validate an issue title * @throws {z.ZodError} if validation fails */export function validateIssueTitle(title: string): string { return issueTitleSchema.parse(title);}
/** * Validate an issue body * @throws {z.ZodError} if validation fails */export function validateIssueBody(body: string): string { return issueBodySchema.parse(body) ?? '';}
/** * Check if a string is a valid AT-URI * Returns true/false without throwing */export function isValidAtUri(uri: string): boolean { return atUriSchema.safeParse(uri).success;}