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 from it! :)
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add validation helpers for Tangled DIDs and handles

Add boolean validation functions to check DID and handle formats:
- isValidHandle(): Check if string is valid AT Protocol handle
- isValidTangledDid(): Check if string is valid Tangled DID (did:plc: format)

These helpers return true/false without throwing, useful for parsing
operations. Includes comprehensive test coverage.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

+58
+23
src/utils/validation.ts
··· 107 107 } 108 108 return { success: false, error: result.error.issues[0]?.message ?? 'Validation failed' }; 109 109 } 110 + 111 + /** 112 + * Validation schema for Tangled-specific DID (did:plc: format only) 113 + */ 114 + export const tangledDidSchema = z 115 + .string() 116 + .regex(/^did:plc:[a-z0-9]+$/, 'Invalid Tangled DID format. Expected: did:plc:...'); 117 + 118 + /** 119 + * Check if a string is a valid AT Protocol handle 120 + * Returns true/false without throwing 121 + */ 122 + export function isValidHandle(handle: string): boolean { 123 + return handleSchema.safeParse(handle).success; 124 + } 125 + 126 + /** 127 + * Check if a string is a valid Tangled DID (did:plc: format) 128 + * Returns true/false without throwing 129 + */ 130 + export function isValidTangledDid(did: string): boolean { 131 + return tangledDidSchema.safeParse(did).success; 132 + }
+35
tests/utils/validation.test.ts
··· 1 1 import { describe, expect, it } from 'vitest'; 2 2 import { 3 + isValidHandle, 4 + isValidTangledDid, 3 5 safeValidateDid, 4 6 safeValidateHandle, 5 7 safeValidateIdentifier, ··· 136 138 }); 137 139 }); 138 140 }); 141 + 142 + describe('Boolean Validation Helpers', () => { 143 + describe('isValidHandle', () => { 144 + it('should return true for valid handles', () => { 145 + expect(isValidHandle('user.bsky.social')).toBe(true); 146 + expect(isValidHandle('markbennett.ca')).toBe(true); 147 + expect(isValidHandle('sub.domain.example.com')).toBe(true); 148 + }); 149 + 150 + it('should return false for invalid handles', () => { 151 + expect(isValidHandle('invalid')).toBe(false); 152 + expect(isValidHandle('.starts-with-dot.com')).toBe(false); 153 + expect(isValidHandle('ends-with-dot.com.')).toBe(false); 154 + expect(isValidHandle('has space.com')).toBe(false); 155 + expect(isValidHandle('')).toBe(false); 156 + }); 157 + }); 158 + 159 + describe('isValidTangledDid', () => { 160 + it('should return true for valid Tangled DIDs (did:plc: format)', () => { 161 + expect(isValidTangledDid('did:plc:b2mcbcamkwyznc5fkplwlxbf')).toBe(true); 162 + expect(isValidTangledDid('did:plc:abc123xyz')).toBe(true); 163 + }); 164 + 165 + it('should return false for invalid Tangled DIDs', () => { 166 + expect(isValidTangledDid('did:plc:')).toBe(false); 167 + expect(isValidTangledDid('did:plc:ABC123')).toBe(false); // uppercase not allowed 168 + expect(isValidTangledDid('did:web:example.com')).toBe(false); // wrong method 169 + expect(isValidTangledDid('not-a-did')).toBe(false); 170 + expect(isValidTangledDid('')).toBe(false); 171 + }); 172 + }); 173 + });