AppView in a box as a Vite plugin thing hatk.dev
2
fork

Configure Feed

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

refactor: extract core atproto lexicons to JSON files, add clean build step

Move com.atproto.repo.strongRef, com.atproto.label.defs, and
com.atproto.moderation.defs from inline objects in lexicon-resolve.ts
to src/lexicons/ JSON files. The resolver now loads all built-in
schemas from disk. createReport lexicon references
com.atproto.repo.strongRef instead of a local #strongRef def.

Build now cleans dist/ before compiling to prevent stale artifacts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+163 -131
+2 -1
packages/hatk/package.json
··· 28 28 "./renderer": "./dist/renderer.js" 29 29 }, 30 30 "scripts": { 31 - "build": "tsc -p tsconfig.build.json && cp -r src/templates dist/templates && cp -r src/lexicons dist/lexicons", 31 + "clean": "rm -rf dist", 32 + "build": "npm run clean && tsc -p tsconfig.build.json && cp -r src/templates dist/templates && cp -r src/lexicons dist/lexicons", 32 33 "prepublishOnly": "npm run build" 33 34 }, 34 35 "dependencies": {
+3 -5
packages/hatk/src/cli.ts
··· 292 292 `, 293 293 ) 294 294 295 - // Copy core framework lexicons under dev.hatk namespace 296 - const coreLexDir = join(dir, 'lexicons', 'dev', 'hatk') 297 - mkdirSync(coreLexDir, { recursive: true }) 298 - const builtinLexDir = join(import.meta.dirname!, 'lexicons', 'dev', 'hatk') 299 - cpSync(builtinLexDir, coreLexDir, { recursive: true }) 295 + // Copy core framework lexicons (dev.hatk.* and dependencies like com.atproto.repo.strongRef) 296 + const builtinLexDir = join(import.meta.dirname!, 'lexicons') 297 + cpSync(builtinLexDir, join(dir, 'lexicons'), { recursive: true }) 300 298 301 299 writeFileSync( 302 300 join(dir, 'seeds', 'seed.ts'),
+28 -116
packages/hatk/src/lexicon-resolve.ts
··· 2 2 // and recursively resolves all $ref dependencies. 3 3 4 4 import { isValidDid } from '@bigmoves/lexicon' 5 + import { join } from 'node:path' 6 + import { readdirSync, readFileSync, statSync } from 'node:fs' 5 7 6 8 // --- Authority --- 7 9 ··· 80 82 } 81 83 } 82 84 83 - // --- Built-in core schemas (not published via DNS) --- 84 - 85 - const coreSchemas: Record<string, Lexicon> = { 86 - 'com.atproto.repo.strongRef': { 87 - lexicon: 1, 88 - id: 'com.atproto.repo.strongRef', 89 - description: 'A URI with a content-hash fingerprint.', 90 - defs: { 91 - main: { 92 - type: 'object', 93 - required: ['uri', 'cid'], 94 - properties: { uri: { type: 'string', format: 'at-uri' }, cid: { type: 'string', format: 'cid' } }, 95 - }, 96 - }, 97 - }, 98 - 'com.atproto.label.defs': { 99 - lexicon: 1, 100 - id: 'com.atproto.label.defs', 101 - defs: { 102 - label: { 103 - type: 'object', 104 - description: 'Metadata tag on an atproto resource (eg, repo or record).', 105 - required: ['src', 'uri', 'val', 'cts'], 106 - properties: { 107 - ver: { type: 'integer' }, 108 - src: { type: 'string', format: 'did' }, 109 - uri: { type: 'string', format: 'uri' }, 110 - cid: { type: 'string', format: 'cid' }, 111 - val: { type: 'string', maxLength: 128 }, 112 - neg: { type: 'boolean' }, 113 - cts: { type: 'string', format: 'datetime' }, 114 - exp: { type: 'string', format: 'datetime' }, 115 - sig: { type: 'bytes' }, 116 - }, 117 - }, 118 - selfLabels: { 119 - type: 'object', 120 - description: 'Metadata tags on an atproto record, published by the author within the record.', 121 - required: ['values'], 122 - properties: { values: { type: 'array', items: { type: 'ref', ref: '#selfLabel' }, maxLength: 10 } }, 123 - }, 124 - selfLabel: { type: 'object', required: ['val'], properties: { val: { type: 'string', maxLength: 128 } } }, 125 - labelValueDefinition: { 126 - type: 'object', 127 - description: 'Declares a label value and its expected interpretations and behaviors.', 128 - required: ['identifier', 'severity', 'blurs', 'locales'], 129 - properties: { 130 - identifier: { type: 'string', maxLength: 100 }, 131 - severity: { type: 'string', knownValues: ['inform', 'alert', 'none'] }, 132 - blurs: { type: 'string', knownValues: ['content', 'media', 'none'] }, 133 - defaultSetting: { type: 'string', knownValues: ['ignore', 'warn', 'hide'] }, 134 - adultOnly: { type: 'boolean' }, 135 - locales: { type: 'array', items: { type: 'ref', ref: '#labelValueDefinitionStrings' } }, 136 - }, 137 - }, 138 - labelValueDefinitionStrings: { 139 - type: 'object', 140 - required: ['lang', 'name', 'description'], 141 - properties: { 142 - lang: { type: 'string', format: 'language' }, 143 - name: { type: 'string', maxLength: 640 }, 144 - description: { type: 'string', maxLength: 100000 }, 145 - }, 146 - }, 147 - labelValue: { 148 - type: 'string', 149 - knownValues: [ 150 - '!hide', 151 - '!no-promote', 152 - '!warn', 153 - '!no-unauthenticated', 154 - 'dmca-violation', 155 - 'doxxing', 156 - 'porn', 157 - 'sexual', 158 - 'nudity', 159 - 'nsfl', 160 - 'gore', 161 - ], 162 - }, 163 - }, 164 - }, 165 - 'com.atproto.moderation.defs': { 166 - lexicon: 1, 167 - id: 'com.atproto.moderation.defs', 168 - defs: { 169 - reasonType: { 170 - type: 'string', 171 - knownValues: [ 172 - 'com.atproto.moderation.defs#reasonSpam', 173 - 'com.atproto.moderation.defs#reasonViolation', 174 - 'com.atproto.moderation.defs#reasonMisleading', 175 - 'com.atproto.moderation.defs#reasonSexual', 176 - 'com.atproto.moderation.defs#reasonRude', 177 - 'com.atproto.moderation.defs#reasonOther', 178 - 'com.atproto.moderation.defs#reasonAppeal', 179 - ], 180 - }, 181 - reasonSpam: { type: 'token', description: 'Spam: frequent unwanted promotion, replies, mentions.' }, 182 - reasonViolation: { type: 'token', description: 'Direct violation of server rules, laws, terms of service.' }, 183 - reasonMisleading: { type: 'token', description: 'Misleading identity, affiliation, or content.' }, 184 - reasonSexual: { type: 'token', description: 'Unwanted or mislabeled sexual content.' }, 185 - reasonRude: { type: 'token', description: 'Rude, harassing, explicit, or otherwise unwelcoming behavior.' }, 186 - reasonOther: { type: 'token', description: 'Reports not falling under another report category.' }, 187 - reasonAppeal: { type: 'token', description: 'Appeal a previously taken moderation action.' }, 188 - subjectType: { 189 - type: 'string', 190 - description: 'Tag describing a type of subject that might be reported.', 191 - knownValues: ['account', 'record', 'chat'], 192 - }, 193 - }, 194 - }, 195 - } 196 - 197 - // --- Resolver --- 198 - 199 85 interface Lexicon { 200 86 lexicon: number 201 87 id: string 202 88 defs: Record<string, any> 203 89 [key: string]: any 204 90 } 91 + 92 + // --- Built-in core schemas (loaded from src/lexicons/) --- 93 + 94 + function loadCoreSchemas(): Record<string, Lexicon> { 95 + const schemas: Record<string, Lexicon> = {} 96 + const lexDir = join(import.meta.dirname!, 'lexicons') 97 + 98 + function walk(dir: string) { 99 + for (const entry of readdirSync(dir)) { 100 + const full = join(dir, entry) 101 + if (statSync(full).isDirectory()) { 102 + walk(full) 103 + } else if (entry.endsWith('.json')) { 104 + const lexicon = JSON.parse(readFileSync(full, 'utf-8')) 105 + if (lexicon.id) schemas[lexicon.id] = lexicon 106 + } 107 + } 108 + } 109 + 110 + try { walk(lexDir) } catch {} 111 + return schemas 112 + } 113 + 114 + const coreSchemas: Record<string, Lexicon> = loadCoreSchemas() 115 + 116 + // --- Resolver --- 205 117 206 118 function refToNsid(ref: string): string | null { 207 119 let nsid = ref.startsWith('lex:') ? ref.slice(4) : ref
+75
packages/hatk/src/lexicons/com/atproto/label/defs.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "com.atproto.label.defs", 4 + "defs": { 5 + "label": { 6 + "type": "object", 7 + "description": "Metadata tag on an atproto resource (eg, repo or record).", 8 + "required": ["src", "uri", "val", "cts"], 9 + "properties": { 10 + "ver": { "type": "integer" }, 11 + "src": { "type": "string", "format": "did" }, 12 + "uri": { "type": "string", "format": "uri" }, 13 + "cid": { "type": "string", "format": "cid" }, 14 + "val": { "type": "string", "maxLength": 128 }, 15 + "neg": { "type": "boolean" }, 16 + "cts": { "type": "string", "format": "datetime" }, 17 + "exp": { "type": "string", "format": "datetime" }, 18 + "sig": { "type": "bytes" } 19 + } 20 + }, 21 + "selfLabels": { 22 + "type": "object", 23 + "description": "Metadata tags on an atproto record, published by the author within the record.", 24 + "required": ["values"], 25 + "properties": { 26 + "values": { "type": "array", "items": { "type": "ref", "ref": "#selfLabel" }, "maxLength": 10 } 27 + } 28 + }, 29 + "selfLabel": { 30 + "type": "object", 31 + "required": ["val"], 32 + "properties": { 33 + "val": { "type": "string", "maxLength": 128 } 34 + } 35 + }, 36 + "labelValueDefinition": { 37 + "type": "object", 38 + "description": "Declares a label value and its expected interpretations and behaviors.", 39 + "required": ["identifier", "severity", "blurs", "locales"], 40 + "properties": { 41 + "identifier": { "type": "string", "maxLength": 100 }, 42 + "severity": { "type": "string", "knownValues": ["inform", "alert", "none"] }, 43 + "blurs": { "type": "string", "knownValues": ["content", "media", "none"] }, 44 + "defaultSetting": { "type": "string", "knownValues": ["ignore", "warn", "hide"] }, 45 + "adultOnly": { "type": "boolean" }, 46 + "locales": { "type": "array", "items": { "type": "ref", "ref": "#labelValueDefinitionStrings" } } 47 + } 48 + }, 49 + "labelValueDefinitionStrings": { 50 + "type": "object", 51 + "required": ["lang", "name", "description"], 52 + "properties": { 53 + "lang": { "type": "string", "format": "language" }, 54 + "name": { "type": "string", "maxLength": 640 }, 55 + "description": { "type": "string", "maxLength": 100000 } 56 + } 57 + }, 58 + "labelValue": { 59 + "type": "string", 60 + "knownValues": [ 61 + "!hide", 62 + "!no-promote", 63 + "!warn", 64 + "!no-unauthenticated", 65 + "dmca-violation", 66 + "doxxing", 67 + "porn", 68 + "sexual", 69 + "nudity", 70 + "nsfl", 71 + "gore" 72 + ] 73 + } 74 + } 75 + }
+30
packages/hatk/src/lexicons/com/atproto/moderation/defs.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "com.atproto.moderation.defs", 4 + "defs": { 5 + "reasonType": { 6 + "type": "string", 7 + "knownValues": [ 8 + "com.atproto.moderation.defs#reasonSpam", 9 + "com.atproto.moderation.defs#reasonViolation", 10 + "com.atproto.moderation.defs#reasonMisleading", 11 + "com.atproto.moderation.defs#reasonSexual", 12 + "com.atproto.moderation.defs#reasonRude", 13 + "com.atproto.moderation.defs#reasonOther", 14 + "com.atproto.moderation.defs#reasonAppeal" 15 + ] 16 + }, 17 + "reasonSpam": { "type": "token", "description": "Spam: frequent unwanted promotion, replies, mentions." }, 18 + "reasonViolation": { "type": "token", "description": "Direct violation of server rules, laws, terms of service." }, 19 + "reasonMisleading": { "type": "token", "description": "Misleading identity, affiliation, or content." }, 20 + "reasonSexual": { "type": "token", "description": "Unwanted or mislabeled sexual content." }, 21 + "reasonRude": { "type": "token", "description": "Rude, harassing, explicit, or otherwise unwelcoming behavior." }, 22 + "reasonOther": { "type": "token", "description": "Reports not falling under another report category." }, 23 + "reasonAppeal": { "type": "token", "description": "Appeal a previously taken moderation action." }, 24 + "subjectType": { 25 + "type": "string", 26 + "description": "Tag describing a type of subject that might be reported.", 27 + "knownValues": ["account", "record", "chat"] 28 + } 29 + } 30 + }
+24
packages/hatk/src/lexicons/com/atproto/repo/strongRef.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "com.atproto.repo.strongRef", 4 + "description": "A URI with a content-hash fingerprint.", 5 + "defs": { 6 + "main": { 7 + "type": "object", 8 + "required": [ 9 + "uri", 10 + "cid" 11 + ], 12 + "properties": { 13 + "uri": { 14 + "type": "string", 15 + "format": "at-uri" 16 + }, 17 + "cid": { 18 + "type": "string", 19 + "format": "cid" 20 + } 21 + } 22 + } 23 + } 24 + }
+1 -9
packages/hatk/src/lexicons/dev/hatk/createReport.json
··· 14 14 "subject": { 15 15 "type": "union", 16 16 "description": "The account or record being reported.", 17 - "refs": ["#repoRef", "#strongRef"] 17 + "refs": ["#repoRef", "com.atproto.repo.strongRef"] 18 18 }, 19 19 "label": { "type": "string", "description": "Label identifier for the report reason." }, 20 20 "reason": { "type": "string", "maxLength": 2000, "description": "Optional free-text explanation." } ··· 42 42 "required": ["did"], 43 43 "properties": { 44 44 "did": { "type": "string", "format": "did" } 45 - } 46 - }, 47 - "strongRef": { 48 - "type": "object", 49 - "required": ["uri", "cid"], 50 - "properties": { 51 - "uri": { "type": "string", "format": "at-uri" }, 52 - "cid": { "type": "string", "format": "cid" } 53 45 } 54 46 } 55 47 }