social components inlay.at
atproto components sdui
100
fork

Configure Feed

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

init

danabra.mov 6e11e322

+17025
+27
.gitignore
··· 1 + # Dependencies 2 + node_modules/ 3 + 4 + # Next.js 5 + .next/ 6 + out/ 7 + 8 + # Environment 9 + .env 10 + .env.local 11 + 12 + # IDE 13 + .idea/ 14 + .vscode/ 15 + 16 + # OS 17 + .DS_Store 18 + 19 + # Build 20 + dist/ 21 + *.tsbuildinfo 22 + 23 + # Worktrees 24 + .worktrees/ 25 + 26 + tap.db* 27 + playwright-report
+1
.husky/pre-commit
··· 1 + npx lint-staged
+4
.lintstagedrc.json
··· 1 + { 2 + "*.{ts,tsx}": ["eslint --fix", "prettier --write"], 3 + "*.{json,css}": ["prettier --write"] 4 + }
+6
.prettierignore
··· 1 + node_modules/ 2 + .next/ 3 + dist/ 4 + drizzle/ 5 + docs/ 6 + *.md
+6
.prettierrc
··· 1 + { 2 + "semi": true, 3 + "singleQuote": false, 4 + "tabWidth": 2, 5 + "trailingComma": "es5" 6 + }
+21
eslint.config.mjs
··· 1 + import tseslint from "typescript-eslint"; 2 + 3 + const eslintConfig = [ 4 + ...tseslint.configs.recommended, 5 + { 6 + files: ["**/*.ts", "**/*.tsx"], 7 + rules: { 8 + "@typescript-eslint/no-unused-vars": [ 9 + "error", 10 + { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }, 11 + ], 12 + "@typescript-eslint/no-explicit-any": "off", 13 + "@typescript-eslint/no-namespace": "off", 14 + }, 15 + }, 16 + { 17 + ignores: ["docs/**", "dist/**", "generated/**"], 18 + }, 19 + ]; 20 + 21 + export default eslintConfig;
+5
generated/at.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * as inlay from "./at/inlay";
+14
generated/at/inlay.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * as Binding from "./inlay/Binding"; 6 + export * as pack from "./inlay/pack"; 7 + export * as Fragment from "./inlay/Fragment"; 8 + export * as Maybe from "./inlay/Maybe"; 9 + export * as Missing from "./inlay/Missing"; 10 + export * as Placeholder from "./inlay/Placeholder"; 11 + export * as Slot from "./inlay/Slot"; 12 + export * as Throw from "./inlay/Throw"; 13 + export * as component from "./inlay/component"; 14 + export * as defs from "./inlay/defs";
+39
generated/at/inlay/Binding.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "./defs.defs"; 7 + 8 + const $nsid = "at.inlay.Binding"; 9 + 10 + export { $nsid }; 11 + 12 + /** Data placeholder resolved by the host during deserialization. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ path: l.array(l.string({ maxLength: 256 })) }), 17 + l.payload( 18 + "application/json", 19 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 20 + ) 21 + ); 22 + export { main }; 23 + 24 + export type $Params = l.InferMethodParams<typeof main>; 25 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 26 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 27 + typeof main, 28 + B 29 + >; 30 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 31 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 32 + typeof main, 33 + B 34 + >; 35 + 36 + export const $lxm = main.nsid, 37 + $params = main.parameters, 38 + $input = main.input, 39 + $output = main.output;
+6
generated/at/inlay/Binding.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Binding.defs"; 6 + export * as $defs from "./Binding.defs";
+43
generated/at/inlay/Fragment.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "./defs.defs"; 7 + 8 + const $nsid = "at.inlay.Fragment"; 9 + 10 + export { $nsid }; 11 + 12 + /** Renders children without a wrapper element. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ 17 + children: l.array( 18 + l.ref<InlayDefs.Element>((() => InlayDefs.element) as any) 19 + ), 20 + }), 21 + l.payload( 22 + "application/json", 23 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 24 + ) 25 + ); 26 + export { main }; 27 + 28 + export type $Params = l.InferMethodParams<typeof main>; 29 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 30 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 31 + typeof main, 32 + B 33 + >; 34 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 35 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 36 + typeof main, 37 + B 38 + >; 39 + 40 + export const $lxm = main.nsid, 41 + $params = main.parameters, 42 + $input = main.input, 43 + $output = main.output;
+6
generated/at/inlay/Fragment.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Fragment.defs"; 6 + export * as $defs from "./Fragment.defs";
+46
generated/at/inlay/Maybe.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "./defs.defs"; 7 + 8 + const $nsid = "at.inlay.Maybe"; 9 + 10 + export { $nsid }; 11 + 12 + /** Renders children if all bindings resolve; otherwise renders fallback. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ 17 + children: l.array( 18 + l.ref<InlayDefs.Element>((() => InlayDefs.element) as any) 19 + ), 20 + fallback: l.optional( 21 + l.ref<InlayDefs.Element>((() => InlayDefs.element) as any) 22 + ), 23 + }), 24 + l.payload( 25 + "application/json", 26 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 27 + ) 28 + ); 29 + export { main }; 30 + 31 + export type $Params = l.InferMethodParams<typeof main>; 32 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 33 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 34 + typeof main, 35 + B 36 + >; 37 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 38 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 39 + typeof main, 40 + B 41 + >; 42 + 43 + export const $lxm = main.nsid, 44 + $params = main.parameters, 45 + $input = main.input, 46 + $output = main.output;
+6
generated/at/inlay/Maybe.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Maybe.defs"; 6 + export * as $defs from "./Maybe.defs";
+41
generated/at/inlay/Missing.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "./defs.defs"; 7 + 8 + const $nsid = "at.inlay.Missing"; 9 + 10 + export { $nsid }; 11 + 12 + /** Signals missing data. Components return this when a required value is absent. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ 17 + path: l.array(l.string({ maxLength: 256 }), { minLength: 1 }), 18 + }), 19 + l.payload( 20 + "application/json", 21 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 22 + ) 23 + ); 24 + export { main }; 25 + 26 + export type $Params = l.InferMethodParams<typeof main>; 27 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 28 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 29 + typeof main, 30 + B 31 + >; 32 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 33 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 34 + typeof main, 35 + B 36 + >; 37 + 38 + export const $lxm = main.nsid, 39 + $params = main.parameters, 40 + $input = main.input, 41 + $output = main.output;
+6
generated/at/inlay/Missing.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Missing.defs"; 6 + export * as $defs from "./Missing.defs";
+44
generated/at/inlay/Placeholder.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "./defs.defs"; 7 + 8 + const $nsid = "at.inlay.Placeholder"; 9 + 10 + export { $nsid }; 11 + 12 + /** Shows fallback content while children are loading. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ 17 + children: l.array( 18 + l.ref<InlayDefs.Element>((() => InlayDefs.element) as any) 19 + ), 20 + fallback: l.ref<InlayDefs.Element>((() => InlayDefs.element) as any), 21 + }), 22 + l.payload( 23 + "application/json", 24 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 25 + ) 26 + ); 27 + export { main }; 28 + 29 + export type $Params = l.InferMethodParams<typeof main>; 30 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 31 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 32 + typeof main, 33 + B 34 + >; 35 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 36 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 37 + typeof main, 38 + B 39 + >; 40 + 41 + export const $lxm = main.nsid, 42 + $params = main.parameters, 43 + $input = main.input, 44 + $output = main.output;
+6
generated/at/inlay/Placeholder.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Placeholder.defs"; 6 + export * as $defs from "./Placeholder.defs";
+39
generated/at/inlay/Slot.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "./defs.defs"; 7 + 8 + const $nsid = "at.inlay.Slot"; 9 + 10 + export { $nsid }; 11 + 12 + /** Opaque reference to a stashed element during XRPC calls. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ id: l.string({ maxLength: 256 }) }), 17 + l.payload( 18 + "application/json", 19 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 20 + ) 21 + ); 22 + export { main }; 23 + 24 + export type $Params = l.InferMethodParams<typeof main>; 25 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 26 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 27 + typeof main, 28 + B 29 + >; 30 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 31 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 32 + typeof main, 33 + B 34 + >; 35 + 36 + export const $lxm = main.nsid, 37 + $params = main.parameters, 38 + $input = main.input, 39 + $output = main.output;
+6
generated/at/inlay/Slot.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Slot.defs"; 6 + export * as $defs from "./Slot.defs";
+42
generated/at/inlay/Throw.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "./defs.defs"; 7 + 8 + const $nsid = "at.inlay.Throw"; 9 + 10 + export { $nsid }; 11 + 12 + /** Error node produced when component resolution fails. The host decides how to render it. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ 17 + message: l.string({ maxLength: 10000 }), 18 + stack: l.optional(l.string({ maxLength: 100000 })), 19 + }), 20 + l.payload( 21 + "application/json", 22 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 23 + ) 24 + ); 25 + export { main }; 26 + 27 + export type $Params = l.InferMethodParams<typeof main>; 28 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 29 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 30 + typeof main, 31 + B 32 + >; 33 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 34 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 35 + typeof main, 36 + B 37 + >; 38 + 39 + export const $lxm = main.nsid, 40 + $params = main.parameters, 41 + $input = main.input, 42 + $output = main.output;
+6
generated/at/inlay/Throw.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Throw.defs"; 6 + export * as $defs from "./Throw.defs";
+305
generated/at/inlay/component.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "./defs.defs"; 7 + 8 + const $nsid = "at.inlay.component"; 9 + 10 + export { $nsid }; 11 + 12 + /** Component record - declares an implementation of a type */ 13 + type Main = { 14 + $type: "at.inlay.component"; 15 + 16 + /** 17 + * NSID this component implements (also the XRPC procedure) 18 + */ 19 + type: l.NsidString; 20 + 21 + /** 22 + * How this component is rendered. Omit for primitives rendered by the host. 23 + */ 24 + body?: 25 + | l.$Typed<BodyExternal> 26 + | l.$Typed<BodyTemplate> 27 + | l.Unknown$TypedObject; 28 + 29 + /** 30 + * What kinds of pages this component is a view for 31 + */ 32 + view?: ( 33 + | l.$Typed<ViewRecord> 34 + | l.$Typed<ViewCollection> 35 + | l.$Typed<ViewIdentity> 36 + | l.Unknown$TypedObject 37 + )[]; 38 + 39 + /** 40 + * Data types this component can render. Used for data-driven discovery. 41 + */ 42 + accepts?: AcceptsEntry[]; 43 + 44 + /** 45 + * Ordered list of pack URIs (import stack). First pack that exports an NSID wins. 46 + */ 47 + imports?: l.AtUriString[]; 48 + 49 + /** 50 + * Platform-managed deployment metadata 51 + */ 52 + via?: l.$Typed<InlayDefs.ViaValtown> | l.Unknown$TypedObject; 53 + description?: string; 54 + createdAt?: l.DatetimeString; 55 + 56 + /** 57 + * Last update timestamp. Set by the publish flow to bust cached responses. 58 + */ 59 + updatedAt?: l.DatetimeString; 60 + }; 61 + 62 + export type { Main }; 63 + 64 + /** Component record - declares an implementation of a type */ 65 + const main = l.record<"tid", Main>( 66 + "tid", 67 + $nsid, 68 + l.object({ 69 + type: l.string({ format: "nsid" }), 70 + body: l.optional( 71 + l.typedUnion( 72 + [ 73 + l.typedRef<BodyExternal>((() => bodyExternal) as any), 74 + l.typedRef<BodyTemplate>((() => bodyTemplate) as any), 75 + ], 76 + false 77 + ) 78 + ), 79 + view: l.optional( 80 + l.array( 81 + l.typedUnion( 82 + [ 83 + l.typedRef<ViewRecord>((() => viewRecord) as any), 84 + l.typedRef<ViewCollection>((() => viewCollection) as any), 85 + l.typedRef<ViewIdentity>((() => viewIdentity) as any), 86 + ], 87 + false 88 + ) 89 + ) 90 + ), 91 + accepts: l.optional( 92 + l.array(l.ref<AcceptsEntry>((() => acceptsEntry) as any)) 93 + ), 94 + imports: l.optional(l.array(l.string({ format: "at-uri" }))), 95 + via: l.optional( 96 + l.typedUnion( 97 + [l.typedRef<InlayDefs.ViaValtown>((() => InlayDefs.viaValtown) as any)], 98 + false 99 + ) 100 + ), 101 + description: l.optional(l.string({ maxGraphemes: 1000, maxLength: 10000 })), 102 + createdAt: l.optional(l.string({ format: "datetime" })), 103 + updatedAt: l.optional(l.string({ format: "datetime" })), 104 + }) 105 + ); 106 + 107 + export { main }; 108 + 109 + export const $isTypeOf = /*#__PURE__*/ main.isTypeOf.bind(main), 110 + $build = /*#__PURE__*/ main.build.bind(main), 111 + $type = /*#__PURE__*/ main.$type; 112 + export const $assert = /*#__PURE__*/ main.assert.bind(main), 113 + $check = /*#__PURE__*/ main.check.bind(main), 114 + $cast = /*#__PURE__*/ main.cast.bind(main), 115 + $ifMatches = /*#__PURE__*/ main.ifMatches.bind(main), 116 + $matches = /*#__PURE__*/ main.matches.bind(main), 117 + $parse = /*#__PURE__*/ main.parse.bind(main), 118 + $safeParse = /*#__PURE__*/ main.safeParse.bind(main), 119 + $validate = /*#__PURE__*/ main.validate.bind(main), 120 + $safeValidate = /*#__PURE__*/ main.safeValidate.bind(main); 121 + 122 + /** Component rendered by calling a remote XRPC endpoint */ 123 + type BodyExternal = { 124 + $type?: "at.inlay.component#bodyExternal"; 125 + 126 + /** 127 + * DID of the service hosting this component 128 + */ 129 + did: l.DidString; 130 + }; 131 + 132 + export type { BodyExternal }; 133 + 134 + /** Component rendered by calling a remote XRPC endpoint */ 135 + const bodyExternal = l.typedObject<BodyExternal>( 136 + $nsid, 137 + "bodyExternal", 138 + l.object({ did: l.string({ format: "did" }) }) 139 + ); 140 + 141 + export { bodyExternal }; 142 + 143 + /** Component rendered by the host from a serialized element tree */ 144 + type BodyTemplate = { 145 + $type?: "at.inlay.component#bodyTemplate"; 146 + 147 + /** 148 + * Serialized element tree with bindings 149 + */ 150 + node: l.LexMap; 151 + }; 152 + 153 + export type { BodyTemplate }; 154 + 155 + /** Component rendered by the host from a serialized element tree */ 156 + const bodyTemplate = l.typedObject<BodyTemplate>( 157 + $nsid, 158 + "bodyTemplate", 159 + l.object({ node: l.lexMap() }) 160 + ); 161 + 162 + export { bodyTemplate }; 163 + 164 + /** Component is a view for individual records of a collection. Omit collection for a generic record view. */ 165 + type ViewRecord = { 166 + $type?: "at.inlay.component#viewRecord"; 167 + 168 + /** 169 + * The collection this component views. Omit for any-collection. 170 + */ 171 + collection?: l.NsidString; 172 + }; 173 + 174 + export type { ViewRecord }; 175 + 176 + /** Component is a view for individual records of a collection. Omit collection for a generic record view. */ 177 + const viewRecord = l.typedObject<ViewRecord>( 178 + $nsid, 179 + "viewRecord", 180 + l.object({ collection: l.optional(l.string({ format: "nsid" })) }) 181 + ); 182 + 183 + export { viewRecord }; 184 + 185 + /** Component is a view for a collection listing. Omit collection for a generic collection view. */ 186 + type ViewCollection = { 187 + $type?: "at.inlay.component#viewCollection"; 188 + 189 + /** 190 + * The collection this component lists. Omit for any-collection. 191 + */ 192 + collection?: l.NsidString; 193 + }; 194 + 195 + export type { ViewCollection }; 196 + 197 + /** Component is a view for a collection listing. Omit collection for a generic collection view. */ 198 + const viewCollection = l.typedObject<ViewCollection>( 199 + $nsid, 200 + "viewCollection", 201 + l.object({ collection: l.optional(l.string({ format: "nsid" })) }) 202 + ); 203 + 204 + export { viewCollection }; 205 + 206 + /** Component is a view for an identity (person/DID). */ 207 + type ViewIdentity = { $type?: "at.inlay.component#viewIdentity" }; 208 + 209 + export type { ViewIdentity }; 210 + 211 + /** Component is a view for an identity (person/DID). */ 212 + const viewIdentity = l.typedObject<ViewIdentity>( 213 + $nsid, 214 + "viewIdentity", 215 + l.object({}) 216 + ); 217 + 218 + export { viewIdentity }; 219 + 220 + /** Declares a data type this component can handle, routed to a specific prop. */ 221 + type AcceptsEntry = { 222 + $type?: "at.inlay.component#acceptsEntry"; 223 + 224 + /** 225 + * Lexicon field type. 226 + */ 227 + type: 228 + | "string" 229 + | "integer" 230 + | "boolean" 231 + | "blob" 232 + | "cid-link" 233 + | "bytes" 234 + | l.UnknownString; 235 + 236 + /** 237 + * String format constraint. Only applies when type is 'string'. 238 + */ 239 + format?: 240 + | "at-uri" 241 + | "did" 242 + | "datetime" 243 + | "uri" 244 + | "handle" 245 + | "at-identifier" 246 + | "nsid" 247 + | "cid" 248 + | "language" 249 + | "record-key" 250 + | "tid" 251 + | l.UnknownString; 252 + 253 + /** 254 + * Prop to bind matched data to. 255 + */ 256 + prop: string; 257 + 258 + /** 259 + * For at-uri strings, restricts to this collection. 260 + */ 261 + collection?: l.NsidString; 262 + }; 263 + 264 + export type { AcceptsEntry }; 265 + 266 + /** Declares a data type this component can handle, routed to a specific prop. */ 267 + const acceptsEntry = l.typedObject<AcceptsEntry>( 268 + $nsid, 269 + "acceptsEntry", 270 + l.object({ 271 + type: l.string<{ 272 + maxLength: 128; 273 + knownValues: [ 274 + "string", 275 + "integer", 276 + "boolean", 277 + "blob", 278 + "cid-link", 279 + "bytes", 280 + ]; 281 + }>({ maxLength: 128 }), 282 + format: l.optional( 283 + l.string<{ 284 + maxLength: 64; 285 + knownValues: [ 286 + "at-uri", 287 + "did", 288 + "datetime", 289 + "uri", 290 + "handle", 291 + "at-identifier", 292 + "nsid", 293 + "cid", 294 + "language", 295 + "record-key", 296 + "tid", 297 + ]; 298 + }>({ maxLength: 64 }) 299 + ), 300 + prop: l.string({ maxLength: 256 }), 301 + collection: l.optional(l.string({ format: "nsid" })), 302 + }) 303 + ); 304 + 305 + export { acceptsEntry };
+6
generated/at/inlay/component.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./component.defs"; 6 + export * as $defs from "./component.defs";
+186
generated/at/inlay/defs.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + 7 + const $nsid = "at.inlay.defs"; 8 + 9 + export { $nsid }; 10 + 11 + type ViaValtown = { 12 + $type?: "at.inlay.defs#viaValtown"; 13 + 14 + /** 15 + * Val Town val UUID 16 + */ 17 + valId: string; 18 + }; 19 + 20 + export type { ViaValtown }; 21 + 22 + const viaValtown = l.typedObject<ViaValtown>( 23 + $nsid, 24 + "viaValtown", 25 + l.object({ valId: l.string({ maxLength: 128 }) }) 26 + ); 27 + 28 + export { viaValtown }; 29 + 30 + /** Cache tag: depend on a specific record, collection, or identity. */ 31 + type TagRecord = { 32 + $type?: "at.inlay.defs#tagRecord"; 33 + 34 + /** 35 + * AT URI at record, collection, or identity granularity 36 + */ 37 + uri: l.AtUriString; 38 + }; 39 + 40 + export type { TagRecord }; 41 + 42 + /** Cache tag: depend on a specific record, collection, or identity. */ 43 + const tagRecord = l.typedObject<TagRecord>( 44 + $nsid, 45 + "tagRecord", 46 + l.object({ uri: l.string({ format: "at-uri" }) }) 47 + ); 48 + 49 + export { tagRecord }; 50 + 51 + /** Cache tag: depend on backlink relationships to a subject. */ 52 + type TagLink = { 53 + $type?: "at.inlay.defs#tagLink"; 54 + 55 + /** 56 + * Subject AT URI that is linked to 57 + */ 58 + subject: l.AtUriString; 59 + 60 + /** 61 + * Collection NSID of the linking records. Omit for any collection. 62 + */ 63 + from?: l.NsidString; 64 + }; 65 + 66 + export type { TagLink }; 67 + 68 + /** Cache tag: depend on backlink relationships to a subject. */ 69 + const tagLink = l.typedObject<TagLink>( 70 + $nsid, 71 + "tagLink", 72 + l.object({ 73 + subject: l.string({ format: "at-uri" }), 74 + from: l.optional(l.string({ format: "nsid" })), 75 + }) 76 + ); 77 + 78 + export { tagLink }; 79 + 80 + /** Cache lifetime and invalidation tags returned by XRPC components. */ 81 + type CachePolicy = { 82 + $type?: "at.inlay.defs#cachePolicy"; 83 + 84 + /** 85 + * How frequently the underlying data changes 86 + */ 87 + life?: "seconds" | "minutes" | "hours" | "max" | l.UnknownString; 88 + 89 + /** 90 + * Data dependencies for cache invalidation 91 + */ 92 + tags?: (l.$Typed<TagRecord> | l.$Typed<TagLink> | l.Unknown$TypedObject)[]; 93 + }; 94 + 95 + export type { CachePolicy }; 96 + 97 + /** Cache lifetime and invalidation tags returned by XRPC components. */ 98 + const cachePolicy = l.typedObject<CachePolicy>( 99 + $nsid, 100 + "cachePolicy", 101 + l.object({ 102 + life: l.optional( 103 + l.string<{ 104 + maxLength: 32; 105 + knownValues: ["seconds", "minutes", "hours", "max"]; 106 + }>({ maxLength: 32 }) 107 + ), 108 + tags: l.optional( 109 + l.array( 110 + l.typedUnion( 111 + [ 112 + l.typedRef<TagRecord>((() => tagRecord) as any), 113 + l.typedRef<TagLink>((() => tagLink) as any), 114 + ], 115 + false 116 + ) 117 + ) 118 + ), 119 + }) 120 + ); 121 + 122 + export { cachePolicy }; 123 + 124 + /** Standard response from a component render call. */ 125 + type Response = { 126 + $type?: "at.inlay.defs#response"; 127 + 128 + /** 129 + * Rendered element tree 130 + */ 131 + node: Element; 132 + 133 + /** 134 + * Cache lifetime and invalidation tags 135 + */ 136 + cache: CachePolicy; 137 + }; 138 + 139 + export type { Response }; 140 + 141 + /** Standard response from a component render call. */ 142 + const response = l.typedObject<Response>( 143 + $nsid, 144 + "response", 145 + l.object({ 146 + node: l.ref<Element>((() => element) as any), 147 + cache: l.ref<CachePolicy>((() => cachePolicy) as any), 148 + }) 149 + ); 150 + 151 + export { response }; 152 + 153 + /** A renderable Inlay element. */ 154 + type Element = { 155 + $type?: "at.inlay.defs#element"; 156 + 157 + /** 158 + * NSID of the component to render. 159 + */ 160 + type: l.NsidString; 161 + 162 + /** 163 + * Properties to pass to the component. 164 + */ 165 + props?: l.LexMap; 166 + 167 + /** 168 + * Stable key that identifies the component among its siblings. 169 + */ 170 + key?: string; 171 + }; 172 + 173 + export type { Element }; 174 + 175 + /** A renderable Inlay element. */ 176 + const element = l.typedObject<Element>( 177 + $nsid, 178 + "element", 179 + l.object({ 180 + type: l.string({ format: "nsid" }), 181 + props: l.optional(l.lexMap()), 182 + key: l.optional(l.string({ maxLength: 256 })), 183 + }) 184 + ); 185 + 186 + export { element };
+6
generated/at/inlay/defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./defs.defs"; 6 + export * as $defs from "./defs.defs";
+80
generated/at/inlay/pack.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + 7 + const $nsid = "at.inlay.pack"; 8 + 9 + export { $nsid }; 10 + 11 + /** A list of type to component exports */ 12 + type Main = { 13 + $type: "at.inlay.pack"; 14 + 15 + /** 16 + * Short slug for the pack (e.g. "core", "ui") 17 + */ 18 + name: string; 19 + 20 + /** 21 + * Type to component mappings 22 + */ 23 + exports: Export$0[]; 24 + createdAt?: l.DatetimeString; 25 + }; 26 + 27 + export type { Main }; 28 + 29 + /** A list of type to component exports */ 30 + const main = l.record<"tid", Main>( 31 + "tid", 32 + $nsid, 33 + l.object({ 34 + name: l.string({ maxLength: 64 }), 35 + exports: l.array(l.ref<Export$0>((() => export$0) as any)), 36 + createdAt: l.optional(l.string({ format: "datetime" })), 37 + }) 38 + ); 39 + 40 + export { main }; 41 + 42 + export const $isTypeOf = /*#__PURE__*/ main.isTypeOf.bind(main), 43 + $build = /*#__PURE__*/ main.build.bind(main), 44 + $type = /*#__PURE__*/ main.$type; 45 + export const $assert = /*#__PURE__*/ main.assert.bind(main), 46 + $check = /*#__PURE__*/ main.check.bind(main), 47 + $cast = /*#__PURE__*/ main.cast.bind(main), 48 + $ifMatches = /*#__PURE__*/ main.ifMatches.bind(main), 49 + $matches = /*#__PURE__*/ main.matches.bind(main), 50 + $parse = /*#__PURE__*/ main.parse.bind(main), 51 + $safeParse = /*#__PURE__*/ main.safeParse.bind(main), 52 + $validate = /*#__PURE__*/ main.validate.bind(main), 53 + $safeValidate = /*#__PURE__*/ main.safeValidate.bind(main); 54 + 55 + type Export$0 = { 56 + $type?: "at.inlay.pack#export"; 57 + 58 + /** 59 + * NSID of the type being exported 60 + */ 61 + type: l.NsidString; 62 + 63 + /** 64 + * AT-URI of the component record 65 + */ 66 + component: l.AtUriString; 67 + }; 68 + 69 + export type { Export$0 as Export }; 70 + 71 + const export$0 = l.typedObject<Export$0>( 72 + $nsid, 73 + "export", 74 + l.object({ 75 + type: l.string({ format: "nsid" }), 76 + component: l.string({ format: "at-uri" }), 77 + }) 78 + ); 79 + 80 + export { export$0 as "export" };
+6
generated/at/inlay/pack.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./pack.defs"; 6 + export * as $defs from "./pack.defs";
+6
generated/index.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * as at from "./at"; 6 + export * as org from "./org";
+5
generated/org.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * as atsui from "./org/atsui";
+22
generated/org/atsui.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * as Avatar from "./atsui/Avatar"; 6 + export * as Blob from "./atsui/Blob"; 7 + export * as Caption from "./atsui/Caption"; 8 + export * as Clip from "./atsui/Clip"; 9 + export * as Cover from "./atsui/Cover"; 10 + export * as Editor from "./atsui/Editor"; 11 + export * as Fill from "./atsui/Fill"; 12 + export * as Grid from "./atsui/Grid"; 13 + export * as Heading from "./atsui/Heading"; 14 + export * as Link from "./atsui/Link"; 15 + export * as List from "./atsui/List"; 16 + export * as Record from "./atsui/Record"; 17 + export * as Row from "./atsui/Row"; 18 + export * as Stack from "./atsui/Stack"; 19 + export * as Tabs from "./atsui/Tabs"; 20 + export * as Text from "./atsui/Text"; 21 + export * as Timestamp from "./atsui/Timestamp"; 22 + export * as Title from "./atsui/Title";
+52
generated/org/atsui/Avatar.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Avatar"; 9 + 10 + export { $nsid }; 11 + 12 + /** Circular profile image at a fixed size. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ 17 + src: l.lexMap(), 18 + did: l.optional(l.string({ format: "did" })), 19 + size: l.optional( 20 + l.withDefault( 21 + l.string<{ 22 + maxLength: 32; 23 + knownValues: ["xsmall", "small", "medium", "large"]; 24 + }>({ maxLength: 32 }), 25 + "medium" 26 + ) 27 + ), 28 + lift: l.optional(l.boolean()), 29 + }), 30 + l.payload( 31 + "application/json", 32 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 33 + ) 34 + ); 35 + export { main }; 36 + 37 + export type $Params = l.InferMethodParams<typeof main>; 38 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 39 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 40 + typeof main, 41 + B 42 + >; 43 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 44 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 45 + typeof main, 46 + B 47 + >; 48 + 49 + export const $lxm = main.nsid, 50 + $params = main.parameters, 51 + $input = main.input, 52 + $output = main.output;
+6
generated/org/atsui/Avatar.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Avatar.defs"; 6 + export * as $defs from "./Avatar.defs";
+63
generated/org/atsui/Blob.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Blob"; 9 + 10 + export { $nsid }; 11 + 12 + /** Displays an AT Protocol blob as an image. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ 17 + src: l.lexMap(), 18 + did: l.string({ format: "did" }), 19 + ratio: l.optional(l.ref<AspectRatio>((() => aspectRatio) as any)), 20 + fit: l.optional(l.enum(["cover", "contain"])), 21 + }), 22 + l.payload( 23 + "application/json", 24 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 25 + ) 26 + ); 27 + export { main }; 28 + 29 + export type $Params = l.InferMethodParams<typeof main>; 30 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 31 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 32 + typeof main, 33 + B 34 + >; 35 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 36 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 37 + typeof main, 38 + B 39 + >; 40 + 41 + export const $lxm = main.nsid, 42 + $params = main.parameters, 43 + $input = main.input, 44 + $output = main.output; 45 + 46 + type AspectRatio = { 47 + $type?: "org.atsui.Blob#aspectRatio"; 48 + width: number; 49 + height: number; 50 + }; 51 + 52 + export type { AspectRatio }; 53 + 54 + const aspectRatio = l.typedObject<AspectRatio>( 55 + $nsid, 56 + "aspectRatio", 57 + l.object({ 58 + width: l.integer({ minimum: 1 }), 59 + height: l.integer({ minimum: 1 }), 60 + }) 61 + ); 62 + 63 + export { aspectRatio };
+6
generated/org/atsui/Blob.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Blob.defs"; 6 + export * as $defs from "./Blob.defs";
+39
generated/org/atsui/Caption.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Caption"; 9 + 10 + export { $nsid }; 11 + 12 + /** Secondary text for timestamps, metadata, or supplementary information. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ children: l.lexMap() }), 17 + l.payload( 18 + "application/json", 19 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 20 + ) 21 + ); 22 + export { main }; 23 + 24 + export type $Params = l.InferMethodParams<typeof main>; 25 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 26 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 27 + typeof main, 28 + B 29 + >; 30 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 31 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 32 + typeof main, 33 + B 34 + >; 35 + 36 + export const $lxm = main.nsid, 37 + $params = main.parameters, 38 + $input = main.input, 39 + $output = main.output;
+6
generated/org/atsui/Caption.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Caption.defs"; 6 + export * as $defs from "./Caption.defs";
+64
generated/org/atsui/Clip.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Clip"; 9 + 10 + export { $nsid }; 11 + 12 + /** Clips child content to a height range relative to its own width. Use min and max to bound the box proportions. Set both to the same value to force an exact aspect ratio. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ 17 + min: l.optional(l.ref<AspectRatio>((() => aspectRatio) as any)), 18 + max: l.optional(l.ref<AspectRatio>((() => aspectRatio) as any)), 19 + children: l.array( 20 + l.ref<InlayDefs.Element>((() => InlayDefs.element) as any) 21 + ), 22 + }), 23 + l.payload( 24 + "application/json", 25 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 26 + ) 27 + ); 28 + export { main }; 29 + 30 + export type $Params = l.InferMethodParams<typeof main>; 31 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 32 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 33 + typeof main, 34 + B 35 + >; 36 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 37 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 38 + typeof main, 39 + B 40 + >; 41 + 42 + export const $lxm = main.nsid, 43 + $params = main.parameters, 44 + $input = main.input, 45 + $output = main.output; 46 + 47 + type AspectRatio = { 48 + $type?: "org.atsui.Clip#aspectRatio"; 49 + width: number; 50 + height: number; 51 + }; 52 + 53 + export type { AspectRatio }; 54 + 55 + const aspectRatio = l.typedObject<AspectRatio>( 56 + $nsid, 57 + "aspectRatio", 58 + l.object({ 59 + width: l.integer({ minimum: 1 }), 60 + height: l.integer({ minimum: 1 }), 61 + }) 62 + ); 63 + 64 + export { aspectRatio };
+6
generated/org/atsui/Clip.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Clip.defs"; 6 + export * as $defs from "./Clip.defs";
+42
generated/org/atsui/Cover.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Cover"; 9 + 10 + export { $nsid }; 11 + 12 + /** Full-width background image. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ 17 + src: l.lexMap(), 18 + did: l.optional(l.string({ format: "did" })), 19 + }), 20 + l.payload( 21 + "application/json", 22 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 23 + ) 24 + ); 25 + export { main }; 26 + 27 + export type $Params = l.InferMethodParams<typeof main>; 28 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 29 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 30 + typeof main, 31 + B 32 + >; 33 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 34 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 35 + typeof main, 36 + B 37 + >; 38 + 39 + export const $lxm = main.nsid, 40 + $params = main.parameters, 41 + $input = main.input, 42 + $output = main.output;
+6
generated/org/atsui/Cover.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Cover.defs"; 6 + export * as $defs from "./Cover.defs";
+39
generated/org/atsui/Editor.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Editor"; 9 + 10 + export { $nsid }; 11 + 12 + /** Template editor for component records. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ uri: l.string({ format: "at-uri" }) }), 17 + l.payload( 18 + "application/json", 19 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 20 + ) 21 + ); 22 + export { main }; 23 + 24 + export type $Params = l.InferMethodParams<typeof main>; 25 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 26 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 27 + typeof main, 28 + B 29 + >; 30 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 31 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 32 + typeof main, 33 + B 34 + >; 35 + 36 + export const $lxm = main.nsid, 37 + $params = main.parameters, 38 + $input = main.input, 39 + $output = main.output;
+6
generated/org/atsui/Editor.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Editor.defs"; 6 + export * as $defs from "./Editor.defs";
+43
generated/org/atsui/Fill.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Fill"; 9 + 10 + export { $nsid }; 11 + 12 + /** Takes remaining space on the parent's main axis. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ 17 + children: l.array( 18 + l.ref<InlayDefs.Element>((() => InlayDefs.element) as any) 19 + ), 20 + }), 21 + l.payload( 22 + "application/json", 23 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 24 + ) 25 + ); 26 + export { main }; 27 + 28 + export type $Params = l.InferMethodParams<typeof main>; 29 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 30 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 31 + typeof main, 32 + B 33 + >; 34 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 35 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 36 + typeof main, 37 + B 38 + >; 39 + 40 + export const $lxm = main.nsid, 41 + $params = main.parameters, 42 + $input = main.input, 43 + $output = main.output;
+6
generated/org/atsui/Fill.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Fill.defs"; 6 + export * as $defs from "./Fill.defs";
+53
generated/org/atsui/Grid.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Grid"; 9 + 10 + export { $nsid }; 11 + 12 + /** Arranges children in a grid of equal columns. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ 17 + columns: l.optional(l.withDefault(l.integer({ minimum: 1 }), 3)), 18 + gap: l.optional( 19 + l.withDefault( 20 + l.string<{ 21 + maxLength: 32; 22 + knownValues: ["none", "small", "medium", "large"]; 23 + }>({ maxLength: 32 }), 24 + "small" 25 + ) 26 + ), 27 + children: l.array( 28 + l.ref<InlayDefs.Element>((() => InlayDefs.element) as any) 29 + ), 30 + }), 31 + l.payload( 32 + "application/json", 33 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 34 + ) 35 + ); 36 + export { main }; 37 + 38 + export type $Params = l.InferMethodParams<typeof main>; 39 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 40 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 41 + typeof main, 42 + B 43 + >; 44 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 45 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 46 + typeof main, 47 + B 48 + >; 49 + 50 + export const $lxm = main.nsid, 51 + $params = main.parameters, 52 + $input = main.input, 53 + $output = main.output;
+6
generated/org/atsui/Grid.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Grid.defs"; 6 + export * as $defs from "./Grid.defs";
+39
generated/org/atsui/Heading.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Heading"; 9 + 10 + export { $nsid }; 11 + 12 + /** A section label or secondary heading. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ children: l.lexMap() }), 17 + l.payload( 18 + "application/json", 19 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 20 + ) 21 + ); 22 + export { main }; 23 + 24 + export type $Params = l.InferMethodParams<typeof main>; 25 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 26 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 27 + typeof main, 28 + B 29 + >; 30 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 31 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 32 + typeof main, 33 + B 34 + >; 35 + 36 + export const $lxm = main.nsid, 37 + $params = main.parameters, 38 + $input = main.input, 39 + $output = main.output;
+6
generated/org/atsui/Heading.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Heading.defs"; 6 + export * as $defs from "./Heading.defs";
+47
generated/org/atsui/Link.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Link"; 9 + 10 + export { $nsid }; 11 + 12 + /** A hyperlink. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ 17 + uri: l.string({ format: "uri" }), 18 + decoration: l.optional( 19 + l.string<{ maxLength: 32; knownValues: ["none", "underline"] }>({ 20 + maxLength: 32, 21 + }) 22 + ), 23 + children: l.optional(l.lexMap()), 24 + }), 25 + l.payload( 26 + "application/json", 27 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 28 + ) 29 + ); 30 + export { main }; 31 + 32 + export type $Params = l.InferMethodParams<typeof main>; 33 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 34 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 35 + typeof main, 36 + B 37 + >; 38 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 39 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 40 + typeof main, 41 + B 42 + >; 43 + 44 + export const $lxm = main.nsid, 45 + $params = main.parameters, 46 + $input = main.input, 47 + $output = main.output;
+6
generated/org/atsui/Link.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Link.defs"; 6 + export * as $defs from "./Link.defs";
+72
generated/org/atsui/List.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.List"; 9 + 10 + export { $nsid }; 11 + 12 + /** Scrollable vertical list with paginated data source. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ 17 + query: l.string({ format: "nsid" }), 18 + did: l.string({ format: "did" }), 19 + input: l.optional(l.lexMap()), 20 + }), 21 + l.payload( 22 + "application/json", 23 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 24 + ) 25 + ); 26 + export { main }; 27 + 28 + export type $Params = l.InferMethodParams<typeof main>; 29 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 30 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 31 + typeof main, 32 + B 33 + >; 34 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 35 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 36 + typeof main, 37 + B 38 + >; 39 + 40 + export const $lxm = main.nsid, 41 + $params = main.parameters, 42 + $input = main.input, 43 + $output = main.output; 44 + 45 + /** Response shape from a List data source query. */ 46 + type Page = { 47 + $type?: "org.atsui.List#page"; 48 + 49 + /** 50 + * Elements to render as list rows. 51 + */ 52 + items: InlayDefs.Element[]; 53 + 54 + /** 55 + * Opaque pagination token. Absent means no more items. 56 + */ 57 + cursor?: string; 58 + }; 59 + 60 + export type { Page }; 61 + 62 + /** Response shape from a List data source query. */ 63 + const page = l.typedObject<Page>( 64 + $nsid, 65 + "page", 66 + l.object({ 67 + items: l.array(l.ref<InlayDefs.Element>((() => InlayDefs.element) as any)), 68 + cursor: l.optional(l.string({ maxLength: 512 })), 69 + }) 70 + ); 71 + 72 + export { page };
+6
generated/org/atsui/List.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./List.defs"; 6 + export * as $defs from "./List.defs";
+39
generated/org/atsui/Record.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Record"; 9 + 10 + export { $nsid }; 11 + 12 + /** Renders any record. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ uri: l.string({ format: "at-uri" }) }), 17 + l.payload( 18 + "application/json", 19 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 20 + ) 21 + ); 22 + export { main }; 23 + 24 + export type $Params = l.InferMethodParams<typeof main>; 25 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 26 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 27 + typeof main, 28 + B 29 + >; 30 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 31 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 32 + typeof main, 33 + B 34 + >; 35 + 36 + export const $lxm = main.nsid, 37 + $params = main.parameters, 38 + $input = main.input, 39 + $output = main.output;
+6
generated/org/atsui/Record.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Record.defs"; 6 + export * as $defs from "./Record.defs";
+73
generated/org/atsui/Row.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Row"; 9 + 10 + export { $nsid }; 11 + 12 + /** Arranges children horizontally with consistent spacing. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ 17 + gap: l.optional( 18 + l.withDefault( 19 + l.string<{ 20 + maxLength: 32; 21 + knownValues: ["none", "small", "medium", "large"]; 22 + }>({ maxLength: 32 }), 23 + "medium" 24 + ) 25 + ), 26 + align: l.optional( 27 + l.withDefault( 28 + l.string<{ 29 + maxLength: 32; 30 + knownValues: ["start", "center", "end", "stretch"]; 31 + }>({ maxLength: 32 }), 32 + "center" 33 + ) 34 + ), 35 + justify: l.optional( 36 + l.withDefault( 37 + l.string<{ 38 + maxLength: 32; 39 + knownValues: ["start", "center", "end", "between"]; 40 + }>({ maxLength: 32 }), 41 + "start" 42 + ) 43 + ), 44 + inset: l.optional(l.boolean()), 45 + sticky: l.optional(l.boolean()), 46 + opaque: l.optional(l.boolean()), 47 + children: l.array( 48 + l.ref<InlayDefs.Element>((() => InlayDefs.element) as any) 49 + ), 50 + }), 51 + l.payload( 52 + "application/json", 53 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 54 + ) 55 + ); 56 + export { main }; 57 + 58 + export type $Params = l.InferMethodParams<typeof main>; 59 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 60 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 61 + typeof main, 62 + B 63 + >; 64 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 65 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 66 + typeof main, 67 + B 68 + >; 69 + 70 + export const $lxm = main.nsid, 71 + $params = main.parameters, 72 + $input = main.input, 73 + $output = main.output;
+6
generated/org/atsui/Row.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Row.defs"; 6 + export * as $defs from "./Row.defs";
+74
generated/org/atsui/Stack.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Stack"; 9 + 10 + export { $nsid }; 11 + 12 + /** Arranges children vertically with consistent spacing. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ 17 + gap: l.optional( 18 + l.withDefault( 19 + l.string<{ 20 + maxLength: 32; 21 + knownValues: ["none", "small", "medium", "large"]; 22 + }>({ maxLength: 32 }), 23 + "medium" 24 + ) 25 + ), 26 + align: l.optional( 27 + l.withDefault( 28 + l.string<{ 29 + maxLength: 32; 30 + knownValues: ["start", "center", "end", "stretch"]; 31 + }>({ maxLength: 32 }), 32 + "stretch" 33 + ) 34 + ), 35 + justify: l.optional( 36 + l.withDefault( 37 + l.string<{ 38 + maxLength: 32; 39 + knownValues: ["start", "center", "end", "between"]; 40 + }>({ maxLength: 32 }), 41 + "start" 42 + ) 43 + ), 44 + inset: l.optional(l.boolean()), 45 + sticky: l.optional(l.boolean()), 46 + opaque: l.optional(l.boolean()), 47 + separator: l.optional(l.boolean()), 48 + children: l.array( 49 + l.ref<InlayDefs.Element>((() => InlayDefs.element) as any) 50 + ), 51 + }), 52 + l.payload( 53 + "application/json", 54 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 55 + ) 56 + ); 57 + export { main }; 58 + 59 + export type $Params = l.InferMethodParams<typeof main>; 60 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 61 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 62 + typeof main, 63 + B 64 + >; 65 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 66 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 67 + typeof main, 68 + B 69 + >; 70 + 71 + export const $lxm = main.nsid, 72 + $params = main.parameters, 73 + $input = main.input, 74 + $output = main.output;
+6
generated/org/atsui/Stack.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Stack.defs"; 6 + export * as $defs from "./Stack.defs";
+72
generated/org/atsui/Tabs.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Tabs"; 9 + 10 + export { $nsid }; 11 + 12 + /** Tabbed content with instant client-side switching. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ items: l.array(l.ref<Tab>((() => tab) as any)) }), 17 + l.payload( 18 + "application/json", 19 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 20 + ) 21 + ); 22 + export { main }; 23 + 24 + export type $Params = l.InferMethodParams<typeof main>; 25 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 26 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 27 + typeof main, 28 + B 29 + >; 30 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 31 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 32 + typeof main, 33 + B 34 + >; 35 + 36 + export const $lxm = main.nsid, 37 + $params = main.parameters, 38 + $input = main.input, 39 + $output = main.output; 40 + 41 + type Tab = { 42 + $type?: "org.atsui.Tabs#tab"; 43 + 44 + /** 45 + * Stable key that identifies the tab among its siblings. 46 + */ 47 + key: string; 48 + 49 + /** 50 + * Display label for the tab. 51 + */ 52 + label: string; 53 + 54 + /** 55 + * Element to render as tab content. 56 + */ 57 + content: InlayDefs.Element; 58 + }; 59 + 60 + export type { Tab }; 61 + 62 + const tab = l.typedObject<Tab>( 63 + $nsid, 64 + "tab", 65 + l.object({ 66 + key: l.string({ maxLength: 64 }), 67 + label: l.string({ maxLength: 128 }), 68 + content: l.ref<InlayDefs.Element>((() => InlayDefs.element) as any), 69 + }) 70 + ); 71 + 72 + export { tab };
+6
generated/org/atsui/Tabs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Tabs.defs"; 6 + export * as $defs from "./Tabs.defs";
+39
generated/org/atsui/Text.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Text"; 9 + 10 + export { $nsid }; 11 + 12 + /** Body text. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ children: l.lexMap() }), 17 + l.payload( 18 + "application/json", 19 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 20 + ) 21 + ); 22 + export { main }; 23 + 24 + export type $Params = l.InferMethodParams<typeof main>; 25 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 26 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 27 + typeof main, 28 + B 29 + >; 30 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 31 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 32 + typeof main, 33 + B 34 + >; 35 + 36 + export const $lxm = main.nsid, 37 + $params = main.parameters, 38 + $input = main.input, 39 + $output = main.output;
+6
generated/org/atsui/Text.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Text.defs"; 6 + export * as $defs from "./Text.defs";
+39
generated/org/atsui/Timestamp.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Timestamp"; 9 + 10 + export { $nsid }; 11 + 12 + /** Displays a datetime value. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ value: l.string({ format: "datetime" }) }), 17 + l.payload( 18 + "application/json", 19 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 20 + ) 21 + ); 22 + export { main }; 23 + 24 + export type $Params = l.InferMethodParams<typeof main>; 25 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 26 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 27 + typeof main, 28 + B 29 + >; 30 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 31 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 32 + typeof main, 33 + B 34 + >; 35 + 36 + export const $lxm = main.nsid, 37 + $params = main.parameters, 38 + $input = main.input, 39 + $output = main.output;
+6
generated/org/atsui/Timestamp.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Timestamp.defs"; 6 + export * as $defs from "./Timestamp.defs";
+39
generated/org/atsui/Title.defs.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + import { l } from "@atproto/lex"; 6 + import * as InlayDefs from "../../at/inlay/defs.defs"; 7 + 8 + const $nsid = "org.atsui.Title"; 9 + 10 + export { $nsid }; 11 + 12 + /** The primary heading for a card or section. */ 13 + const main = l.procedure( 14 + $nsid, 15 + l.params(), 16 + l.jsonPayload({ children: l.lexMap() }), 17 + l.payload( 18 + "application/json", 19 + l.ref<InlayDefs.Response>((() => InlayDefs.response) as any) 20 + ) 21 + ); 22 + export { main }; 23 + 24 + export type $Params = l.InferMethodParams<typeof main>; 25 + export type $Input<B = l.BinaryData> = l.InferMethodInput<typeof main, B>; 26 + export type $InputBody<B = l.BinaryData> = l.InferMethodInputBody< 27 + typeof main, 28 + B 29 + >; 30 + export type $Output<B = l.BinaryData> = l.InferMethodOutput<typeof main, B>; 31 + export type $OutputBody<B = l.BinaryData> = l.InferMethodOutputBody< 32 + typeof main, 33 + B 34 + >; 35 + 36 + export const $lxm = main.nsid, 37 + $params = main.parameters, 38 + $input = main.input, 39 + $output = main.output;
+6
generated/org/atsui/Title.ts
··· 1 + /* 2 + * THIS FILE WAS GENERATED BY "@atproto/lex". DO NOT EDIT. 3 + */ 4 + 5 + export * from "./Title.defs"; 6 + export * as $defs from "./Title.defs";
+19
invalidator/package.json
··· 1 + { 2 + "name": "@atui/invalidator", 3 + "version": "0.0.1", 4 + "private": true, 5 + "type": "module", 6 + "scripts": { 7 + "dev": "tsx watch src/index.ts", 8 + "start": "tsx src/index.ts" 9 + }, 10 + "dependencies": { 11 + "dotenv": "^17.2.3", 12 + "ioredis": "^5.6.0", 13 + "ws": "^8.18.0" 14 + }, 15 + "devDependencies": { 16 + "@types/ws": "^8.0.0", 17 + "tsx": "^4.0.0" 18 + } 19 + }
+282
invalidator/src/cache-invalidation.ts
··· 1 + // Cache invalidation via firehose events 2 + // Uses reverse index with wildcard pattern matching 3 + 4 + import { 5 + getRedis, 6 + closeRedis as closeRedisClient, 7 + CACHE_PREFIX, 8 + INDEX_PREFIX, 9 + } from "./redis.js"; 10 + import type { JetstreamEvent } from "./jetstream.js"; 11 + 12 + // Parse at-uri into parts 13 + function parseAtUri( 14 + uri: string 15 + ): { did: string; collection: string; rkey: string } | null { 16 + const match = uri.match(/^at:\/\/([^/]+)\/([^/]+)\/([^/]+)$/); 17 + if (!match) return null; 18 + return { did: match[1], collection: match[2], rkey: match[3] }; 19 + } 20 + 21 + // Generate all wildcard patterns to check for a record URI 22 + function getRecordPatterns(uri: string): string[] { 23 + const parts = parseAtUri(uri); 24 + if (!parts) return [uri]; 25 + 26 + return [ 27 + // Exact match 28 + `record:${uri}`, 29 + // Any rkey for this did+collection 30 + `record:at://${parts.did}/${parts.collection}/*`, 31 + // Any record by anyone in this collection 32 + `record:at://*/${parts.collection}/*`, 33 + ]; 34 + } 35 + 36 + // Generate all wildcard patterns to check for a link (backlink) 37 + // Tags are stored as: link:${subject}:${matchPattern} 38 + // where matchPattern is a URI pattern like "at://*/app.bsky.feed.like/*" 39 + function getLinkPatterns( 40 + subject: string, 41 + linkerDid: string, 42 + linkCollection: string, 43 + linkRkey: string 44 + ): string[] { 45 + const subjectParts = parseAtUri(subject); 46 + if (!subjectParts) return []; 47 + 48 + // Check all combinations of subject patterns × match patterns 49 + const patterns: string[] = []; 50 + 51 + // Subject variations: exact, wildcard rkey, wildcard did 52 + const subjectVariants = [ 53 + subject, // exact subject 54 + `at://${subjectParts.did}/${subjectParts.collection}/*`, // any rkey by this did 55 + `at://*/${subjectParts.collection}/*`, // any record in collection 56 + ]; 57 + 58 + // Match pattern variations for the linking record 59 + const matchVariants = [ 60 + `at://${linkerDid}/${linkCollection}/${linkRkey}`, // exact link 61 + `at://${linkerDid}/${linkCollection}/*`, // any link by this did 62 + `at://*/${linkCollection}/*`, // any link by anyone 63 + `at://*/*`, // matches link tags with no `from` filter 64 + ]; 65 + 66 + // Generate all combinations 67 + for (const subjectVar of subjectVariants) { 68 + for (const matchVar of matchVariants) { 69 + patterns.push(`link:${subjectVar}:${matchVar}`); 70 + } 71 + } 72 + 73 + return patterns; 74 + } 75 + 76 + // Regex to match at-uri pattern 77 + const AT_URI_PATTERN = /at:\/\/[^/]+\/[^/]+\/[^/\s"',}\]]+/g; 78 + 79 + // Extract all at-uris from a record by scanning JSON 80 + export function extractAtUris(record: unknown): string[] { 81 + if (!record) return []; 82 + 83 + const json = JSON.stringify(record); 84 + const matches = json.match(AT_URI_PATTERN); 85 + 86 + if (!matches) return []; 87 + 88 + // Deduplicate 89 + return [...new Set(matches)]; 90 + } 91 + 92 + // Batch of events to process together 93 + type EventBatch = { 94 + patterns: string[]; 95 + uri: string; 96 + }[]; 97 + 98 + let eventBatch: EventBatch = []; 99 + let batchTimeout: ReturnType<typeof setTimeout> | null = null; 100 + 101 + const BATCH_SIZE = 100; 102 + const BATCH_DELAY_MS = 50; 103 + 104 + async function processBatch(batch: EventBatch): Promise<void> { 105 + if (batch.length === 0) return; 106 + 107 + const allPatterns = batch.flatMap((e) => e.patterns); 108 + if (allPatterns.length === 0) return; 109 + 110 + // Pipeline EXISTS for all patterns 111 + const pipeline = getRedis().pipeline(); 112 + for (const pattern of allPatterns) { 113 + pipeline.exists(INDEX_PREFIX + pattern); 114 + } 115 + const results = await pipeline.exec(); 116 + 117 + // Find which patterns have entries 118 + const patternsWithEntries: string[] = []; 119 + for (let i = 0; i < allPatterns.length; i++) { 120 + const [err, exists] = results![i]; 121 + if (!err && exists === 1) { 122 + patternsWithEntries.push(allPatterns[i]); 123 + } 124 + } 125 + 126 + if (patternsWithEntries.length === 0) { 127 + return; // Nothing cached for any of these events 128 + } 129 + 130 + // Get cache keys for matching patterns 131 + const membersPipeline = getRedis().pipeline(); 132 + for (const pattern of patternsWithEntries) { 133 + membersPipeline.smembers(INDEX_PREFIX + pattern); 134 + } 135 + const membersResults = await membersPipeline.exec(); 136 + 137 + // Collect all cache keys and index keys to delete 138 + const cacheKeysToDelete = new Set<string>(); 139 + const indexKeysToDelete = new Set<string>(); 140 + 141 + for (let i = 0; i < patternsWithEntries.length; i++) { 142 + const [err, members] = membersResults![i]; 143 + if (!err && Array.isArray(members) && members.length > 0) { 144 + for (const key of members) { 145 + cacheKeysToDelete.add(key as string); 146 + } 147 + indexKeysToDelete.add(INDEX_PREFIX + patternsWithEntries[i]); 148 + } 149 + } 150 + 151 + if (cacheKeysToDelete.size > 0) { 152 + const delPipeline = getRedis().pipeline(); 153 + for (const key of cacheKeysToDelete) { 154 + delPipeline.del(CACHE_PREFIX + key); 155 + } 156 + for (const key of indexKeysToDelete) { 157 + delPipeline.del(key); 158 + } 159 + await delPipeline.exec(); 160 + 161 + console.log( 162 + `[cache] Invalidated ${cacheKeysToDelete.size} entries (patterns: ${patternsWithEntries.join(", ")})` 163 + ); 164 + } 165 + } 166 + 167 + function flushBatch(): void { 168 + if (batchTimeout) { 169 + clearTimeout(batchTimeout); 170 + batchTimeout = null; 171 + } 172 + const batch = eventBatch; 173 + eventBatch = []; 174 + processBatch(batch).catch((err) => { 175 + console.error("[cache] Batch processing error:", err); 176 + }); 177 + } 178 + 179 + function scheduleBatchFlush(): void { 180 + if (!batchTimeout) { 181 + batchTimeout = setTimeout(flushBatch, BATCH_DELAY_MS); 182 + } 183 + } 184 + 185 + export async function handleCacheInvalidation( 186 + event: JetstreamEvent 187 + ): Promise<void> { 188 + // Identity changes (handle change, suspension, deactivation): 189 + // invalidate all cached entries tagged with this DID's records 190 + if (event.kind === "identity") { 191 + const { did } = event; 192 + const pattern = `record:at://${did}/*`; 193 + const redis = getRedis(); 194 + const members = await redis.smembers(INDEX_PREFIX + pattern); 195 + if (members.length > 0) { 196 + const pipeline = redis.pipeline(); 197 + for (const key of members) { 198 + pipeline.del(CACHE_PREFIX + key); 199 + } 200 + pipeline.del(INDEX_PREFIX + pattern); 201 + await pipeline.exec(); 202 + console.log( 203 + `[cache] Identity change for ${did}: invalidated ${members.length} entries` 204 + ); 205 + } 206 + return; 207 + } 208 + 209 + if (event.kind !== "commit") return; 210 + 211 + const { did, commit } = event; 212 + const { collection, rkey, record } = commit; 213 + 214 + const uri = `at://${did}/${collection}/${rkey}`; 215 + 216 + // Lexicon schema records: rkey is the NSID, bust the cached resolution 217 + if (collection === "com.atproto.lexicon.schema") { 218 + const tag = `lexicon:${rkey}`; 219 + const redis = getRedis(); 220 + const members = await redis.smembers(INDEX_PREFIX + tag); 221 + if (members.length > 0) { 222 + const pipeline = redis.pipeline(); 223 + for (const key of members) { 224 + pipeline.del(CACHE_PREFIX + key); 225 + } 226 + pipeline.del(INDEX_PREFIX + tag); 227 + await pipeline.exec(); 228 + console.log( 229 + `[cache] Invalidated lexicon ${rkey} (${members.length} entries)` 230 + ); 231 + } 232 + return; 233 + } 234 + 235 + const patterns: string[] = []; 236 + 237 + // Record patterns - always check these 238 + patterns.push(...getRecordPatterns(uri)); 239 + 240 + // Link patterns - extract all at-uris from record and generate patterns for each 241 + const subjects = extractAtUris(record); 242 + for (const subject of subjects) { 243 + patterns.push(...getLinkPatterns(subject, did, collection, rkey)); 244 + } 245 + 246 + // Add to batch 247 + eventBatch.push({ patterns, uri }); 248 + 249 + // Flush if batch is full, otherwise schedule 250 + if (eventBatch.length >= BATCH_SIZE) { 251 + flushBatch(); 252 + } else { 253 + scheduleBatchFlush(); 254 + } 255 + } 256 + 257 + export async function flushCache(): Promise<number> { 258 + const redis = getRedis(); 259 + let deleted = 0; 260 + for (const prefix of [CACHE_PREFIX, INDEX_PREFIX]) { 261 + let cursor = "0"; 262 + do { 263 + const [next, keys] = await redis.scan( 264 + cursor, 265 + "MATCH", 266 + `${prefix}*`, 267 + "COUNT", 268 + 500 269 + ); 270 + cursor = next; 271 + if (keys.length > 0) { 272 + await redis.del(...keys); 273 + deleted += keys.length; 274 + } 275 + } while (cursor !== "0"); 276 + } 277 + return deleted; 278 + } 279 + 280 + export async function closeRedis(): Promise<void> { 281 + await closeRedisClient(); 282 + }
+13
invalidator/src/db.ts
··· 1 + // Cursor management via Redis (no Postgres needed) 2 + import { getRedis } from "./redis.js"; 3 + 4 + const CURSOR_KEY = "atui:invalidator:cursor"; 5 + 6 + export async function getCursor(): Promise<bigint | undefined> { 7 + const val = await getRedis().get(CURSOR_KEY); 8 + return val ? BigInt(val) : undefined; 9 + } 10 + 11 + export async function setCursor(cursorUs: bigint): Promise<void> { 12 + await getRedis().set(CURSOR_KEY, cursorUs.toString()); 13 + }
+8
invalidator/src/dotenv.ts
··· 1 + import { config } from "dotenv"; 2 + import { resolve, dirname } from "path"; 3 + import { fileURLToPath } from "url"; 4 + 5 + const __dirname = dirname(fileURLToPath(import.meta.url)); 6 + 7 + // Load .env from workspace root (two levels up from invalidator/src/) 8 + config({ path: resolve(__dirname, "../..", ".env") });
+84
invalidator/src/index.ts
··· 1 + // invalidator/src/index.ts 2 + // Cache invalidation via Jetstream - watches ALL events for backlink tracking 3 + import "./dotenv.js"; 4 + import { Jetstream } from "./jetstream.js"; 5 + import { 6 + handleCacheInvalidation, 7 + flushCache, 8 + closeRedis, 9 + } from "./cache-invalidation.js"; 10 + import { getCursor, setCursor } from "./db.js"; 11 + 12 + const JETSTREAM_URL = 13 + process.env.JETSTREAM_URL || 14 + "wss://jetstream1.us-east.bsky.network/subscribe"; 15 + 16 + // Optional: filter to specific DIDs for testing 17 + const WATCH_DIDS = process.env.WATCH_DIDS?.split(",").filter(Boolean) || []; 18 + 19 + async function main() { 20 + console.log("Starting cache invalidator..."); 21 + console.log("Listening to ALL collections for backlink tracking"); 22 + if (WATCH_DIDS.length > 0) { 23 + console.log(`Filtering to DIDs: ${WATCH_DIDS.join(", ")}`); 24 + } 25 + 26 + const MAX_LAG_MS = 10 * 60 * 1000; // 10 minutes — skip replay if too far behind 27 + 28 + let cursor = await getCursor(); 29 + if (cursor) { 30 + const cursorDate = new Date(Number(cursor) / 1000); 31 + const lagMs = Date.now() - cursorDate.getTime(); 32 + console.log( 33 + `Cursor: ${cursorDate.toISOString()} (${(lagMs / 1000 / 60).toFixed(1)} minutes behind)` 34 + ); 35 + if (lagMs > MAX_LAG_MS) { 36 + console.log( 37 + `Cursor too far behind (${(lagMs / 1000 / 60).toFixed(0)}m > ${MAX_LAG_MS / 60000}m limit), flushing cache and starting from latest` 38 + ); 39 + const flushed = await flushCache(); 40 + console.log(`Flushed ${flushed} cache entries`); 41 + cursor = undefined; 42 + } 43 + } else { 44 + console.log("No cursor, starting from latest"); 45 + } 46 + 47 + let eventCount = 0; 48 + let lastLogTime = Date.now(); 49 + 50 + // Log processing rate every 10 seconds 51 + setInterval(() => { 52 + const elapsed = (Date.now() - lastLogTime) / 1000; 53 + const rate = eventCount / elapsed; 54 + console.log( 55 + `[rate] ${rate.toFixed(0)} events/s (${eventCount} in ${elapsed.toFixed(0)}s)` 56 + ); 57 + eventCount = 0; 58 + lastLogTime = Date.now(); 59 + }, 10_000); 60 + 61 + const jetstream = new Jetstream({ 62 + instanceUrl: JETSTREAM_URL, 63 + // No collection filter - need ALL events for backlink tracking 64 + wantedDids: WATCH_DIDS.length > 0 ? WATCH_DIDS : undefined, 65 + cursor, 66 + setCursor, 67 + onEvent: async (event) => { 68 + eventCount++; 69 + await handleCacheInvalidation(event); 70 + }, 71 + onError: (err) => console.error("Jetstream error:", err), 72 + }); 73 + 74 + // Handle shutdown gracefully 75 + process.on("SIGINT", async () => { 76 + console.log("Shutting down..."); 77 + await closeRedis(); 78 + process.exit(0); 79 + }); 80 + 81 + jetstream.start(); 82 + } 83 + 84 + main().catch(console.error);
+101
invalidator/src/jetstream.ts
··· 1 + // ingester/src/jetstream.ts 2 + import WebSocket from "ws"; 3 + 4 + export type JetstreamEvent = { 5 + did: string; 6 + time_us: number; 7 + } & (CommitEvent | AccountEvent | IdentityEvent); 8 + 9 + type CommitEvent = { 10 + kind: "commit"; 11 + commit: { 12 + operation: "create" | "update" | "delete"; 13 + collection: string; 14 + rkey: string; 15 + record?: unknown; 16 + cid?: string; 17 + }; 18 + }; 19 + 20 + type AccountEvent = { 21 + kind: "account"; 22 + account: { active: boolean; status?: string }; 23 + }; 24 + 25 + type IdentityEvent = { 26 + kind: "identity"; 27 + identity: { handle?: string }; 28 + }; 29 + 30 + export type JetstreamOptions = { 31 + instanceUrl: string; 32 + wantedCollections?: string[]; // If undefined, listens to all collections 33 + wantedDids?: string[]; 34 + cursor?: bigint; 35 + setCursor: (cursor: bigint) => Promise<void>; 36 + onEvent: (event: JetstreamEvent) => Promise<void>; 37 + onError: (error: Error) => void; 38 + }; 39 + 40 + export class Jetstream { 41 + private ws: WebSocket | null = null; 42 + private lastCursorSave = 0; 43 + private latestCursor: bigint | undefined; 44 + 45 + constructor(private opts: JetstreamOptions) {} 46 + 47 + start() { 48 + const params = new URLSearchParams(); 49 + if (this.opts.wantedCollections) { 50 + for (const c of this.opts.wantedCollections) { 51 + params.append("wantedCollections", c); 52 + } 53 + } 54 + if (this.opts.wantedDids) { 55 + for (const did of this.opts.wantedDids) { 56 + params.append("wantedDids", did); 57 + } 58 + } 59 + if (this.opts.cursor) { 60 + params.set("cursor", this.opts.cursor.toString()); 61 + } 62 + 63 + const url = `${this.opts.instanceUrl}?${params}`; 64 + console.log(`Connecting to Jetstream: ${url}`); 65 + 66 + this.ws = new WebSocket(url); 67 + 68 + this.ws.on("open", () => console.log("Jetstream connected")); 69 + 70 + this.ws.on("message", async (data) => { 71 + try { 72 + const event = JSON.parse(data.toString()) as JetstreamEvent; 73 + this.latestCursor = BigInt(event.time_us); 74 + await this.opts.onEvent(event); 75 + await this.maybeSaveCursor(); 76 + } catch (err) { 77 + this.opts.onError(err as Error); 78 + } 79 + }); 80 + 81 + this.ws.on("close", () => { 82 + console.log("Jetstream disconnected, reconnecting in 5s..."); 83 + setTimeout(() => this.start(), 5000); 84 + }); 85 + 86 + this.ws.on("error", (err) => this.opts.onError(err)); 87 + } 88 + 89 + private async maybeSaveCursor() { 90 + const now = Date.now(); 91 + if (now - this.lastCursorSave > 30_000 && this.latestCursor) { 92 + this.lastCursorSave = now; // Update before await to prevent concurrent saves 93 + await this.opts.setCursor(this.latestCursor); 94 + const cursorDate = new Date(Number(this.latestCursor) / 1000); 95 + const lagMs = Date.now() - cursorDate.getTime(); 96 + console.log( 97 + `Cursor saved: ${this.latestCursor} (${cursorDate.toISOString()}, lag: ${(lagMs / 1000 / 60).toFixed(1)}m)` 98 + ); 99 + } 100 + } 101 + }
+29
invalidator/src/redis.ts
··· 1 + import { Redis } from "ioredis"; 2 + 3 + let redis: Redis | null = null; 4 + 5 + export function getRedis(): Redis { 6 + if (!redis) { 7 + redis = new Redis(process.env.REDIS_URL || "redis://localhost:6379", { 8 + maxRetriesPerRequest: 3, 9 + lazyConnect: true, 10 + }); 11 + redis.on("error", (err: Error) => { 12 + console.error("[redis] connection error:", err.message); 13 + }); 14 + redis.on("connect", () => { 15 + console.log("[redis] connected"); 16 + }); 17 + } 18 + return redis; 19 + } 20 + 21 + export async function closeRedis(): Promise<void> { 22 + if (redis) { 23 + await redis.quit(); 24 + redis = null; 25 + } 26 + } 27 + 28 + export const CACHE_PREFIX = "atui:cache:"; 29 + export const INDEX_PREFIX = "atui:idx:";
+10
invalidator/tsconfig.json
··· 1 + { 2 + "extends": "../tsconfig.json", 3 + "compilerOptions": { 4 + "module": "NodeNext", 5 + "moduleResolution": "NodeNext", 6 + "rootDir": "./src", 7 + "outDir": "./dist" 8 + }, 9 + "include": ["src/**/*"] 10 + }
+90
lexicons.json
··· 1 + { 2 + "version": 1, 3 + "lexicons": [ 4 + "app.bsky.actor.defs", 5 + "app.bsky.feed.defs", 6 + "app.bsky.feed.getPostThread" 7 + ], 8 + "resolutions": { 9 + "app.bsky.actor.defs": { 10 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.actor.defs", 11 + "cid": "bafyreigwqwhe2jxohagozazfbrf6dxgzphvkg3d3lg7uxdvepsimqyclka" 12 + }, 13 + "app.bsky.actor.status": { 14 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.actor.status", 15 + "cid": "bafyreifdg4b64wohpwkh5lydc6tckvol2rspnpni6dec6recy2rhvlnz4a" 16 + }, 17 + "app.bsky.embed.defs": { 18 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.defs", 19 + "cid": "bafyreia42uud4qil67wknywzbxfyxc3b7woewsii54cakq2ould3ldetei" 20 + }, 21 + "app.bsky.embed.external": { 22 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.external", 23 + "cid": "bafyreiblxmpzgwg4fbr45b4xzts3h4k72k7cdnrxy2ub2w5d7mnwzznkwi" 24 + }, 25 + "app.bsky.embed.images": { 26 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.images", 27 + "cid": "bafyreifrntpx63uebiskpooozv6hji62swectq3pocw5h5gpkkqynmazdm" 28 + }, 29 + "app.bsky.embed.record": { 30 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.record", 31 + "cid": "bafyreigdtmu53blwxoygphg5zh5zpmlftz64c3jyqpv2yqpx3nrichkyla" 32 + }, 33 + "app.bsky.embed.recordWithMedia": { 34 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.recordWithMedia", 35 + "cid": "bafyreia7jrw2p73egm7vrunssgzeyj2rwmk3s4dymfhgzcavxjfaje3qfi" 36 + }, 37 + "app.bsky.embed.video": { 38 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.embed.video", 39 + "cid": "bafyreib7cq67zkhasxlwomomskgnbuxvhzzde5brdkpv26fucbv3r7c5ue" 40 + }, 41 + "app.bsky.feed.defs": { 42 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.defs", 43 + "cid": "bafyreiadwvxawxifsnm7ae6l56aq23qs7ndih7npgs6pxmkoin7gi3k6pu" 44 + }, 45 + "app.bsky.feed.getPostThread": { 46 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.getPostThread", 47 + "cid": "bafyreif37upfzfu7t4rv3pqzz3swu2oc4nrxdxfr3xqjsrrmgyckneitta" 48 + }, 49 + "app.bsky.feed.postgate": { 50 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.postgate", 51 + "cid": "bafyreiai5efexyluyptv5tbl6kqbqlnneczqzexcqnxmitmulyjfaftgva" 52 + }, 53 + "app.bsky.feed.threadgate": { 54 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.feed.threadgate", 55 + "cid": "bafyreiht77wd6duduz4yqp62m6dwma5dy7gdihps4g2nd73acfzqlglvdi" 56 + }, 57 + "app.bsky.graph.defs": { 58 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.graph.defs", 59 + "cid": "bafyreifcipomli7yggtl46xufgxlnrw7se6xmsdxmzgfcz2tiu76ljatxm" 60 + }, 61 + "app.bsky.labeler.defs": { 62 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.labeler.defs", 63 + "cid": "bafyreicxx5i36v5dbqk5vvfzhnta5gajrvc544mnepux4wksrkid7mw3q4" 64 + }, 65 + "app.bsky.notification.defs": { 66 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.notification.defs", 67 + "cid": "bafyreickbpnayydlyfakliahgf23jjuesllh6qrslyofk5yz5xizjavhui" 68 + }, 69 + "app.bsky.richtext.facet": { 70 + "uri": "at://did:plc:4v4y5r3lwsbtmsxhile2ljac/com.atproto.lexicon.schema/app.bsky.richtext.facet", 71 + "cid": "bafyreidg56eo7zynf6ihz4xb627vwoqf5idnevkmwp7sxc4tijg6xngbu4" 72 + }, 73 + "com.atproto.label.defs": { 74 + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.label.defs", 75 + "cid": "bafyreig4hmnb2xkecyg4aaqfhr2rrcxxb3gsr4xks4rqb7rscrycalbrji" 76 + }, 77 + "com.atproto.moderation.defs": { 78 + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.moderation.defs", 79 + "cid": "bafyreideawy4rlpgces2oebk5q4kpurbonhb5qtl4pes7dvxsc5osaiksy" 80 + }, 81 + "com.atproto.repo.strongRef": { 82 + "uri": "at://did:plc:6msi3pj7krzih5qxqtryxlzw/com.atproto.lexicon.schema/com.atproto.repo.strongRef", 83 + "cid": "bafyreifrkdbnkvfjujntdaeigolnrjj3srrs53tfixjhmacclps72qlov4" 84 + }, 85 + "tools.ozone.report.defs": { 86 + "uri": "at://did:plc:33dt5kftu3jq2h5h4jjlqezt/com.atproto.lexicon.schema/tools.ozone.report.defs", 87 + "cid": "bafyreic3l2rmh2ugirt3jz372wcvy333m7t2ynlyzj2k54oshijs6lxdfu" 88 + } 89 + } 90 + }
+34
lexicons/at/inlay/Binding.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "at.inlay.Binding", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Data placeholder resolved by the host during deserialization.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["path"], 13 + "properties": { 14 + "path": { 15 + "type": "array", 16 + "items": { 17 + "type": "string", 18 + "maxLength": 256 19 + }, 20 + "description": "Path segments to resolve against scope" 21 + } 22 + } 23 + } 24 + }, 25 + "output": { 26 + "encoding": "application/json", 27 + "schema": { 28 + "type": "ref", 29 + "ref": "at.inlay.defs#response" 30 + } 31 + } 32 + } 33 + } 34 + }
+33
lexicons/at/inlay/Fragment.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "at.inlay.Fragment", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Renders children without a wrapper element.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["children"], 13 + "properties": { 14 + "children": { 15 + "type": "array", 16 + "items": { 17 + "type": "ref", 18 + "ref": "at.inlay.defs#element" 19 + } 20 + } 21 + } 22 + } 23 + }, 24 + "output": { 25 + "encoding": "application/json", 26 + "schema": { 27 + "type": "ref", 28 + "ref": "at.inlay.defs#response" 29 + } 30 + } 31 + } 32 + } 33 + }
+37
lexicons/at/inlay/Maybe.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "at.inlay.Maybe", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Renders children if all bindings resolve; otherwise renders fallback.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["children"], 13 + "properties": { 14 + "children": { 15 + "type": "array", 16 + "items": { 17 + "type": "ref", 18 + "ref": "at.inlay.defs#element" 19 + } 20 + }, 21 + "fallback": { 22 + "type": "ref", 23 + "ref": "at.inlay.defs#element" 24 + } 25 + } 26 + } 27 + }, 28 + "output": { 29 + "encoding": "application/json", 30 + "schema": { 31 + "type": "ref", 32 + "ref": "at.inlay.defs#response" 33 + } 34 + } 35 + } 36 + } 37 + }
+35
lexicons/at/inlay/Missing.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "at.inlay.Missing", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Signals missing data. Components return this when a required value is absent.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["path"], 13 + "properties": { 14 + "path": { 15 + "type": "array", 16 + "minLength": 1, 17 + "items": { 18 + "type": "string", 19 + "maxLength": 256 20 + }, 21 + "description": "Path segments identifying what is missing" 22 + } 23 + } 24 + } 25 + }, 26 + "output": { 27 + "encoding": "application/json", 28 + "schema": { 29 + "type": "ref", 30 + "ref": "at.inlay.defs#response" 31 + } 32 + } 33 + } 34 + } 35 + }
+37
lexicons/at/inlay/Placeholder.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "at.inlay.Placeholder", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Shows fallback content while children are loading.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["children", "fallback"], 13 + "properties": { 14 + "children": { 15 + "type": "array", 16 + "items": { 17 + "type": "ref", 18 + "ref": "at.inlay.defs#element" 19 + } 20 + }, 21 + "fallback": { 22 + "type": "ref", 23 + "ref": "at.inlay.defs#element" 24 + } 25 + } 26 + } 27 + }, 28 + "output": { 29 + "encoding": "application/json", 30 + "schema": { 31 + "type": "ref", 32 + "ref": "at.inlay.defs#response" 33 + } 34 + } 35 + } 36 + } 37 + }
+31
lexicons/at/inlay/Slot.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "at.inlay.Slot", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Opaque reference to a stashed element during XRPC calls.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["id"], 13 + "properties": { 14 + "id": { 15 + "type": "string", 16 + "maxLength": 256, 17 + "description": "Key into the host's stash map" 18 + } 19 + } 20 + } 21 + }, 22 + "output": { 23 + "encoding": "application/json", 24 + "schema": { 25 + "type": "ref", 26 + "ref": "at.inlay.defs#response" 27 + } 28 + } 29 + } 30 + } 31 + }
+36
lexicons/at/inlay/Throw.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "at.inlay.Throw", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Error node produced when component resolution fails. The host decides how to render it.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["message"], 13 + "properties": { 14 + "message": { 15 + "type": "string", 16 + "maxLength": 10000, 17 + "description": "Human-readable error message" 18 + }, 19 + "stack": { 20 + "type": "string", 21 + "maxLength": 100000, 22 + "description": "Stack trace, if available" 23 + } 24 + } 25 + } 26 + }, 27 + "output": { 28 + "encoding": "application/json", 29 + "schema": { 30 + "type": "ref", 31 + "ref": "at.inlay.defs#response" 32 + } 33 + } 34 + } 35 + } 36 + }
+168
lexicons/at/inlay/component.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "at.inlay.component", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "tid", 8 + "description": "Component record - declares an implementation of a type", 9 + "record": { 10 + "type": "object", 11 + "required": ["type"], 12 + "properties": { 13 + "type": { 14 + "type": "string", 15 + "format": "nsid", 16 + "description": "NSID this component implements (also the XRPC procedure)" 17 + }, 18 + "body": { 19 + "type": "union", 20 + "refs": ["#bodyExternal", "#bodyTemplate"], 21 + "description": "How this component is rendered. Omit for primitives rendered by the host." 22 + }, 23 + "view": { 24 + "type": "array", 25 + "items": { 26 + "type": "union", 27 + "refs": ["#viewRecord", "#viewCollection", "#viewIdentity"] 28 + }, 29 + "description": "What kinds of pages this component is a view for" 30 + }, 31 + "accepts": { 32 + "type": "array", 33 + "items": { 34 + "type": "ref", 35 + "ref": "#acceptsEntry" 36 + }, 37 + "description": "Data types this component can render. Used for data-driven discovery." 38 + }, 39 + "imports": { 40 + "type": "array", 41 + "items": { 42 + "type": "string", 43 + "format": "at-uri" 44 + }, 45 + "description": "Ordered list of pack URIs (import stack). First pack that exports an NSID wins." 46 + }, 47 + "via": { 48 + "type": "union", 49 + "refs": ["at.inlay.defs#viaValtown"], 50 + "description": "Platform-managed deployment metadata" 51 + }, 52 + "description": { 53 + "type": "string", 54 + "maxGraphemes": 1000, 55 + "maxLength": 10000 56 + }, 57 + "createdAt": { 58 + "type": "string", 59 + "format": "datetime" 60 + }, 61 + "updatedAt": { 62 + "type": "string", 63 + "format": "datetime", 64 + "description": "Last update timestamp. Set by the publish flow to bust cached responses." 65 + } 66 + } 67 + } 68 + }, 69 + "bodyExternal": { 70 + "type": "object", 71 + "description": "Component rendered by calling a remote XRPC endpoint", 72 + "required": ["did"], 73 + "properties": { 74 + "did": { 75 + "type": "string", 76 + "format": "did", 77 + "description": "DID of the service hosting this component" 78 + } 79 + } 80 + }, 81 + "bodyTemplate": { 82 + "type": "object", 83 + "description": "Component rendered by the host from a serialized element tree", 84 + "required": ["node"], 85 + "properties": { 86 + "node": { 87 + "type": "unknown", 88 + "description": "Serialized element tree with bindings" 89 + } 90 + } 91 + }, 92 + "viewRecord": { 93 + "type": "object", 94 + "description": "Component is a view for individual records of a collection. Omit collection for a generic record view.", 95 + "properties": { 96 + "collection": { 97 + "type": "string", 98 + "format": "nsid", 99 + "description": "The collection this component views. Omit for any-collection." 100 + } 101 + } 102 + }, 103 + "viewCollection": { 104 + "type": "object", 105 + "description": "Component is a view for a collection listing. Omit collection for a generic collection view.", 106 + "properties": { 107 + "collection": { 108 + "type": "string", 109 + "format": "nsid", 110 + "description": "The collection this component lists. Omit for any-collection." 111 + } 112 + } 113 + }, 114 + "viewIdentity": { 115 + "type": "object", 116 + "description": "Component is a view for an identity (person/DID).", 117 + "properties": {} 118 + }, 119 + "acceptsEntry": { 120 + "type": "object", 121 + "description": "Declares a data type this component can handle, routed to a specific prop.", 122 + "required": ["type", "prop"], 123 + "properties": { 124 + "type": { 125 + "type": "string", 126 + "maxLength": 128, 127 + "description": "Lexicon field type.", 128 + "knownValues": [ 129 + "string", 130 + "integer", 131 + "boolean", 132 + "blob", 133 + "cid-link", 134 + "bytes" 135 + ] 136 + }, 137 + "format": { 138 + "type": "string", 139 + "maxLength": 64, 140 + "description": "String format constraint. Only applies when type is 'string'.", 141 + "knownValues": [ 142 + "at-uri", 143 + "did", 144 + "datetime", 145 + "uri", 146 + "handle", 147 + "at-identifier", 148 + "nsid", 149 + "cid", 150 + "language", 151 + "record-key", 152 + "tid" 153 + ] 154 + }, 155 + "prop": { 156 + "type": "string", 157 + "maxLength": 256, 158 + "description": "Prop to bind matched data to." 159 + }, 160 + "collection": { 161 + "type": "string", 162 + "format": "nsid", 163 + "description": "For at-uri strings, restricts to this collection." 164 + } 165 + } 166 + } 167 + } 168 + }
+105
lexicons/at/inlay/defs.json
··· 1 + { 2 + "id": "at.inlay.defs", 3 + "defs": { 4 + "viaValtown": { 5 + "type": "object", 6 + "required": ["valId"], 7 + "properties": { 8 + "valId": { 9 + "type": "string", 10 + "maxLength": 128, 11 + "description": "Val Town val UUID" 12 + } 13 + } 14 + }, 15 + "tagRecord": { 16 + "type": "object", 17 + "description": "Cache tag: depend on a specific record, collection, or identity.", 18 + "required": ["uri"], 19 + "properties": { 20 + "uri": { 21 + "type": "string", 22 + "format": "at-uri", 23 + "description": "AT URI at record, collection, or identity granularity" 24 + } 25 + } 26 + }, 27 + "tagLink": { 28 + "type": "object", 29 + "description": "Cache tag: depend on backlink relationships to a subject.", 30 + "required": ["subject"], 31 + "properties": { 32 + "subject": { 33 + "type": "string", 34 + "format": "at-uri", 35 + "description": "Subject AT URI that is linked to" 36 + }, 37 + "from": { 38 + "type": "string", 39 + "format": "nsid", 40 + "description": "Collection NSID of the linking records. Omit for any collection." 41 + } 42 + } 43 + }, 44 + "cachePolicy": { 45 + "type": "object", 46 + "description": "Cache lifetime and invalidation tags returned by XRPC components.", 47 + "properties": { 48 + "life": { 49 + "type": "string", 50 + "maxLength": 32, 51 + "knownValues": ["seconds", "minutes", "hours", "max"], 52 + "description": "How frequently the underlying data changes" 53 + }, 54 + "tags": { 55 + "type": "array", 56 + "items": { 57 + "type": "union", 58 + "refs": ["#tagRecord", "#tagLink"] 59 + }, 60 + "description": "Data dependencies for cache invalidation" 61 + } 62 + } 63 + }, 64 + "response": { 65 + "type": "object", 66 + "description": "Standard response from a component render call.", 67 + "required": ["node", "cache"], 68 + "properties": { 69 + "node": { 70 + "type": "ref", 71 + "ref": "#element", 72 + "description": "Rendered element tree" 73 + }, 74 + "cache": { 75 + "type": "ref", 76 + "ref": "#cachePolicy", 77 + "description": "Cache lifetime and invalidation tags" 78 + } 79 + } 80 + }, 81 + "element": { 82 + "type": "object", 83 + "required": ["type"], 84 + "properties": { 85 + "type": { 86 + "type": "string", 87 + "format": "nsid", 88 + "description": "NSID of the component to render." 89 + }, 90 + "props": { 91 + "type": "unknown", 92 + "description": "Properties to pass to the component." 93 + }, 94 + "key": { 95 + "type": "string", 96 + "maxLength": 256, 97 + "description": "Stable key that identifies the component among its siblings." 98 + } 99 + }, 100 + "description": "A renderable Inlay element." 101 + } 102 + }, 103 + "$type": "com.atproto.lexicon.schema", 104 + "lexicon": 1 105 + }
+50
lexicons/at/inlay/pack.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "at.inlay.pack", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "key": "tid", 8 + "description": "A list of type to component exports", 9 + "record": { 10 + "type": "object", 11 + "required": ["name", "exports"], 12 + "properties": { 13 + "name": { 14 + "type": "string", 15 + "maxLength": 64, 16 + "description": "Short slug for the pack (e.g. \"core\", \"ui\")" 17 + }, 18 + "exports": { 19 + "type": "array", 20 + "items": { 21 + "type": "ref", 22 + "ref": "#export" 23 + }, 24 + "description": "Type to component mappings" 25 + }, 26 + "createdAt": { 27 + "type": "string", 28 + "format": "datetime" 29 + } 30 + } 31 + } 32 + }, 33 + "export": { 34 + "type": "object", 35 + "required": ["type", "component"], 36 + "properties": { 37 + "type": { 38 + "type": "string", 39 + "format": "nsid", 40 + "description": "NSID of the type being exported" 41 + }, 42 + "component": { 43 + "type": "string", 44 + "format": "at-uri", 45 + "description": "AT-URI of the component record" 46 + } 47 + } 48 + } 49 + } 50 + }
+46
lexicons/org/atsui/Avatar.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Avatar", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Circular profile image at a fixed size.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["src"], 13 + "properties": { 14 + "src": { 15 + "type": "unknown", 16 + "description": "Blob ref for the image." 17 + }, 18 + "did": { 19 + "type": "string", 20 + "format": "did", 21 + "description": "DID of the blob owner. Used to resolve blob URLs." 22 + }, 23 + "size": { 24 + "type": "string", 25 + "maxLength": 32, 26 + "description": "Size token.", 27 + "knownValues": ["xsmall", "small", "medium", "large"], 28 + "default": "medium" 29 + }, 30 + "lift": { 31 + "type": "boolean", 32 + "description": "Pull the avatar up by half its own height, overlapping the element above." 33 + } 34 + } 35 + } 36 + }, 37 + "output": { 38 + "encoding": "application/json", 39 + "schema": { 40 + "type": "ref", 41 + "ref": "at.inlay.defs#response" 42 + } 43 + } 44 + } 45 + } 46 + }
+60
lexicons/org/atsui/Blob.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Blob", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Displays an AT Protocol blob as an image.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["src", "did"], 13 + "properties": { 14 + "src": { 15 + "type": "unknown", 16 + "description": "Blob ref for the image." 17 + }, 18 + "did": { 19 + "type": "string", 20 + "format": "did", 21 + "description": "DID of the blob owner. Used to resolve blob URLs." 22 + }, 23 + "ratio": { 24 + "type": "ref", 25 + "ref": "#aspectRatio", 26 + "description": "Known aspect ratio for CLS prevention. Reserves space before the image loads." 27 + }, 28 + "fit": { 29 + "type": "string", 30 + "maxLength": 32, 31 + "enum": ["cover", "contain"], 32 + "description": "When set, Blob fills its container height. 'cover' crops to fill; 'contain' letterboxes." 33 + } 34 + } 35 + } 36 + }, 37 + "output": { 38 + "encoding": "application/json", 39 + "schema": { 40 + "type": "ref", 41 + "ref": "at.inlay.defs#response" 42 + } 43 + } 44 + }, 45 + "aspectRatio": { 46 + "type": "object", 47 + "required": ["width", "height"], 48 + "properties": { 49 + "width": { 50 + "type": "integer", 51 + "minimum": 1 52 + }, 53 + "height": { 54 + "type": "integer", 55 + "minimum": 1 56 + } 57 + } 58 + } 59 + } 60 + }
+29
lexicons/org/atsui/Caption.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Caption", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Secondary text for timestamps, metadata, or supplementary information.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["children"], 13 + "properties": { 14 + "children": { 15 + "type": "unknown" 16 + } 17 + } 18 + } 19 + }, 20 + "output": { 21 + "encoding": "application/json", 22 + "schema": { 23 + "type": "ref", 24 + "ref": "at.inlay.defs#response" 25 + } 26 + } 27 + } 28 + } 29 + }
+57
lexicons/org/atsui/Clip.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Clip", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Clips child content to a height range relative to its own width. Use min and max to bound the box proportions. Set both to the same value to force an exact aspect ratio.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["children"], 13 + "properties": { 14 + "min": { 15 + "type": "ref", 16 + "ref": "#aspectRatio", 17 + "description": "Minimum box proportions (shortest allowed shape). E.g. {width:1, height:1} means at least as tall as wide." 18 + }, 19 + "max": { 20 + "type": "ref", 21 + "ref": "#aspectRatio", 22 + "description": "Maximum box proportions (tallest allowed shape). E.g. {width:1, height:2} means at most twice as tall as wide." 23 + }, 24 + "children": { 25 + "type": "array", 26 + "items": { 27 + "type": "ref", 28 + "ref": "at.inlay.defs#element" 29 + } 30 + } 31 + } 32 + } 33 + }, 34 + "output": { 35 + "encoding": "application/json", 36 + "schema": { 37 + "type": "ref", 38 + "ref": "at.inlay.defs#response" 39 + } 40 + } 41 + }, 42 + "aspectRatio": { 43 + "type": "object", 44 + "required": ["width", "height"], 45 + "properties": { 46 + "width": { 47 + "type": "integer", 48 + "minimum": 1 49 + }, 50 + "height": { 51 + "type": "integer", 52 + "minimum": 1 53 + } 54 + } 55 + } 56 + } 57 + }
+35
lexicons/org/atsui/Cover.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Cover", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Full-width background image.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["src"], 13 + "properties": { 14 + "src": { 15 + "type": "unknown", 16 + "description": "Blob ref for the background image." 17 + }, 18 + "did": { 19 + "type": "string", 20 + "format": "did", 21 + "description": "DID of the blob owner. Used to resolve blob URLs." 22 + } 23 + } 24 + } 25 + }, 26 + "output": { 27 + "encoding": "application/json", 28 + "schema": { 29 + "type": "ref", 30 + "ref": "at.inlay.defs#response" 31 + } 32 + } 33 + } 34 + } 35 + }
+31
lexicons/org/atsui/Editor.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Editor", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Template editor for component records.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["uri"], 13 + "properties": { 14 + "uri": { 15 + "type": "string", 16 + "format": "at-uri", 17 + "description": "AT-URI of the component record to edit" 18 + } 19 + } 20 + } 21 + }, 22 + "output": { 23 + "encoding": "application/json", 24 + "schema": { 25 + "type": "ref", 26 + "ref": "at.inlay.defs#response" 27 + } 28 + } 29 + } 30 + } 31 + }
+33
lexicons/org/atsui/Fill.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Fill", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Takes remaining space on the parent's main axis.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["children"], 13 + "properties": { 14 + "children": { 15 + "type": "array", 16 + "items": { 17 + "type": "ref", 18 + "ref": "at.inlay.defs#element" 19 + } 20 + } 21 + } 22 + } 23 + }, 24 + "output": { 25 + "encoding": "application/json", 26 + "schema": { 27 + "type": "ref", 28 + "ref": "at.inlay.defs#response" 29 + } 30 + } 31 + } 32 + } 33 + }
+46
lexicons/org/atsui/Grid.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Grid", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Arranges children in a grid of equal columns.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["children"], 13 + "properties": { 14 + "columns": { 15 + "type": "integer", 16 + "minimum": 1, 17 + "description": "Number of equal columns.", 18 + "default": 3 19 + }, 20 + "gap": { 21 + "type": "string", 22 + "maxLength": 32, 23 + "description": "Space between children.", 24 + "knownValues": ["none", "small", "medium", "large"], 25 + "default": "small" 26 + }, 27 + "children": { 28 + "type": "array", 29 + "items": { 30 + "type": "ref", 31 + "ref": "at.inlay.defs#element" 32 + } 33 + } 34 + } 35 + } 36 + }, 37 + "output": { 38 + "encoding": "application/json", 39 + "schema": { 40 + "type": "ref", 41 + "ref": "at.inlay.defs#response" 42 + } 43 + } 44 + } 45 + } 46 + }
+29
lexicons/org/atsui/Heading.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Heading", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "A section label or secondary heading.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["children"], 13 + "properties": { 14 + "children": { 15 + "type": "unknown" 16 + } 17 + } 18 + } 19 + }, 20 + "output": { 21 + "encoding": "application/json", 22 + "schema": { 23 + "type": "ref", 24 + "ref": "at.inlay.defs#response" 25 + } 26 + } 27 + } 28 + } 29 + }
+38
lexicons/org/atsui/Link.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Link", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "A hyperlink.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["uri"], 13 + "properties": { 14 + "uri": { 15 + "type": "string", 16 + "format": "uri" 17 + }, 18 + "decoration": { 19 + "type": "string", 20 + "maxLength": 32, 21 + "knownValues": ["none", "underline"] 22 + }, 23 + "children": { 24 + "type": "unknown" 25 + } 26 + } 27 + } 28 + }, 29 + "output": { 30 + "encoding": "application/json", 31 + "schema": { 32 + "type": "ref", 33 + "ref": "at.inlay.defs#response" 34 + } 35 + } 36 + } 37 + } 38 + }
+57
lexicons/org/atsui/List.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.List", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Scrollable vertical list with paginated data source.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["query", "did"], 13 + "properties": { 14 + "query": { 15 + "type": "string", 16 + "format": "nsid", 17 + "description": "XRPC query to call for pages of items." 18 + }, 19 + "did": { 20 + "type": "string", 21 + "format": "did", 22 + "description": "DID of the service that implements the query." 23 + }, 24 + "input": { 25 + "type": "unknown", 26 + "description": "Parameters to pass to the query." 27 + } 28 + } 29 + } 30 + }, 31 + "output": { 32 + "encoding": "application/json", 33 + "schema": { 34 + "type": "ref", 35 + "ref": "at.inlay.defs#response" 36 + } 37 + } 38 + }, 39 + "page": { 40 + "type": "object", 41 + "description": "Response shape from a List data source query.", 42 + "required": ["items"], 43 + "properties": { 44 + "items": { 45 + "type": "array", 46 + "items": { "type": "ref", "ref": "at.inlay.defs#element" }, 47 + "description": "Elements to render as list rows." 48 + }, 49 + "cursor": { 50 + "type": "string", 51 + "maxLength": 512, 52 + "description": "Opaque pagination token. Absent means no more items." 53 + } 54 + } 55 + } 56 + } 57 + }
+31
lexicons/org/atsui/Record.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Record", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Renders any record.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["uri"], 13 + "properties": { 14 + "uri": { 15 + "type": "string", 16 + "format": "at-uri", 17 + "description": "AT-URI of the record to display" 18 + } 19 + } 20 + } 21 + }, 22 + "output": { 23 + "encoding": "application/json", 24 + "schema": { 25 + "type": "ref", 26 + "ref": "at.inlay.defs#response" 27 + } 28 + } 29 + } 30 + } 31 + }
+66
lexicons/org/atsui/Row.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Row", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Arranges children horizontally with consistent spacing.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["children"], 13 + "properties": { 14 + "gap": { 15 + "type": "string", 16 + "maxLength": 32, 17 + "description": "Space between children.", 18 + "knownValues": ["none", "small", "medium", "large"], 19 + "default": "medium" 20 + }, 21 + "align": { 22 + "type": "string", 23 + "maxLength": 32, 24 + "description": "Cross-axis (vertical) alignment of children.", 25 + "knownValues": ["start", "center", "end", "stretch"], 26 + "default": "center" 27 + }, 28 + "justify": { 29 + "type": "string", 30 + "maxLength": 32, 31 + "description": "Main-axis distribution of children.", 32 + "knownValues": ["start", "center", "end", "between"], 33 + "default": "start" 34 + }, 35 + "inset": { 36 + "type": "boolean", 37 + "description": "Whether this container has inset padding. The theme controls the amount." 38 + }, 39 + "sticky": { 40 + "type": "boolean", 41 + "description": "Whether the container sticks to the top of the scroll area." 42 + }, 43 + "opaque": { 44 + "type": "boolean", 45 + "description": "Whether the container has an opaque background." 46 + }, 47 + "children": { 48 + "type": "array", 49 + "items": { 50 + "type": "ref", 51 + "ref": "at.inlay.defs#element" 52 + } 53 + } 54 + } 55 + } 56 + }, 57 + "output": { 58 + "encoding": "application/json", 59 + "schema": { 60 + "type": "ref", 61 + "ref": "at.inlay.defs#response" 62 + } 63 + } 64 + } 65 + } 66 + }
+70
lexicons/org/atsui/Stack.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Stack", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Arranges children vertically with consistent spacing.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["children"], 13 + "properties": { 14 + "gap": { 15 + "type": "string", 16 + "maxLength": 32, 17 + "description": "Space between children.", 18 + "knownValues": ["none", "small", "medium", "large"], 19 + "default": "medium" 20 + }, 21 + "align": { 22 + "type": "string", 23 + "maxLength": 32, 24 + "description": "Cross-axis alignment of children.", 25 + "knownValues": ["start", "center", "end", "stretch"], 26 + "default": "stretch" 27 + }, 28 + "justify": { 29 + "type": "string", 30 + "maxLength": 32, 31 + "description": "Main-axis distribution of children.", 32 + "knownValues": ["start", "center", "end", "between"], 33 + "default": "start" 34 + }, 35 + "inset": { 36 + "type": "boolean", 37 + "description": "Whether this container has inset padding. The theme controls the amount." 38 + }, 39 + "sticky": { 40 + "type": "boolean", 41 + "description": "Whether the container sticks to the top of the scroll area." 42 + }, 43 + "opaque": { 44 + "type": "boolean", 45 + "description": "Whether the container has an opaque background." 46 + }, 47 + "separator": { 48 + "type": "boolean", 49 + "description": "Whether to show a visual separator between children." 50 + }, 51 + "children": { 52 + "type": "array", 53 + "items": { 54 + "type": "ref", 55 + "ref": "at.inlay.defs#element" 56 + } 57 + } 58 + } 59 + } 60 + }, 61 + "output": { 62 + "encoding": "application/json", 63 + "schema": { 64 + "type": "ref", 65 + "ref": "at.inlay.defs#response" 66 + } 67 + } 68 + } 69 + } 70 + }
+52
lexicons/org/atsui/Tabs.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Tabs", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Tabbed content with instant client-side switching.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["items"], 13 + "properties": { 14 + "items": { 15 + "type": "array", 16 + "items": { "type": "ref", "ref": "#tab" }, 17 + "description": "Tabs to display." 18 + } 19 + } 20 + } 21 + }, 22 + "output": { 23 + "encoding": "application/json", 24 + "schema": { 25 + "type": "ref", 26 + "ref": "at.inlay.defs#response" 27 + } 28 + } 29 + }, 30 + "tab": { 31 + "type": "object", 32 + "required": ["key", "label", "content"], 33 + "properties": { 34 + "key": { 35 + "type": "string", 36 + "maxLength": 64, 37 + "description": "Stable key that identifies the tab among its siblings." 38 + }, 39 + "label": { 40 + "type": "string", 41 + "maxLength": 128, 42 + "description": "Display label for the tab." 43 + }, 44 + "content": { 45 + "type": "ref", 46 + "ref": "at.inlay.defs#element", 47 + "description": "Element to render as tab content." 48 + } 49 + } 50 + } 51 + } 52 + }
+29
lexicons/org/atsui/Text.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Text", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Body text.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["children"], 13 + "properties": { 14 + "children": { 15 + "type": "unknown" 16 + } 17 + } 18 + } 19 + }, 20 + "output": { 21 + "encoding": "application/json", 22 + "schema": { 23 + "type": "ref", 24 + "ref": "at.inlay.defs#response" 25 + } 26 + } 27 + } 28 + } 29 + }
+30
lexicons/org/atsui/Timestamp.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Timestamp", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "Displays a datetime value.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["value"], 13 + "properties": { 14 + "value": { 15 + "type": "string", 16 + "format": "datetime" 17 + } 18 + } 19 + } 20 + }, 21 + "output": { 22 + "encoding": "application/json", 23 + "schema": { 24 + "type": "ref", 25 + "ref": "at.inlay.defs#response" 26 + } 27 + } 28 + } 29 + } 30 + }
+29
lexicons/org/atsui/Title.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "org.atsui.Title", 4 + "defs": { 5 + "main": { 6 + "type": "procedure", 7 + "description": "The primary heading for a card or section.", 8 + "input": { 9 + "encoding": "application/json", 10 + "schema": { 11 + "type": "object", 12 + "required": ["children"], 13 + "properties": { 14 + "children": { 15 + "type": "unknown" 16 + } 17 + } 18 + } 19 + }, 20 + "output": { 21 + "encoding": "application/json", 22 + "schema": { 23 + "type": "ref", 24 + "ref": "at.inlay.defs#response" 25 + } 26 + } 27 + } 28 + } 29 + }
+3911
package-lock.json
··· 1 + { 2 + "name": "inlay", 3 + "lockfileVersion": 3, 4 + "requires": true, 5 + "packages": { 6 + "": { 7 + "name": "inlay", 8 + "workspaces": [ 9 + "packages/@inlay/core", 10 + "packages/@inlay/render", 11 + "packages/@inlay/cache", 12 + "proto", 13 + "invalidator" 14 + ], 15 + "devDependencies": { 16 + "@atproto/lex": "^0.0.18", 17 + "@atproto/lex-resolver": "^0.0.15", 18 + "eslint": "^9.0.0", 19 + "husky": "^9.0.0", 20 + "lint-staged": "^15.0.0", 21 + "prettier": "^3.0.0", 22 + "tsx": "^4.0.0", 23 + "typescript": "^5.9.0", 24 + "typescript-eslint": "^8.0.0" 25 + }, 26 + "engines": { 27 + "node": ">=22.0.0" 28 + } 29 + }, 30 + "invalidator": { 31 + "name": "@atui/invalidator", 32 + "version": "0.0.1", 33 + "dependencies": { 34 + "dotenv": "^17.2.3", 35 + "ioredis": "^5.6.0", 36 + "ws": "^8.18.0" 37 + }, 38 + "devDependencies": { 39 + "@types/ws": "^8.0.0", 40 + "tsx": "^4.0.0" 41 + } 42 + }, 43 + "invalidator/node_modules/dotenv": { 44 + "version": "17.3.1", 45 + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", 46 + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", 47 + "license": "BSD-2-Clause", 48 + "engines": { 49 + "node": ">=12" 50 + }, 51 + "funding": { 52 + "url": "https://dotenvx.com" 53 + } 54 + }, 55 + "node_modules/@atproto-labs/did-resolver": { 56 + "version": "0.2.6", 57 + "resolved": "https://registry.npmjs.org/@atproto-labs/did-resolver/-/did-resolver-0.2.6.tgz", 58 + "integrity": "sha512-2K1bC04nI2fmgNcvof+yA28IhGlpWn2JKYlPa7To9JTKI45FINCGkQSGiL2nyXlyzDJJ34fZ1aq6/IRFIOIiqg==", 59 + "license": "MIT", 60 + "dependencies": { 61 + "@atproto-labs/fetch": "0.2.3", 62 + "@atproto-labs/pipe": "0.1.1", 63 + "@atproto-labs/simple-store": "0.3.0", 64 + "@atproto-labs/simple-store-memory": "0.1.4", 65 + "@atproto/did": "0.3.0", 66 + "zod": "^3.23.8" 67 + } 68 + }, 69 + "node_modules/@atproto-labs/fetch": { 70 + "version": "0.2.3", 71 + "resolved": "https://registry.npmjs.org/@atproto-labs/fetch/-/fetch-0.2.3.tgz", 72 + "integrity": "sha512-NZtbJOCbxKUFRFKMpamT38PUQMY0hX0p7TG5AEYOPhZKZEP7dHZ1K2s1aB8MdVH0qxmqX7nQleNrrvLf09Zfdw==", 73 + "license": "MIT", 74 + "dependencies": { 75 + "@atproto-labs/pipe": "0.1.1" 76 + } 77 + }, 78 + "node_modules/@atproto-labs/pipe": { 79 + "version": "0.1.1", 80 + "resolved": "https://registry.npmjs.org/@atproto-labs/pipe/-/pipe-0.1.1.tgz", 81 + "integrity": "sha512-hdNw2oUs2B6BN1lp+32pF7cp8EMKuIN5Qok2Vvv/aOpG/3tNSJ9YkvfI0k6Zd188LeDDYRUpYpxcoFIcGH/FNg==", 82 + "license": "MIT" 83 + }, 84 + "node_modules/@atproto-labs/simple-store": { 85 + "version": "0.3.0", 86 + "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store/-/simple-store-0.3.0.tgz", 87 + "integrity": "sha512-nOb6ONKBRJHRlukW1sVawUkBqReLlLx6hT35VS3imaNPwiXDxLnTK7lxw3Lrl9k5yugSBDQAkZAq3MPTEFSUBQ==", 88 + "license": "MIT" 89 + }, 90 + "node_modules/@atproto-labs/simple-store-memory": { 91 + "version": "0.1.4", 92 + "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store-memory/-/simple-store-memory-0.1.4.tgz", 93 + "integrity": "sha512-3mKY4dP8I7yKPFj9VKpYyCRzGJOi5CEpOLPlRhoJyLmgs3J4RzDrjn323Oakjz2Aj2JzRU/AIvWRAZVhpYNJHw==", 94 + "license": "MIT", 95 + "dependencies": { 96 + "@atproto-labs/simple-store": "0.3.0", 97 + "lru-cache": "^10.2.0" 98 + } 99 + }, 100 + "node_modules/@atproto/common": { 101 + "version": "0.5.13", 102 + "resolved": "https://registry.npmjs.org/@atproto/common/-/common-0.5.13.tgz", 103 + "integrity": "sha512-+5c3wlvZVCCReoPSwvkPhKz3Y2FZlJzm69BrfkHMccAH0Rs0KLwnWtoE34zyzTLNH7hhy5okx5qJS6+ZlgE9Sg==", 104 + "license": "MIT", 105 + "dependencies": { 106 + "@atproto/common-web": "^0.4.17", 107 + "@atproto/lex-cbor": "^0.0.13", 108 + "@atproto/lex-data": "^0.0.12", 109 + "iso-datestring-validator": "^2.2.2", 110 + "multiformats": "^9.9.0", 111 + "pino": "^8.21.0" 112 + }, 113 + "engines": { 114 + "node": ">=18.7.0" 115 + } 116 + }, 117 + "node_modules/@atproto/common-web": { 118 + "version": "0.4.17", 119 + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.17.tgz", 120 + "integrity": "sha512-sfxD8NGxyoxhxmM9EUshEFbWcJ3+JHEOZF4Quk6HsCh1UxpHBmLabT/vEsAkDWl+C/8U0ine0+c/gHyE/OZiQQ==", 121 + "license": "MIT", 122 + "dependencies": { 123 + "@atproto/lex-data": "^0.0.12", 124 + "@atproto/lex-json": "^0.0.12", 125 + "@atproto/syntax": "^0.4.3", 126 + "zod": "^3.23.8" 127 + } 128 + }, 129 + "node_modules/@atproto/crypto": { 130 + "version": "0.4.5", 131 + "resolved": "https://registry.npmjs.org/@atproto/crypto/-/crypto-0.4.5.tgz", 132 + "integrity": "sha512-n40aKkMoCatP0u9Yvhrdk6fXyOHFDDbkdm4h4HCyWW+KlKl8iXfD5iV+ECq+w5BM+QH25aIpt3/j6EUNerhLxw==", 133 + "license": "MIT", 134 + "dependencies": { 135 + "@noble/curves": "^1.7.0", 136 + "@noble/hashes": "^1.6.1", 137 + "uint8arrays": "3.0.0" 138 + }, 139 + "engines": { 140 + "node": ">=18.7.0" 141 + } 142 + }, 143 + "node_modules/@atproto/did": { 144 + "version": "0.3.0", 145 + "resolved": "https://registry.npmjs.org/@atproto/did/-/did-0.3.0.tgz", 146 + "integrity": "sha512-raUPzUGegtW/6OxwCmM8bhZvuIMzxG5t9oWsth6Tp91Kb5fTnHV2h/KKNF1C82doeA4BdXCErTyg7ISwLbQkzA==", 147 + "license": "MIT", 148 + "dependencies": { 149 + "zod": "^3.23.8" 150 + } 151 + }, 152 + "node_modules/@atproto/lex": { 153 + "version": "0.0.18", 154 + "resolved": "https://registry.npmjs.org/@atproto/lex/-/lex-0.0.18.tgz", 155 + "integrity": "sha512-uHqtV2gZNYOkOYXq1t6wO2Nb0gFfGmi40AGCVbTNccIkHsZvxRuLlaNuNrBFB+5h/HzlVeMwjBUf0ny3jD4hXw==", 156 + "license": "MIT", 157 + "dependencies": { 158 + "@atproto/lex-builder": "^0.0.16", 159 + "@atproto/lex-client": "^0.0.13", 160 + "@atproto/lex-data": "^0.0.12", 161 + "@atproto/lex-installer": "^0.0.18", 162 + "@atproto/lex-json": "^0.0.12", 163 + "@atproto/lex-schema": "^0.0.13", 164 + "tslib": "^2.8.1", 165 + "yargs": "^17.0.0" 166 + }, 167 + "bin": { 168 + "lex": "bin/lex", 169 + "ts-lex": "bin/lex" 170 + } 171 + }, 172 + "node_modules/@atproto/lex-builder": { 173 + "version": "0.0.16", 174 + "resolved": "https://registry.npmjs.org/@atproto/lex-builder/-/lex-builder-0.0.16.tgz", 175 + "integrity": "sha512-z9h6kLiifyL0mBVzlHJ3cK3XwhHRttSePmk2XmhQ1gC0tfa7exUhvCMp9YpEv2N5oUVMItl00L8SLBsVsuMMtg==", 176 + "license": "MIT", 177 + "dependencies": { 178 + "@atproto/lex-document": "^0.0.14", 179 + "@atproto/lex-schema": "^0.0.13", 180 + "prettier": "^3.2.5", 181 + "ts-morph": "^27.0.0", 182 + "tslib": "^2.8.1" 183 + } 184 + }, 185 + "node_modules/@atproto/lex-cbor": { 186 + "version": "0.0.13", 187 + "resolved": "https://registry.npmjs.org/@atproto/lex-cbor/-/lex-cbor-0.0.13.tgz", 188 + "integrity": "sha512-63nbzXJnQwV02XGpEa8WZxt7Zu87dnbzrUVL0Mqr55S1EGCzEF9U7Dauc9tKKLoZ88GmYrJN0irBsXtSi0VeWg==", 189 + "license": "MIT", 190 + "dependencies": { 191 + "@atproto/lex-data": "^0.0.12", 192 + "tslib": "^2.8.1" 193 + } 194 + }, 195 + "node_modules/@atproto/lex-client": { 196 + "version": "0.0.13", 197 + "resolved": "https://registry.npmjs.org/@atproto/lex-client/-/lex-client-0.0.13.tgz", 198 + "integrity": "sha512-NftQ9SSIilMFFj99fBlv1hvZ6Oe4Bl+HYn4VkXrWsGrHeOIM3GLgVZSMWAlg33rCvd6bYfb+YnIdPxcV6lCU0g==", 199 + "license": "MIT", 200 + "dependencies": { 201 + "@atproto/lex-data": "^0.0.12", 202 + "@atproto/lex-json": "^0.0.12", 203 + "@atproto/lex-schema": "^0.0.13", 204 + "tslib": "^2.8.1" 205 + } 206 + }, 207 + "node_modules/@atproto/lex-data": { 208 + "version": "0.0.12", 209 + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.12.tgz", 210 + "integrity": "sha512-aekJudcK1p6sbTqUv2bJMJBAGZaOJS0mgDclpK3U6VuBREK/au4B6ffunBFWgrDfg0Vwj2JGyEA7E51WZkJcRw==", 211 + "license": "MIT", 212 + "dependencies": { 213 + "multiformats": "^9.9.0", 214 + "tslib": "^2.8.1", 215 + "uint8arrays": "3.0.0", 216 + "unicode-segmenter": "^0.14.0" 217 + } 218 + }, 219 + "node_modules/@atproto/lex-document": { 220 + "version": "0.0.14", 221 + "resolved": "https://registry.npmjs.org/@atproto/lex-document/-/lex-document-0.0.14.tgz", 222 + "integrity": "sha512-BaCSZOZUIv3kQ23b3Lhe4sprJYHc0spSeWS3TLaMoVi6bFZ3RzeM8n7ROTzVP3BTNYxpRHPHtOarx9A8ZAAC4w==", 223 + "license": "MIT", 224 + "dependencies": { 225 + "@atproto/lex-schema": "^0.0.13", 226 + "core-js": "^3", 227 + "tslib": "^2.8.1" 228 + } 229 + }, 230 + "node_modules/@atproto/lex-installer": { 231 + "version": "0.0.18", 232 + "resolved": "https://registry.npmjs.org/@atproto/lex-installer/-/lex-installer-0.0.18.tgz", 233 + "integrity": "sha512-ukDMHIpoaqk6ph0kFnLsRnYr3TJ+rDsAyVRwTzdiLb29pPYgQHD+3wSMHH/rQ7XXUuPklv4SFBTKLTMeIeEgkg==", 234 + "license": "MIT", 235 + "dependencies": { 236 + "@atproto/lex-builder": "^0.0.16", 237 + "@atproto/lex-cbor": "^0.0.13", 238 + "@atproto/lex-data": "^0.0.12", 239 + "@atproto/lex-document": "^0.0.14", 240 + "@atproto/lex-resolver": "^0.0.15", 241 + "@atproto/lex-schema": "^0.0.13", 242 + "@atproto/syntax": "^0.4.3", 243 + "tslib": "^2.8.1" 244 + } 245 + }, 246 + "node_modules/@atproto/lex-json": { 247 + "version": "0.0.12", 248 + "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.12.tgz", 249 + "integrity": "sha512-XlEpnWWZdDJ5BIgG25GyH+6iBfyrFL18BI5JSE6rUfMObbFMrQRaCuRLQfryRXNysVz3L3U+Qb9y8KcXbE8AcA==", 250 + "license": "MIT", 251 + "dependencies": { 252 + "@atproto/lex-data": "^0.0.12", 253 + "tslib": "^2.8.1" 254 + } 255 + }, 256 + "node_modules/@atproto/lex-resolver": { 257 + "version": "0.0.15", 258 + "resolved": "https://registry.npmjs.org/@atproto/lex-resolver/-/lex-resolver-0.0.15.tgz", 259 + "integrity": "sha512-oNxcNCts3ReJ+A4hTPthQbPA68yMQ0ZrBUIiUTZwRazrmg/xkV15jtyYSnH4CGBjRdt420MV9aLR2s4qLSmyTQ==", 260 + "license": "MIT", 261 + "dependencies": { 262 + "@atproto-labs/did-resolver": "^0.2.6", 263 + "@atproto/crypto": "^0.4.5", 264 + "@atproto/lex-client": "^0.0.13", 265 + "@atproto/lex-data": "^0.0.12", 266 + "@atproto/lex-document": "^0.0.14", 267 + "@atproto/lex-schema": "^0.0.13", 268 + "@atproto/repo": "^0.8.12", 269 + "@atproto/syntax": "^0.4.3", 270 + "tslib": "^2.8.1" 271 + } 272 + }, 273 + "node_modules/@atproto/lex-schema": { 274 + "version": "0.0.13", 275 + "resolved": "https://registry.npmjs.org/@atproto/lex-schema/-/lex-schema-0.0.13.tgz", 276 + "integrity": "sha512-FeY4YBesEUO4Ey3BJhDRma0cZt6XxunSZPXny5Q/6ltc7pvyJGXXtJ8D7mHl7p5EXPwylEYOQkM6ck4IyfMP0A==", 277 + "license": "MIT", 278 + "dependencies": { 279 + "@atproto/lex-data": "^0.0.12", 280 + "@atproto/syntax": "^0.4.3", 281 + "tslib": "^2.8.1" 282 + } 283 + }, 284 + "node_modules/@atproto/lexicon": { 285 + "version": "0.6.1", 286 + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.6.1.tgz", 287 + "integrity": "sha512-/vI1kVlY50Si+5MXpvOucelnYwb0UJ6Qto5mCp+7Q5C+Jtp+SoSykAPVvjVtTnQUH2vrKOFOwpb3C375vSKzXw==", 288 + "license": "MIT", 289 + "dependencies": { 290 + "@atproto/common-web": "^0.4.13", 291 + "@atproto/syntax": "^0.4.3", 292 + "iso-datestring-validator": "^2.2.2", 293 + "multiformats": "^9.9.0", 294 + "zod": "^3.23.8" 295 + } 296 + }, 297 + "node_modules/@atproto/repo": { 298 + "version": "0.8.12", 299 + "resolved": "https://registry.npmjs.org/@atproto/repo/-/repo-0.8.12.tgz", 300 + "integrity": "sha512-QpVTVulgfz5PUiCTELlDBiRvnsnwrFWi+6CfY88VwXzrRHd9NE8GItK7sfxQ6U65vD/idH8ddCgFrlrsn1REPQ==", 301 + "license": "MIT", 302 + "dependencies": { 303 + "@atproto/common": "^0.5.3", 304 + "@atproto/common-web": "^0.4.7", 305 + "@atproto/crypto": "^0.4.5", 306 + "@atproto/lexicon": "^0.6.0", 307 + "@ipld/dag-cbor": "^7.0.0", 308 + "multiformats": "^9.9.0", 309 + "uint8arrays": "3.0.0", 310 + "varint": "^6.0.0", 311 + "zod": "^3.23.8" 312 + }, 313 + "engines": { 314 + "node": ">=18.7.0" 315 + } 316 + }, 317 + "node_modules/@atproto/syntax": { 318 + "version": "0.4.3", 319 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.3.tgz", 320 + "integrity": "sha512-YoZUz40YAJr5nPwvCDWgodEOlt5IftZqPJvA0JDWjuZKD8yXddTwSzXSaKQAzGOpuM+/A3uXRtPzJJqlScc+iA==", 321 + "license": "MIT", 322 + "dependencies": { 323 + "tslib": "^2.8.1" 324 + } 325 + }, 326 + "node_modules/@atui/invalidator": { 327 + "resolved": "invalidator", 328 + "link": true 329 + }, 330 + "node_modules/@esbuild/aix-ppc64": { 331 + "version": "0.27.3", 332 + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", 333 + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", 334 + "cpu": [ 335 + "ppc64" 336 + ], 337 + "dev": true, 338 + "license": "MIT", 339 + "optional": true, 340 + "os": [ 341 + "aix" 342 + ], 343 + "engines": { 344 + "node": ">=18" 345 + } 346 + }, 347 + "node_modules/@esbuild/android-arm": { 348 + "version": "0.27.3", 349 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", 350 + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", 351 + "cpu": [ 352 + "arm" 353 + ], 354 + "dev": true, 355 + "license": "MIT", 356 + "optional": true, 357 + "os": [ 358 + "android" 359 + ], 360 + "engines": { 361 + "node": ">=18" 362 + } 363 + }, 364 + "node_modules/@esbuild/android-arm64": { 365 + "version": "0.27.3", 366 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", 367 + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", 368 + "cpu": [ 369 + "arm64" 370 + ], 371 + "dev": true, 372 + "license": "MIT", 373 + "optional": true, 374 + "os": [ 375 + "android" 376 + ], 377 + "engines": { 378 + "node": ">=18" 379 + } 380 + }, 381 + "node_modules/@esbuild/android-x64": { 382 + "version": "0.27.3", 383 + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", 384 + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", 385 + "cpu": [ 386 + "x64" 387 + ], 388 + "dev": true, 389 + "license": "MIT", 390 + "optional": true, 391 + "os": [ 392 + "android" 393 + ], 394 + "engines": { 395 + "node": ">=18" 396 + } 397 + }, 398 + "node_modules/@esbuild/darwin-arm64": { 399 + "version": "0.27.3", 400 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", 401 + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", 402 + "cpu": [ 403 + "arm64" 404 + ], 405 + "dev": true, 406 + "license": "MIT", 407 + "optional": true, 408 + "os": [ 409 + "darwin" 410 + ], 411 + "engines": { 412 + "node": ">=18" 413 + } 414 + }, 415 + "node_modules/@esbuild/darwin-x64": { 416 + "version": "0.27.3", 417 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", 418 + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", 419 + "cpu": [ 420 + "x64" 421 + ], 422 + "dev": true, 423 + "license": "MIT", 424 + "optional": true, 425 + "os": [ 426 + "darwin" 427 + ], 428 + "engines": { 429 + "node": ">=18" 430 + } 431 + }, 432 + "node_modules/@esbuild/freebsd-arm64": { 433 + "version": "0.27.3", 434 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", 435 + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", 436 + "cpu": [ 437 + "arm64" 438 + ], 439 + "dev": true, 440 + "license": "MIT", 441 + "optional": true, 442 + "os": [ 443 + "freebsd" 444 + ], 445 + "engines": { 446 + "node": ">=18" 447 + } 448 + }, 449 + "node_modules/@esbuild/freebsd-x64": { 450 + "version": "0.27.3", 451 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", 452 + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", 453 + "cpu": [ 454 + "x64" 455 + ], 456 + "dev": true, 457 + "license": "MIT", 458 + "optional": true, 459 + "os": [ 460 + "freebsd" 461 + ], 462 + "engines": { 463 + "node": ">=18" 464 + } 465 + }, 466 + "node_modules/@esbuild/linux-arm": { 467 + "version": "0.27.3", 468 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", 469 + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", 470 + "cpu": [ 471 + "arm" 472 + ], 473 + "dev": true, 474 + "license": "MIT", 475 + "optional": true, 476 + "os": [ 477 + "linux" 478 + ], 479 + "engines": { 480 + "node": ">=18" 481 + } 482 + }, 483 + "node_modules/@esbuild/linux-arm64": { 484 + "version": "0.27.3", 485 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", 486 + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", 487 + "cpu": [ 488 + "arm64" 489 + ], 490 + "dev": true, 491 + "license": "MIT", 492 + "optional": true, 493 + "os": [ 494 + "linux" 495 + ], 496 + "engines": { 497 + "node": ">=18" 498 + } 499 + }, 500 + "node_modules/@esbuild/linux-ia32": { 501 + "version": "0.27.3", 502 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", 503 + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", 504 + "cpu": [ 505 + "ia32" 506 + ], 507 + "dev": true, 508 + "license": "MIT", 509 + "optional": true, 510 + "os": [ 511 + "linux" 512 + ], 513 + "engines": { 514 + "node": ">=18" 515 + } 516 + }, 517 + "node_modules/@esbuild/linux-loong64": { 518 + "version": "0.27.3", 519 + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", 520 + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", 521 + "cpu": [ 522 + "loong64" 523 + ], 524 + "dev": true, 525 + "license": "MIT", 526 + "optional": true, 527 + "os": [ 528 + "linux" 529 + ], 530 + "engines": { 531 + "node": ">=18" 532 + } 533 + }, 534 + "node_modules/@esbuild/linux-mips64el": { 535 + "version": "0.27.3", 536 + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", 537 + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", 538 + "cpu": [ 539 + "mips64el" 540 + ], 541 + "dev": true, 542 + "license": "MIT", 543 + "optional": true, 544 + "os": [ 545 + "linux" 546 + ], 547 + "engines": { 548 + "node": ">=18" 549 + } 550 + }, 551 + "node_modules/@esbuild/linux-ppc64": { 552 + "version": "0.27.3", 553 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", 554 + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", 555 + "cpu": [ 556 + "ppc64" 557 + ], 558 + "dev": true, 559 + "license": "MIT", 560 + "optional": true, 561 + "os": [ 562 + "linux" 563 + ], 564 + "engines": { 565 + "node": ">=18" 566 + } 567 + }, 568 + "node_modules/@esbuild/linux-riscv64": { 569 + "version": "0.27.3", 570 + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", 571 + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", 572 + "cpu": [ 573 + "riscv64" 574 + ], 575 + "dev": true, 576 + "license": "MIT", 577 + "optional": true, 578 + "os": [ 579 + "linux" 580 + ], 581 + "engines": { 582 + "node": ">=18" 583 + } 584 + }, 585 + "node_modules/@esbuild/linux-s390x": { 586 + "version": "0.27.3", 587 + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", 588 + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", 589 + "cpu": [ 590 + "s390x" 591 + ], 592 + "dev": true, 593 + "license": "MIT", 594 + "optional": true, 595 + "os": [ 596 + "linux" 597 + ], 598 + "engines": { 599 + "node": ">=18" 600 + } 601 + }, 602 + "node_modules/@esbuild/linux-x64": { 603 + "version": "0.27.3", 604 + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", 605 + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", 606 + "cpu": [ 607 + "x64" 608 + ], 609 + "dev": true, 610 + "license": "MIT", 611 + "optional": true, 612 + "os": [ 613 + "linux" 614 + ], 615 + "engines": { 616 + "node": ">=18" 617 + } 618 + }, 619 + "node_modules/@esbuild/netbsd-arm64": { 620 + "version": "0.27.3", 621 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", 622 + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", 623 + "cpu": [ 624 + "arm64" 625 + ], 626 + "dev": true, 627 + "license": "MIT", 628 + "optional": true, 629 + "os": [ 630 + "netbsd" 631 + ], 632 + "engines": { 633 + "node": ">=18" 634 + } 635 + }, 636 + "node_modules/@esbuild/netbsd-x64": { 637 + "version": "0.27.3", 638 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", 639 + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", 640 + "cpu": [ 641 + "x64" 642 + ], 643 + "dev": true, 644 + "license": "MIT", 645 + "optional": true, 646 + "os": [ 647 + "netbsd" 648 + ], 649 + "engines": { 650 + "node": ">=18" 651 + } 652 + }, 653 + "node_modules/@esbuild/openbsd-arm64": { 654 + "version": "0.27.3", 655 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", 656 + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", 657 + "cpu": [ 658 + "arm64" 659 + ], 660 + "dev": true, 661 + "license": "MIT", 662 + "optional": true, 663 + "os": [ 664 + "openbsd" 665 + ], 666 + "engines": { 667 + "node": ">=18" 668 + } 669 + }, 670 + "node_modules/@esbuild/openbsd-x64": { 671 + "version": "0.27.3", 672 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", 673 + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", 674 + "cpu": [ 675 + "x64" 676 + ], 677 + "dev": true, 678 + "license": "MIT", 679 + "optional": true, 680 + "os": [ 681 + "openbsd" 682 + ], 683 + "engines": { 684 + "node": ">=18" 685 + } 686 + }, 687 + "node_modules/@esbuild/openharmony-arm64": { 688 + "version": "0.27.3", 689 + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", 690 + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", 691 + "cpu": [ 692 + "arm64" 693 + ], 694 + "dev": true, 695 + "license": "MIT", 696 + "optional": true, 697 + "os": [ 698 + "openharmony" 699 + ], 700 + "engines": { 701 + "node": ">=18" 702 + } 703 + }, 704 + "node_modules/@esbuild/sunos-x64": { 705 + "version": "0.27.3", 706 + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", 707 + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", 708 + "cpu": [ 709 + "x64" 710 + ], 711 + "dev": true, 712 + "license": "MIT", 713 + "optional": true, 714 + "os": [ 715 + "sunos" 716 + ], 717 + "engines": { 718 + "node": ">=18" 719 + } 720 + }, 721 + "node_modules/@esbuild/win32-arm64": { 722 + "version": "0.27.3", 723 + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", 724 + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", 725 + "cpu": [ 726 + "arm64" 727 + ], 728 + "dev": true, 729 + "license": "MIT", 730 + "optional": true, 731 + "os": [ 732 + "win32" 733 + ], 734 + "engines": { 735 + "node": ">=18" 736 + } 737 + }, 738 + "node_modules/@esbuild/win32-ia32": { 739 + "version": "0.27.3", 740 + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", 741 + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", 742 + "cpu": [ 743 + "ia32" 744 + ], 745 + "dev": true, 746 + "license": "MIT", 747 + "optional": true, 748 + "os": [ 749 + "win32" 750 + ], 751 + "engines": { 752 + "node": ">=18" 753 + } 754 + }, 755 + "node_modules/@esbuild/win32-x64": { 756 + "version": "0.27.3", 757 + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", 758 + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", 759 + "cpu": [ 760 + "x64" 761 + ], 762 + "dev": true, 763 + "license": "MIT", 764 + "optional": true, 765 + "os": [ 766 + "win32" 767 + ], 768 + "engines": { 769 + "node": ">=18" 770 + } 771 + }, 772 + "node_modules/@eslint-community/eslint-utils": { 773 + "version": "4.9.1", 774 + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", 775 + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", 776 + "dev": true, 777 + "license": "MIT", 778 + "dependencies": { 779 + "eslint-visitor-keys": "^3.4.3" 780 + }, 781 + "engines": { 782 + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 783 + }, 784 + "funding": { 785 + "url": "https://opencollective.com/eslint" 786 + }, 787 + "peerDependencies": { 788 + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" 789 + } 790 + }, 791 + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { 792 + "version": "3.4.3", 793 + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", 794 + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", 795 + "dev": true, 796 + "license": "Apache-2.0", 797 + "engines": { 798 + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 799 + }, 800 + "funding": { 801 + "url": "https://opencollective.com/eslint" 802 + } 803 + }, 804 + "node_modules/@eslint-community/regexpp": { 805 + "version": "4.12.2", 806 + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", 807 + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", 808 + "dev": true, 809 + "license": "MIT", 810 + "engines": { 811 + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" 812 + } 813 + }, 814 + "node_modules/@eslint/config-array": { 815 + "version": "0.21.1", 816 + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", 817 + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", 818 + "dev": true, 819 + "license": "Apache-2.0", 820 + "dependencies": { 821 + "@eslint/object-schema": "^2.1.7", 822 + "debug": "^4.3.1", 823 + "minimatch": "^3.1.2" 824 + }, 825 + "engines": { 826 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 827 + } 828 + }, 829 + "node_modules/@eslint/config-helpers": { 830 + "version": "0.4.2", 831 + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", 832 + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", 833 + "dev": true, 834 + "license": "Apache-2.0", 835 + "dependencies": { 836 + "@eslint/core": "^0.17.0" 837 + }, 838 + "engines": { 839 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 840 + } 841 + }, 842 + "node_modules/@eslint/core": { 843 + "version": "0.17.0", 844 + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", 845 + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", 846 + "dev": true, 847 + "license": "Apache-2.0", 848 + "dependencies": { 849 + "@types/json-schema": "^7.0.15" 850 + }, 851 + "engines": { 852 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 853 + } 854 + }, 855 + "node_modules/@eslint/eslintrc": { 856 + "version": "3.3.4", 857 + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.4.tgz", 858 + "integrity": "sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ==", 859 + "dev": true, 860 + "license": "MIT", 861 + "dependencies": { 862 + "ajv": "^6.14.0", 863 + "debug": "^4.3.2", 864 + "espree": "^10.0.1", 865 + "globals": "^14.0.0", 866 + "ignore": "^5.2.0", 867 + "import-fresh": "^3.2.1", 868 + "js-yaml": "^4.1.1", 869 + "minimatch": "^3.1.3", 870 + "strip-json-comments": "^3.1.1" 871 + }, 872 + "engines": { 873 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 874 + }, 875 + "funding": { 876 + "url": "https://opencollective.com/eslint" 877 + } 878 + }, 879 + "node_modules/@eslint/js": { 880 + "version": "9.39.3", 881 + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.3.tgz", 882 + "integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==", 883 + "dev": true, 884 + "license": "MIT", 885 + "engines": { 886 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 887 + }, 888 + "funding": { 889 + "url": "https://eslint.org/donate" 890 + } 891 + }, 892 + "node_modules/@eslint/object-schema": { 893 + "version": "2.1.7", 894 + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", 895 + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", 896 + "dev": true, 897 + "license": "Apache-2.0", 898 + "engines": { 899 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 900 + } 901 + }, 902 + "node_modules/@eslint/plugin-kit": { 903 + "version": "0.4.1", 904 + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", 905 + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", 906 + "dev": true, 907 + "license": "Apache-2.0", 908 + "dependencies": { 909 + "@eslint/core": "^0.17.0", 910 + "levn": "^0.4.1" 911 + }, 912 + "engines": { 913 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 914 + } 915 + }, 916 + "node_modules/@hono/node-server": { 917 + "version": "1.19.9", 918 + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", 919 + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", 920 + "license": "MIT", 921 + "engines": { 922 + "node": ">=18.14.1" 923 + }, 924 + "peerDependencies": { 925 + "hono": "^4" 926 + } 927 + }, 928 + "node_modules/@humanfs/core": { 929 + "version": "0.19.1", 930 + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", 931 + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", 932 + "dev": true, 933 + "license": "Apache-2.0", 934 + "engines": { 935 + "node": ">=18.18.0" 936 + } 937 + }, 938 + "node_modules/@humanfs/node": { 939 + "version": "0.16.7", 940 + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", 941 + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", 942 + "dev": true, 943 + "license": "Apache-2.0", 944 + "dependencies": { 945 + "@humanfs/core": "^0.19.1", 946 + "@humanwhocodes/retry": "^0.4.0" 947 + }, 948 + "engines": { 949 + "node": ">=18.18.0" 950 + } 951 + }, 952 + "node_modules/@humanwhocodes/module-importer": { 953 + "version": "1.0.1", 954 + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", 955 + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", 956 + "dev": true, 957 + "license": "Apache-2.0", 958 + "engines": { 959 + "node": ">=12.22" 960 + }, 961 + "funding": { 962 + "type": "github", 963 + "url": "https://github.com/sponsors/nzakas" 964 + } 965 + }, 966 + "node_modules/@humanwhocodes/retry": { 967 + "version": "0.4.3", 968 + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", 969 + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", 970 + "dev": true, 971 + "license": "Apache-2.0", 972 + "engines": { 973 + "node": ">=18.18" 974 + }, 975 + "funding": { 976 + "type": "github", 977 + "url": "https://github.com/sponsors/nzakas" 978 + } 979 + }, 980 + "node_modules/@inlay/cache": { 981 + "resolved": "packages/@inlay/cache", 982 + "link": true 983 + }, 984 + "node_modules/@inlay/core": { 985 + "resolved": "packages/@inlay/core", 986 + "link": true 987 + }, 988 + "node_modules/@inlay/render": { 989 + "resolved": "packages/@inlay/render", 990 + "link": true 991 + }, 992 + "node_modules/@ioredis/commands": { 993 + "version": "1.5.0", 994 + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.5.0.tgz", 995 + "integrity": "sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow==", 996 + "license": "MIT" 997 + }, 998 + "node_modules/@ipld/dag-cbor": { 999 + "version": "7.0.3", 1000 + "resolved": "https://registry.npmjs.org/@ipld/dag-cbor/-/dag-cbor-7.0.3.tgz", 1001 + "integrity": "sha512-1VVh2huHsuohdXC1bGJNE8WR72slZ9XE2T3wbBBq31dm7ZBatmKLLxrB+XAqafxfRFjv08RZmj/W/ZqaM13AuA==", 1002 + "license": "(Apache-2.0 AND MIT)", 1003 + "dependencies": { 1004 + "cborg": "^1.6.0", 1005 + "multiformats": "^9.5.4" 1006 + } 1007 + }, 1008 + "node_modules/@noble/curves": { 1009 + "version": "1.9.7", 1010 + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", 1011 + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", 1012 + "license": "MIT", 1013 + "dependencies": { 1014 + "@noble/hashes": "1.8.0" 1015 + }, 1016 + "engines": { 1017 + "node": "^14.21.3 || >=16" 1018 + }, 1019 + "funding": { 1020 + "url": "https://paulmillr.com/funding/" 1021 + } 1022 + }, 1023 + "node_modules/@noble/hashes": { 1024 + "version": "1.8.0", 1025 + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", 1026 + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", 1027 + "license": "MIT", 1028 + "engines": { 1029 + "node": "^14.21.3 || >=16" 1030 + }, 1031 + "funding": { 1032 + "url": "https://paulmillr.com/funding/" 1033 + } 1034 + }, 1035 + "node_modules/@ts-morph/common": { 1036 + "version": "0.28.1", 1037 + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.28.1.tgz", 1038 + "integrity": "sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==", 1039 + "license": "MIT", 1040 + "dependencies": { 1041 + "minimatch": "^10.0.1", 1042 + "path-browserify": "^1.0.1", 1043 + "tinyglobby": "^0.2.14" 1044 + } 1045 + }, 1046 + "node_modules/@ts-morph/common/node_modules/balanced-match": { 1047 + "version": "4.0.4", 1048 + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", 1049 + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", 1050 + "license": "MIT", 1051 + "engines": { 1052 + "node": "18 || 20 || >=22" 1053 + } 1054 + }, 1055 + "node_modules/@ts-morph/common/node_modules/brace-expansion": { 1056 + "version": "5.0.3", 1057 + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", 1058 + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", 1059 + "license": "MIT", 1060 + "dependencies": { 1061 + "balanced-match": "^4.0.2" 1062 + }, 1063 + "engines": { 1064 + "node": "18 || 20 || >=22" 1065 + } 1066 + }, 1067 + "node_modules/@ts-morph/common/node_modules/minimatch": { 1068 + "version": "10.2.3", 1069 + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz", 1070 + "integrity": "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==", 1071 + "license": "BlueOak-1.0.0", 1072 + "dependencies": { 1073 + "brace-expansion": "^5.0.2" 1074 + }, 1075 + "engines": { 1076 + "node": "18 || 20 || >=22" 1077 + }, 1078 + "funding": { 1079 + "url": "https://github.com/sponsors/isaacs" 1080 + } 1081 + }, 1082 + "node_modules/@types/estree": { 1083 + "version": "1.0.8", 1084 + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", 1085 + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", 1086 + "dev": true, 1087 + "license": "MIT" 1088 + }, 1089 + "node_modules/@types/json-schema": { 1090 + "version": "7.0.15", 1091 + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", 1092 + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", 1093 + "dev": true, 1094 + "license": "MIT" 1095 + }, 1096 + "node_modules/@types/node": { 1097 + "version": "25.3.0", 1098 + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.0.tgz", 1099 + "integrity": "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==", 1100 + "dev": true, 1101 + "license": "MIT", 1102 + "dependencies": { 1103 + "undici-types": "~7.18.0" 1104 + } 1105 + }, 1106 + "node_modules/@types/ws": { 1107 + "version": "8.18.1", 1108 + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", 1109 + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", 1110 + "dev": true, 1111 + "license": "MIT", 1112 + "dependencies": { 1113 + "@types/node": "*" 1114 + } 1115 + }, 1116 + "node_modules/@typescript-eslint/eslint-plugin": { 1117 + "version": "8.56.1", 1118 + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz", 1119 + "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==", 1120 + "dev": true, 1121 + "license": "MIT", 1122 + "dependencies": { 1123 + "@eslint-community/regexpp": "^4.12.2", 1124 + "@typescript-eslint/scope-manager": "8.56.1", 1125 + "@typescript-eslint/type-utils": "8.56.1", 1126 + "@typescript-eslint/utils": "8.56.1", 1127 + "@typescript-eslint/visitor-keys": "8.56.1", 1128 + "ignore": "^7.0.5", 1129 + "natural-compare": "^1.4.0", 1130 + "ts-api-utils": "^2.4.0" 1131 + }, 1132 + "engines": { 1133 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1134 + }, 1135 + "funding": { 1136 + "type": "opencollective", 1137 + "url": "https://opencollective.com/typescript-eslint" 1138 + }, 1139 + "peerDependencies": { 1140 + "@typescript-eslint/parser": "^8.56.1", 1141 + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", 1142 + "typescript": ">=4.8.4 <6.0.0" 1143 + } 1144 + }, 1145 + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { 1146 + "version": "7.0.5", 1147 + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", 1148 + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", 1149 + "dev": true, 1150 + "license": "MIT", 1151 + "engines": { 1152 + "node": ">= 4" 1153 + } 1154 + }, 1155 + "node_modules/@typescript-eslint/parser": { 1156 + "version": "8.56.1", 1157 + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", 1158 + "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", 1159 + "dev": true, 1160 + "license": "MIT", 1161 + "dependencies": { 1162 + "@typescript-eslint/scope-manager": "8.56.1", 1163 + "@typescript-eslint/types": "8.56.1", 1164 + "@typescript-eslint/typescript-estree": "8.56.1", 1165 + "@typescript-eslint/visitor-keys": "8.56.1", 1166 + "debug": "^4.4.3" 1167 + }, 1168 + "engines": { 1169 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1170 + }, 1171 + "funding": { 1172 + "type": "opencollective", 1173 + "url": "https://opencollective.com/typescript-eslint" 1174 + }, 1175 + "peerDependencies": { 1176 + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", 1177 + "typescript": ">=4.8.4 <6.0.0" 1178 + } 1179 + }, 1180 + "node_modules/@typescript-eslint/project-service": { 1181 + "version": "8.56.1", 1182 + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", 1183 + "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", 1184 + "dev": true, 1185 + "license": "MIT", 1186 + "dependencies": { 1187 + "@typescript-eslint/tsconfig-utils": "^8.56.1", 1188 + "@typescript-eslint/types": "^8.56.1", 1189 + "debug": "^4.4.3" 1190 + }, 1191 + "engines": { 1192 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1193 + }, 1194 + "funding": { 1195 + "type": "opencollective", 1196 + "url": "https://opencollective.com/typescript-eslint" 1197 + }, 1198 + "peerDependencies": { 1199 + "typescript": ">=4.8.4 <6.0.0" 1200 + } 1201 + }, 1202 + "node_modules/@typescript-eslint/scope-manager": { 1203 + "version": "8.56.1", 1204 + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", 1205 + "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", 1206 + "dev": true, 1207 + "license": "MIT", 1208 + "dependencies": { 1209 + "@typescript-eslint/types": "8.56.1", 1210 + "@typescript-eslint/visitor-keys": "8.56.1" 1211 + }, 1212 + "engines": { 1213 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1214 + }, 1215 + "funding": { 1216 + "type": "opencollective", 1217 + "url": "https://opencollective.com/typescript-eslint" 1218 + } 1219 + }, 1220 + "node_modules/@typescript-eslint/tsconfig-utils": { 1221 + "version": "8.56.1", 1222 + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", 1223 + "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", 1224 + "dev": true, 1225 + "license": "MIT", 1226 + "engines": { 1227 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1228 + }, 1229 + "funding": { 1230 + "type": "opencollective", 1231 + "url": "https://opencollective.com/typescript-eslint" 1232 + }, 1233 + "peerDependencies": { 1234 + "typescript": ">=4.8.4 <6.0.0" 1235 + } 1236 + }, 1237 + "node_modules/@typescript-eslint/type-utils": { 1238 + "version": "8.56.1", 1239 + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz", 1240 + "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==", 1241 + "dev": true, 1242 + "license": "MIT", 1243 + "dependencies": { 1244 + "@typescript-eslint/types": "8.56.1", 1245 + "@typescript-eslint/typescript-estree": "8.56.1", 1246 + "@typescript-eslint/utils": "8.56.1", 1247 + "debug": "^4.4.3", 1248 + "ts-api-utils": "^2.4.0" 1249 + }, 1250 + "engines": { 1251 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1252 + }, 1253 + "funding": { 1254 + "type": "opencollective", 1255 + "url": "https://opencollective.com/typescript-eslint" 1256 + }, 1257 + "peerDependencies": { 1258 + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", 1259 + "typescript": ">=4.8.4 <6.0.0" 1260 + } 1261 + }, 1262 + "node_modules/@typescript-eslint/types": { 1263 + "version": "8.56.1", 1264 + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", 1265 + "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", 1266 + "dev": true, 1267 + "license": "MIT", 1268 + "engines": { 1269 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1270 + }, 1271 + "funding": { 1272 + "type": "opencollective", 1273 + "url": "https://opencollective.com/typescript-eslint" 1274 + } 1275 + }, 1276 + "node_modules/@typescript-eslint/typescript-estree": { 1277 + "version": "8.56.1", 1278 + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", 1279 + "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", 1280 + "dev": true, 1281 + "license": "MIT", 1282 + "dependencies": { 1283 + "@typescript-eslint/project-service": "8.56.1", 1284 + "@typescript-eslint/tsconfig-utils": "8.56.1", 1285 + "@typescript-eslint/types": "8.56.1", 1286 + "@typescript-eslint/visitor-keys": "8.56.1", 1287 + "debug": "^4.4.3", 1288 + "minimatch": "^10.2.2", 1289 + "semver": "^7.7.3", 1290 + "tinyglobby": "^0.2.15", 1291 + "ts-api-utils": "^2.4.0" 1292 + }, 1293 + "engines": { 1294 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1295 + }, 1296 + "funding": { 1297 + "type": "opencollective", 1298 + "url": "https://opencollective.com/typescript-eslint" 1299 + }, 1300 + "peerDependencies": { 1301 + "typescript": ">=4.8.4 <6.0.0" 1302 + } 1303 + }, 1304 + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { 1305 + "version": "4.0.4", 1306 + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", 1307 + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", 1308 + "dev": true, 1309 + "license": "MIT", 1310 + "engines": { 1311 + "node": "18 || 20 || >=22" 1312 + } 1313 + }, 1314 + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { 1315 + "version": "5.0.3", 1316 + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", 1317 + "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", 1318 + "dev": true, 1319 + "license": "MIT", 1320 + "dependencies": { 1321 + "balanced-match": "^4.0.2" 1322 + }, 1323 + "engines": { 1324 + "node": "18 || 20 || >=22" 1325 + } 1326 + }, 1327 + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { 1328 + "version": "10.2.3", 1329 + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.3.tgz", 1330 + "integrity": "sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==", 1331 + "dev": true, 1332 + "license": "BlueOak-1.0.0", 1333 + "dependencies": { 1334 + "brace-expansion": "^5.0.2" 1335 + }, 1336 + "engines": { 1337 + "node": "18 || 20 || >=22" 1338 + }, 1339 + "funding": { 1340 + "url": "https://github.com/sponsors/isaacs" 1341 + } 1342 + }, 1343 + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { 1344 + "version": "7.7.4", 1345 + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", 1346 + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", 1347 + "dev": true, 1348 + "license": "ISC", 1349 + "bin": { 1350 + "semver": "bin/semver.js" 1351 + }, 1352 + "engines": { 1353 + "node": ">=10" 1354 + } 1355 + }, 1356 + "node_modules/@typescript-eslint/utils": { 1357 + "version": "8.56.1", 1358 + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", 1359 + "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", 1360 + "dev": true, 1361 + "license": "MIT", 1362 + "dependencies": { 1363 + "@eslint-community/eslint-utils": "^4.9.1", 1364 + "@typescript-eslint/scope-manager": "8.56.1", 1365 + "@typescript-eslint/types": "8.56.1", 1366 + "@typescript-eslint/typescript-estree": "8.56.1" 1367 + }, 1368 + "engines": { 1369 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1370 + }, 1371 + "funding": { 1372 + "type": "opencollective", 1373 + "url": "https://opencollective.com/typescript-eslint" 1374 + }, 1375 + "peerDependencies": { 1376 + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", 1377 + "typescript": ">=4.8.4 <6.0.0" 1378 + } 1379 + }, 1380 + "node_modules/@typescript-eslint/visitor-keys": { 1381 + "version": "8.56.1", 1382 + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", 1383 + "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", 1384 + "dev": true, 1385 + "license": "MIT", 1386 + "dependencies": { 1387 + "@typescript-eslint/types": "8.56.1", 1388 + "eslint-visitor-keys": "^5.0.0" 1389 + }, 1390 + "engines": { 1391 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1392 + }, 1393 + "funding": { 1394 + "type": "opencollective", 1395 + "url": "https://opencollective.com/typescript-eslint" 1396 + } 1397 + }, 1398 + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { 1399 + "version": "5.0.1", 1400 + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", 1401 + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", 1402 + "dev": true, 1403 + "license": "Apache-2.0", 1404 + "engines": { 1405 + "node": "^20.19.0 || ^22.13.0 || >=24" 1406 + }, 1407 + "funding": { 1408 + "url": "https://opencollective.com/eslint" 1409 + } 1410 + }, 1411 + "node_modules/abort-controller": { 1412 + "version": "3.0.0", 1413 + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 1414 + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 1415 + "license": "MIT", 1416 + "dependencies": { 1417 + "event-target-shim": "^5.0.0" 1418 + }, 1419 + "engines": { 1420 + "node": ">=6.5" 1421 + } 1422 + }, 1423 + "node_modules/acorn": { 1424 + "version": "8.16.0", 1425 + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", 1426 + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", 1427 + "dev": true, 1428 + "license": "MIT", 1429 + "bin": { 1430 + "acorn": "bin/acorn" 1431 + }, 1432 + "engines": { 1433 + "node": ">=0.4.0" 1434 + } 1435 + }, 1436 + "node_modules/acorn-jsx": { 1437 + "version": "5.3.2", 1438 + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 1439 + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 1440 + "dev": true, 1441 + "license": "MIT", 1442 + "peerDependencies": { 1443 + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 1444 + } 1445 + }, 1446 + "node_modules/ajv": { 1447 + "version": "6.14.0", 1448 + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", 1449 + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", 1450 + "dev": true, 1451 + "license": "MIT", 1452 + "dependencies": { 1453 + "fast-deep-equal": "^3.1.1", 1454 + "fast-json-stable-stringify": "^2.0.0", 1455 + "json-schema-traverse": "^0.4.1", 1456 + "uri-js": "^4.2.2" 1457 + }, 1458 + "funding": { 1459 + "type": "github", 1460 + "url": "https://github.com/sponsors/epoberezkin" 1461 + } 1462 + }, 1463 + "node_modules/ansi-escapes": { 1464 + "version": "7.3.0", 1465 + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", 1466 + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", 1467 + "dev": true, 1468 + "license": "MIT", 1469 + "dependencies": { 1470 + "environment": "^1.0.0" 1471 + }, 1472 + "engines": { 1473 + "node": ">=18" 1474 + }, 1475 + "funding": { 1476 + "url": "https://github.com/sponsors/sindresorhus" 1477 + } 1478 + }, 1479 + "node_modules/ansi-regex": { 1480 + "version": "6.2.2", 1481 + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", 1482 + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", 1483 + "dev": true, 1484 + "license": "MIT", 1485 + "engines": { 1486 + "node": ">=12" 1487 + }, 1488 + "funding": { 1489 + "url": "https://github.com/chalk/ansi-regex?sponsor=1" 1490 + } 1491 + }, 1492 + "node_modules/ansi-styles": { 1493 + "version": "4.3.0", 1494 + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1495 + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1496 + "license": "MIT", 1497 + "dependencies": { 1498 + "color-convert": "^2.0.1" 1499 + }, 1500 + "engines": { 1501 + "node": ">=8" 1502 + }, 1503 + "funding": { 1504 + "url": "https://github.com/chalk/ansi-styles?sponsor=1" 1505 + } 1506 + }, 1507 + "node_modules/argparse": { 1508 + "version": "2.0.1", 1509 + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 1510 + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 1511 + "dev": true, 1512 + "license": "Python-2.0" 1513 + }, 1514 + "node_modules/atomic-sleep": { 1515 + "version": "1.0.0", 1516 + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", 1517 + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", 1518 + "license": "MIT", 1519 + "engines": { 1520 + "node": ">=8.0.0" 1521 + } 1522 + }, 1523 + "node_modules/balanced-match": { 1524 + "version": "1.0.2", 1525 + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1526 + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 1527 + "dev": true, 1528 + "license": "MIT" 1529 + }, 1530 + "node_modules/base64-js": { 1531 + "version": "1.5.1", 1532 + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 1533 + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 1534 + "funding": [ 1535 + { 1536 + "type": "github", 1537 + "url": "https://github.com/sponsors/feross" 1538 + }, 1539 + { 1540 + "type": "patreon", 1541 + "url": "https://www.patreon.com/feross" 1542 + }, 1543 + { 1544 + "type": "consulting", 1545 + "url": "https://feross.org/support" 1546 + } 1547 + ], 1548 + "license": "MIT" 1549 + }, 1550 + "node_modules/brace-expansion": { 1551 + "version": "1.1.12", 1552 + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", 1553 + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", 1554 + "dev": true, 1555 + "license": "MIT", 1556 + "dependencies": { 1557 + "balanced-match": "^1.0.0", 1558 + "concat-map": "0.0.1" 1559 + } 1560 + }, 1561 + "node_modules/braces": { 1562 + "version": "3.0.3", 1563 + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 1564 + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 1565 + "dev": true, 1566 + "license": "MIT", 1567 + "dependencies": { 1568 + "fill-range": "^7.1.1" 1569 + }, 1570 + "engines": { 1571 + "node": ">=8" 1572 + } 1573 + }, 1574 + "node_modules/buffer": { 1575 + "version": "6.0.3", 1576 + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", 1577 + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", 1578 + "funding": [ 1579 + { 1580 + "type": "github", 1581 + "url": "https://github.com/sponsors/feross" 1582 + }, 1583 + { 1584 + "type": "patreon", 1585 + "url": "https://www.patreon.com/feross" 1586 + }, 1587 + { 1588 + "type": "consulting", 1589 + "url": "https://feross.org/support" 1590 + } 1591 + ], 1592 + "license": "MIT", 1593 + "dependencies": { 1594 + "base64-js": "^1.3.1", 1595 + "ieee754": "^1.2.1" 1596 + } 1597 + }, 1598 + "node_modules/callsites": { 1599 + "version": "3.1.0", 1600 + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 1601 + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 1602 + "dev": true, 1603 + "license": "MIT", 1604 + "engines": { 1605 + "node": ">=6" 1606 + } 1607 + }, 1608 + "node_modules/cborg": { 1609 + "version": "1.10.2", 1610 + "resolved": "https://registry.npmjs.org/cborg/-/cborg-1.10.2.tgz", 1611 + "integrity": "sha512-b3tFPA9pUr2zCUiCfRd2+wok2/LBSNUMKOuRRok+WlvvAgEt/PlbgPTsZUcwCOs53IJvLgTp0eotwtosE6njug==", 1612 + "license": "Apache-2.0", 1613 + "bin": { 1614 + "cborg": "cli.js" 1615 + } 1616 + }, 1617 + "node_modules/chalk": { 1618 + "version": "4.1.2", 1619 + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 1620 + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 1621 + "dev": true, 1622 + "license": "MIT", 1623 + "dependencies": { 1624 + "ansi-styles": "^4.1.0", 1625 + "supports-color": "^7.1.0" 1626 + }, 1627 + "engines": { 1628 + "node": ">=10" 1629 + }, 1630 + "funding": { 1631 + "url": "https://github.com/chalk/chalk?sponsor=1" 1632 + } 1633 + }, 1634 + "node_modules/cli-cursor": { 1635 + "version": "5.0.0", 1636 + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", 1637 + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", 1638 + "dev": true, 1639 + "license": "MIT", 1640 + "dependencies": { 1641 + "restore-cursor": "^5.0.0" 1642 + }, 1643 + "engines": { 1644 + "node": ">=18" 1645 + }, 1646 + "funding": { 1647 + "url": "https://github.com/sponsors/sindresorhus" 1648 + } 1649 + }, 1650 + "node_modules/cli-truncate": { 1651 + "version": "4.0.0", 1652 + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", 1653 + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", 1654 + "dev": true, 1655 + "license": "MIT", 1656 + "dependencies": { 1657 + "slice-ansi": "^5.0.0", 1658 + "string-width": "^7.0.0" 1659 + }, 1660 + "engines": { 1661 + "node": ">=18" 1662 + }, 1663 + "funding": { 1664 + "url": "https://github.com/sponsors/sindresorhus" 1665 + } 1666 + }, 1667 + "node_modules/cliui": { 1668 + "version": "8.0.1", 1669 + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", 1670 + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", 1671 + "license": "ISC", 1672 + "dependencies": { 1673 + "string-width": "^4.2.0", 1674 + "strip-ansi": "^6.0.1", 1675 + "wrap-ansi": "^7.0.0" 1676 + }, 1677 + "engines": { 1678 + "node": ">=12" 1679 + } 1680 + }, 1681 + "node_modules/cliui/node_modules/ansi-regex": { 1682 + "version": "5.0.1", 1683 + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 1684 + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 1685 + "license": "MIT", 1686 + "engines": { 1687 + "node": ">=8" 1688 + } 1689 + }, 1690 + "node_modules/cliui/node_modules/emoji-regex": { 1691 + "version": "8.0.0", 1692 + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1693 + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1694 + "license": "MIT" 1695 + }, 1696 + "node_modules/cliui/node_modules/is-fullwidth-code-point": { 1697 + "version": "3.0.0", 1698 + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1699 + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1700 + "license": "MIT", 1701 + "engines": { 1702 + "node": ">=8" 1703 + } 1704 + }, 1705 + "node_modules/cliui/node_modules/string-width": { 1706 + "version": "4.2.3", 1707 + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1708 + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1709 + "license": "MIT", 1710 + "dependencies": { 1711 + "emoji-regex": "^8.0.0", 1712 + "is-fullwidth-code-point": "^3.0.0", 1713 + "strip-ansi": "^6.0.1" 1714 + }, 1715 + "engines": { 1716 + "node": ">=8" 1717 + } 1718 + }, 1719 + "node_modules/cliui/node_modules/strip-ansi": { 1720 + "version": "6.0.1", 1721 + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1722 + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1723 + "license": "MIT", 1724 + "dependencies": { 1725 + "ansi-regex": "^5.0.1" 1726 + }, 1727 + "engines": { 1728 + "node": ">=8" 1729 + } 1730 + }, 1731 + "node_modules/cliui/node_modules/wrap-ansi": { 1732 + "version": "7.0.0", 1733 + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1734 + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1735 + "license": "MIT", 1736 + "dependencies": { 1737 + "ansi-styles": "^4.0.0", 1738 + "string-width": "^4.1.0", 1739 + "strip-ansi": "^6.0.0" 1740 + }, 1741 + "engines": { 1742 + "node": ">=10" 1743 + }, 1744 + "funding": { 1745 + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 1746 + } 1747 + }, 1748 + "node_modules/cluster-key-slot": { 1749 + "version": "1.1.2", 1750 + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", 1751 + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", 1752 + "license": "Apache-2.0", 1753 + "engines": { 1754 + "node": ">=0.10.0" 1755 + } 1756 + }, 1757 + "node_modules/code-block-writer": { 1758 + "version": "13.0.3", 1759 + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", 1760 + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", 1761 + "license": "MIT" 1762 + }, 1763 + "node_modules/color-convert": { 1764 + "version": "2.0.1", 1765 + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1766 + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1767 + "license": "MIT", 1768 + "dependencies": { 1769 + "color-name": "~1.1.4" 1770 + }, 1771 + "engines": { 1772 + "node": ">=7.0.0" 1773 + } 1774 + }, 1775 + "node_modules/color-name": { 1776 + "version": "1.1.4", 1777 + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1778 + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1779 + "license": "MIT" 1780 + }, 1781 + "node_modules/colorette": { 1782 + "version": "2.0.20", 1783 + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", 1784 + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", 1785 + "dev": true, 1786 + "license": "MIT" 1787 + }, 1788 + "node_modules/commander": { 1789 + "version": "13.1.0", 1790 + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", 1791 + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", 1792 + "dev": true, 1793 + "license": "MIT", 1794 + "engines": { 1795 + "node": ">=18" 1796 + } 1797 + }, 1798 + "node_modules/concat-map": { 1799 + "version": "0.0.1", 1800 + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1801 + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 1802 + "dev": true, 1803 + "license": "MIT" 1804 + }, 1805 + "node_modules/core-js": { 1806 + "version": "3.48.0", 1807 + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", 1808 + "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", 1809 + "hasInstallScript": true, 1810 + "license": "MIT", 1811 + "funding": { 1812 + "type": "opencollective", 1813 + "url": "https://opencollective.com/core-js" 1814 + } 1815 + }, 1816 + "node_modules/cross-spawn": { 1817 + "version": "7.0.6", 1818 + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", 1819 + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", 1820 + "dev": true, 1821 + "license": "MIT", 1822 + "dependencies": { 1823 + "path-key": "^3.1.0", 1824 + "shebang-command": "^2.0.0", 1825 + "which": "^2.0.1" 1826 + }, 1827 + "engines": { 1828 + "node": ">= 8" 1829 + } 1830 + }, 1831 + "node_modules/debug": { 1832 + "version": "4.4.3", 1833 + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", 1834 + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", 1835 + "license": "MIT", 1836 + "dependencies": { 1837 + "ms": "^2.1.3" 1838 + }, 1839 + "engines": { 1840 + "node": ">=6.0" 1841 + }, 1842 + "peerDependenciesMeta": { 1843 + "supports-color": { 1844 + "optional": true 1845 + } 1846 + } 1847 + }, 1848 + "node_modules/deep-is": { 1849 + "version": "0.1.4", 1850 + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 1851 + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 1852 + "dev": true, 1853 + "license": "MIT" 1854 + }, 1855 + "node_modules/denque": { 1856 + "version": "2.1.0", 1857 + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", 1858 + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", 1859 + "license": "Apache-2.0", 1860 + "engines": { 1861 + "node": ">=0.10" 1862 + } 1863 + }, 1864 + "node_modules/dotenv": { 1865 + "version": "16.6.1", 1866 + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", 1867 + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", 1868 + "license": "BSD-2-Clause", 1869 + "engines": { 1870 + "node": ">=12" 1871 + }, 1872 + "funding": { 1873 + "url": "https://dotenvx.com" 1874 + } 1875 + }, 1876 + "node_modules/environment": { 1877 + "version": "1.1.0", 1878 + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", 1879 + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", 1880 + "dev": true, 1881 + "license": "MIT", 1882 + "engines": { 1883 + "node": ">=18" 1884 + }, 1885 + "funding": { 1886 + "url": "https://github.com/sponsors/sindresorhus" 1887 + } 1888 + }, 1889 + "node_modules/esbuild": { 1890 + "version": "0.27.3", 1891 + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", 1892 + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", 1893 + "dev": true, 1894 + "hasInstallScript": true, 1895 + "license": "MIT", 1896 + "bin": { 1897 + "esbuild": "bin/esbuild" 1898 + }, 1899 + "engines": { 1900 + "node": ">=18" 1901 + }, 1902 + "optionalDependencies": { 1903 + "@esbuild/aix-ppc64": "0.27.3", 1904 + "@esbuild/android-arm": "0.27.3", 1905 + "@esbuild/android-arm64": "0.27.3", 1906 + "@esbuild/android-x64": "0.27.3", 1907 + "@esbuild/darwin-arm64": "0.27.3", 1908 + "@esbuild/darwin-x64": "0.27.3", 1909 + "@esbuild/freebsd-arm64": "0.27.3", 1910 + "@esbuild/freebsd-x64": "0.27.3", 1911 + "@esbuild/linux-arm": "0.27.3", 1912 + "@esbuild/linux-arm64": "0.27.3", 1913 + "@esbuild/linux-ia32": "0.27.3", 1914 + "@esbuild/linux-loong64": "0.27.3", 1915 + "@esbuild/linux-mips64el": "0.27.3", 1916 + "@esbuild/linux-ppc64": "0.27.3", 1917 + "@esbuild/linux-riscv64": "0.27.3", 1918 + "@esbuild/linux-s390x": "0.27.3", 1919 + "@esbuild/linux-x64": "0.27.3", 1920 + "@esbuild/netbsd-arm64": "0.27.3", 1921 + "@esbuild/netbsd-x64": "0.27.3", 1922 + "@esbuild/openbsd-arm64": "0.27.3", 1923 + "@esbuild/openbsd-x64": "0.27.3", 1924 + "@esbuild/openharmony-arm64": "0.27.3", 1925 + "@esbuild/sunos-x64": "0.27.3", 1926 + "@esbuild/win32-arm64": "0.27.3", 1927 + "@esbuild/win32-ia32": "0.27.3", 1928 + "@esbuild/win32-x64": "0.27.3" 1929 + } 1930 + }, 1931 + "node_modules/escalade": { 1932 + "version": "3.2.0", 1933 + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", 1934 + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", 1935 + "license": "MIT", 1936 + "engines": { 1937 + "node": ">=6" 1938 + } 1939 + }, 1940 + "node_modules/escape-string-regexp": { 1941 + "version": "4.0.0", 1942 + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1943 + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1944 + "dev": true, 1945 + "license": "MIT", 1946 + "engines": { 1947 + "node": ">=10" 1948 + }, 1949 + "funding": { 1950 + "url": "https://github.com/sponsors/sindresorhus" 1951 + } 1952 + }, 1953 + "node_modules/eslint": { 1954 + "version": "9.39.3", 1955 + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.3.tgz", 1956 + "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", 1957 + "dev": true, 1958 + "license": "MIT", 1959 + "dependencies": { 1960 + "@eslint-community/eslint-utils": "^4.8.0", 1961 + "@eslint-community/regexpp": "^4.12.1", 1962 + "@eslint/config-array": "^0.21.1", 1963 + "@eslint/config-helpers": "^0.4.2", 1964 + "@eslint/core": "^0.17.0", 1965 + "@eslint/eslintrc": "^3.3.1", 1966 + "@eslint/js": "9.39.3", 1967 + "@eslint/plugin-kit": "^0.4.1", 1968 + "@humanfs/node": "^0.16.6", 1969 + "@humanwhocodes/module-importer": "^1.0.1", 1970 + "@humanwhocodes/retry": "^0.4.2", 1971 + "@types/estree": "^1.0.6", 1972 + "ajv": "^6.12.4", 1973 + "chalk": "^4.0.0", 1974 + "cross-spawn": "^7.0.6", 1975 + "debug": "^4.3.2", 1976 + "escape-string-regexp": "^4.0.0", 1977 + "eslint-scope": "^8.4.0", 1978 + "eslint-visitor-keys": "^4.2.1", 1979 + "espree": "^10.4.0", 1980 + "esquery": "^1.5.0", 1981 + "esutils": "^2.0.2", 1982 + "fast-deep-equal": "^3.1.3", 1983 + "file-entry-cache": "^8.0.0", 1984 + "find-up": "^5.0.0", 1985 + "glob-parent": "^6.0.2", 1986 + "ignore": "^5.2.0", 1987 + "imurmurhash": "^0.1.4", 1988 + "is-glob": "^4.0.0", 1989 + "json-stable-stringify-without-jsonify": "^1.0.1", 1990 + "lodash.merge": "^4.6.2", 1991 + "minimatch": "^3.1.2", 1992 + "natural-compare": "^1.4.0", 1993 + "optionator": "^0.9.3" 1994 + }, 1995 + "bin": { 1996 + "eslint": "bin/eslint.js" 1997 + }, 1998 + "engines": { 1999 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 2000 + }, 2001 + "funding": { 2002 + "url": "https://eslint.org/donate" 2003 + }, 2004 + "peerDependencies": { 2005 + "jiti": "*" 2006 + }, 2007 + "peerDependenciesMeta": { 2008 + "jiti": { 2009 + "optional": true 2010 + } 2011 + } 2012 + }, 2013 + "node_modules/eslint-scope": { 2014 + "version": "8.4.0", 2015 + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", 2016 + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", 2017 + "dev": true, 2018 + "license": "BSD-2-Clause", 2019 + "dependencies": { 2020 + "esrecurse": "^4.3.0", 2021 + "estraverse": "^5.2.0" 2022 + }, 2023 + "engines": { 2024 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 2025 + }, 2026 + "funding": { 2027 + "url": "https://opencollective.com/eslint" 2028 + } 2029 + }, 2030 + "node_modules/eslint-visitor-keys": { 2031 + "version": "4.2.1", 2032 + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", 2033 + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", 2034 + "dev": true, 2035 + "license": "Apache-2.0", 2036 + "engines": { 2037 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 2038 + }, 2039 + "funding": { 2040 + "url": "https://opencollective.com/eslint" 2041 + } 2042 + }, 2043 + "node_modules/espree": { 2044 + "version": "10.4.0", 2045 + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", 2046 + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", 2047 + "dev": true, 2048 + "license": "BSD-2-Clause", 2049 + "dependencies": { 2050 + "acorn": "^8.15.0", 2051 + "acorn-jsx": "^5.3.2", 2052 + "eslint-visitor-keys": "^4.2.1" 2053 + }, 2054 + "engines": { 2055 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 2056 + }, 2057 + "funding": { 2058 + "url": "https://opencollective.com/eslint" 2059 + } 2060 + }, 2061 + "node_modules/esquery": { 2062 + "version": "1.7.0", 2063 + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", 2064 + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", 2065 + "dev": true, 2066 + "license": "BSD-3-Clause", 2067 + "dependencies": { 2068 + "estraverse": "^5.1.0" 2069 + }, 2070 + "engines": { 2071 + "node": ">=0.10" 2072 + } 2073 + }, 2074 + "node_modules/esrecurse": { 2075 + "version": "4.3.0", 2076 + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 2077 + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 2078 + "dev": true, 2079 + "license": "BSD-2-Clause", 2080 + "dependencies": { 2081 + "estraverse": "^5.2.0" 2082 + }, 2083 + "engines": { 2084 + "node": ">=4.0" 2085 + } 2086 + }, 2087 + "node_modules/estraverse": { 2088 + "version": "5.3.0", 2089 + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 2090 + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 2091 + "dev": true, 2092 + "license": "BSD-2-Clause", 2093 + "engines": { 2094 + "node": ">=4.0" 2095 + } 2096 + }, 2097 + "node_modules/esutils": { 2098 + "version": "2.0.3", 2099 + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 2100 + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 2101 + "dev": true, 2102 + "license": "BSD-2-Clause", 2103 + "engines": { 2104 + "node": ">=0.10.0" 2105 + } 2106 + }, 2107 + "node_modules/event-target-shim": { 2108 + "version": "5.0.1", 2109 + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 2110 + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 2111 + "license": "MIT", 2112 + "engines": { 2113 + "node": ">=6" 2114 + } 2115 + }, 2116 + "node_modules/eventemitter3": { 2117 + "version": "5.0.4", 2118 + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", 2119 + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", 2120 + "dev": true, 2121 + "license": "MIT" 2122 + }, 2123 + "node_modules/events": { 2124 + "version": "3.3.0", 2125 + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", 2126 + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", 2127 + "license": "MIT", 2128 + "engines": { 2129 + "node": ">=0.8.x" 2130 + } 2131 + }, 2132 + "node_modules/execa": { 2133 + "version": "8.0.1", 2134 + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", 2135 + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", 2136 + "dev": true, 2137 + "license": "MIT", 2138 + "dependencies": { 2139 + "cross-spawn": "^7.0.3", 2140 + "get-stream": "^8.0.1", 2141 + "human-signals": "^5.0.0", 2142 + "is-stream": "^3.0.0", 2143 + "merge-stream": "^2.0.0", 2144 + "npm-run-path": "^5.1.0", 2145 + "onetime": "^6.0.0", 2146 + "signal-exit": "^4.1.0", 2147 + "strip-final-newline": "^3.0.0" 2148 + }, 2149 + "engines": { 2150 + "node": ">=16.17" 2151 + }, 2152 + "funding": { 2153 + "url": "https://github.com/sindresorhus/execa?sponsor=1" 2154 + } 2155 + }, 2156 + "node_modules/fast-deep-equal": { 2157 + "version": "3.1.3", 2158 + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 2159 + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 2160 + "dev": true, 2161 + "license": "MIT" 2162 + }, 2163 + "node_modules/fast-json-stable-stringify": { 2164 + "version": "2.1.0", 2165 + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 2166 + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 2167 + "dev": true, 2168 + "license": "MIT" 2169 + }, 2170 + "node_modules/fast-levenshtein": { 2171 + "version": "2.0.6", 2172 + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 2173 + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 2174 + "dev": true, 2175 + "license": "MIT" 2176 + }, 2177 + "node_modules/fast-redact": { 2178 + "version": "3.5.0", 2179 + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", 2180 + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", 2181 + "license": "MIT", 2182 + "engines": { 2183 + "node": ">=6" 2184 + } 2185 + }, 2186 + "node_modules/file-entry-cache": { 2187 + "version": "8.0.0", 2188 + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", 2189 + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", 2190 + "dev": true, 2191 + "license": "MIT", 2192 + "dependencies": { 2193 + "flat-cache": "^4.0.0" 2194 + }, 2195 + "engines": { 2196 + "node": ">=16.0.0" 2197 + } 2198 + }, 2199 + "node_modules/fill-range": { 2200 + "version": "7.1.1", 2201 + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 2202 + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 2203 + "dev": true, 2204 + "license": "MIT", 2205 + "dependencies": { 2206 + "to-regex-range": "^5.0.1" 2207 + }, 2208 + "engines": { 2209 + "node": ">=8" 2210 + } 2211 + }, 2212 + "node_modules/find-up": { 2213 + "version": "5.0.0", 2214 + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 2215 + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 2216 + "dev": true, 2217 + "license": "MIT", 2218 + "dependencies": { 2219 + "locate-path": "^6.0.0", 2220 + "path-exists": "^4.0.0" 2221 + }, 2222 + "engines": { 2223 + "node": ">=10" 2224 + }, 2225 + "funding": { 2226 + "url": "https://github.com/sponsors/sindresorhus" 2227 + } 2228 + }, 2229 + "node_modules/flat-cache": { 2230 + "version": "4.0.1", 2231 + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", 2232 + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", 2233 + "dev": true, 2234 + "license": "MIT", 2235 + "dependencies": { 2236 + "flatted": "^3.2.9", 2237 + "keyv": "^4.5.4" 2238 + }, 2239 + "engines": { 2240 + "node": ">=16" 2241 + } 2242 + }, 2243 + "node_modules/flatted": { 2244 + "version": "3.3.3", 2245 + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", 2246 + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", 2247 + "dev": true, 2248 + "license": "ISC" 2249 + }, 2250 + "node_modules/fsevents": { 2251 + "version": "2.3.3", 2252 + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 2253 + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 2254 + "dev": true, 2255 + "hasInstallScript": true, 2256 + "license": "MIT", 2257 + "optional": true, 2258 + "os": [ 2259 + "darwin" 2260 + ], 2261 + "engines": { 2262 + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 2263 + } 2264 + }, 2265 + "node_modules/get-caller-file": { 2266 + "version": "2.0.5", 2267 + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 2268 + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 2269 + "license": "ISC", 2270 + "engines": { 2271 + "node": "6.* || 8.* || >= 10.*" 2272 + } 2273 + }, 2274 + "node_modules/get-east-asian-width": { 2275 + "version": "1.5.0", 2276 + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", 2277 + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", 2278 + "dev": true, 2279 + "license": "MIT", 2280 + "engines": { 2281 + "node": ">=18" 2282 + }, 2283 + "funding": { 2284 + "url": "https://github.com/sponsors/sindresorhus" 2285 + } 2286 + }, 2287 + "node_modules/get-stream": { 2288 + "version": "8.0.1", 2289 + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", 2290 + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", 2291 + "dev": true, 2292 + "license": "MIT", 2293 + "engines": { 2294 + "node": ">=16" 2295 + }, 2296 + "funding": { 2297 + "url": "https://github.com/sponsors/sindresorhus" 2298 + } 2299 + }, 2300 + "node_modules/get-tsconfig": { 2301 + "version": "4.13.6", 2302 + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", 2303 + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", 2304 + "dev": true, 2305 + "license": "MIT", 2306 + "dependencies": { 2307 + "resolve-pkg-maps": "^1.0.0" 2308 + }, 2309 + "funding": { 2310 + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" 2311 + } 2312 + }, 2313 + "node_modules/glob-parent": { 2314 + "version": "6.0.2", 2315 + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 2316 + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 2317 + "dev": true, 2318 + "license": "ISC", 2319 + "dependencies": { 2320 + "is-glob": "^4.0.3" 2321 + }, 2322 + "engines": { 2323 + "node": ">=10.13.0" 2324 + } 2325 + }, 2326 + "node_modules/globals": { 2327 + "version": "14.0.0", 2328 + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", 2329 + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", 2330 + "dev": true, 2331 + "license": "MIT", 2332 + "engines": { 2333 + "node": ">=18" 2334 + }, 2335 + "funding": { 2336 + "url": "https://github.com/sponsors/sindresorhus" 2337 + } 2338 + }, 2339 + "node_modules/has-flag": { 2340 + "version": "4.0.0", 2341 + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 2342 + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 2343 + "dev": true, 2344 + "license": "MIT", 2345 + "engines": { 2346 + "node": ">=8" 2347 + } 2348 + }, 2349 + "node_modules/hono": { 2350 + "version": "4.12.2", 2351 + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.2.tgz", 2352 + "integrity": "sha512-gJnaDHXKDayjt8ue0n8Gs0A007yKXj4Xzb8+cNjZeYsSzzwKc0Lr+OZgYwVfB0pHfUs17EPoLvrOsEaJ9mj+Tg==", 2353 + "license": "MIT", 2354 + "engines": { 2355 + "node": ">=16.9.0" 2356 + } 2357 + }, 2358 + "node_modules/human-signals": { 2359 + "version": "5.0.0", 2360 + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", 2361 + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", 2362 + "dev": true, 2363 + "license": "Apache-2.0", 2364 + "engines": { 2365 + "node": ">=16.17.0" 2366 + } 2367 + }, 2368 + "node_modules/husky": { 2369 + "version": "9.1.7", 2370 + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", 2371 + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", 2372 + "dev": true, 2373 + "license": "MIT", 2374 + "bin": { 2375 + "husky": "bin.js" 2376 + }, 2377 + "engines": { 2378 + "node": ">=18" 2379 + }, 2380 + "funding": { 2381 + "url": "https://github.com/sponsors/typicode" 2382 + } 2383 + }, 2384 + "node_modules/ieee754": { 2385 + "version": "1.2.1", 2386 + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 2387 + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 2388 + "funding": [ 2389 + { 2390 + "type": "github", 2391 + "url": "https://github.com/sponsors/feross" 2392 + }, 2393 + { 2394 + "type": "patreon", 2395 + "url": "https://www.patreon.com/feross" 2396 + }, 2397 + { 2398 + "type": "consulting", 2399 + "url": "https://feross.org/support" 2400 + } 2401 + ], 2402 + "license": "BSD-3-Clause" 2403 + }, 2404 + "node_modules/ignore": { 2405 + "version": "5.3.2", 2406 + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", 2407 + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", 2408 + "dev": true, 2409 + "license": "MIT", 2410 + "engines": { 2411 + "node": ">= 4" 2412 + } 2413 + }, 2414 + "node_modules/import-fresh": { 2415 + "version": "3.3.1", 2416 + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", 2417 + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", 2418 + "dev": true, 2419 + "license": "MIT", 2420 + "dependencies": { 2421 + "parent-module": "^1.0.0", 2422 + "resolve-from": "^4.0.0" 2423 + }, 2424 + "engines": { 2425 + "node": ">=6" 2426 + }, 2427 + "funding": { 2428 + "url": "https://github.com/sponsors/sindresorhus" 2429 + } 2430 + }, 2431 + "node_modules/imurmurhash": { 2432 + "version": "0.1.4", 2433 + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 2434 + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 2435 + "dev": true, 2436 + "license": "MIT", 2437 + "engines": { 2438 + "node": ">=0.8.19" 2439 + } 2440 + }, 2441 + "node_modules/ioredis": { 2442 + "version": "5.9.3", 2443 + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.9.3.tgz", 2444 + "integrity": "sha512-VI5tMCdeoxZWU5vjHWsiE/Su76JGhBvWF1MJnV9ZtGltHk9BmD48oDq8Tj8haZ85aceXZMxLNDQZRVo5QKNgXA==", 2445 + "license": "MIT", 2446 + "dependencies": { 2447 + "@ioredis/commands": "1.5.0", 2448 + "cluster-key-slot": "^1.1.0", 2449 + "debug": "^4.3.4", 2450 + "denque": "^2.1.0", 2451 + "lodash.defaults": "^4.2.0", 2452 + "lodash.isarguments": "^3.1.0", 2453 + "redis-errors": "^1.2.0", 2454 + "redis-parser": "^3.0.0", 2455 + "standard-as-callback": "^2.1.0" 2456 + }, 2457 + "engines": { 2458 + "node": ">=12.22.0" 2459 + }, 2460 + "funding": { 2461 + "type": "opencollective", 2462 + "url": "https://opencollective.com/ioredis" 2463 + } 2464 + }, 2465 + "node_modules/is-extglob": { 2466 + "version": "2.1.1", 2467 + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 2468 + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 2469 + "dev": true, 2470 + "license": "MIT", 2471 + "engines": { 2472 + "node": ">=0.10.0" 2473 + } 2474 + }, 2475 + "node_modules/is-fullwidth-code-point": { 2476 + "version": "4.0.0", 2477 + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", 2478 + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", 2479 + "dev": true, 2480 + "license": "MIT", 2481 + "engines": { 2482 + "node": ">=12" 2483 + }, 2484 + "funding": { 2485 + "url": "https://github.com/sponsors/sindresorhus" 2486 + } 2487 + }, 2488 + "node_modules/is-glob": { 2489 + "version": "4.0.3", 2490 + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 2491 + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 2492 + "dev": true, 2493 + "license": "MIT", 2494 + "dependencies": { 2495 + "is-extglob": "^2.1.1" 2496 + }, 2497 + "engines": { 2498 + "node": ">=0.10.0" 2499 + } 2500 + }, 2501 + "node_modules/is-number": { 2502 + "version": "7.0.0", 2503 + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 2504 + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 2505 + "dev": true, 2506 + "license": "MIT", 2507 + "engines": { 2508 + "node": ">=0.12.0" 2509 + } 2510 + }, 2511 + "node_modules/is-stream": { 2512 + "version": "3.0.0", 2513 + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", 2514 + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", 2515 + "dev": true, 2516 + "license": "MIT", 2517 + "engines": { 2518 + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 2519 + }, 2520 + "funding": { 2521 + "url": "https://github.com/sponsors/sindresorhus" 2522 + } 2523 + }, 2524 + "node_modules/isexe": { 2525 + "version": "2.0.0", 2526 + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 2527 + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 2528 + "dev": true, 2529 + "license": "ISC" 2530 + }, 2531 + "node_modules/iso-datestring-validator": { 2532 + "version": "2.2.2", 2533 + "resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz", 2534 + "integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==", 2535 + "license": "MIT" 2536 + }, 2537 + "node_modules/js-yaml": { 2538 + "version": "4.1.1", 2539 + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", 2540 + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", 2541 + "dev": true, 2542 + "license": "MIT", 2543 + "dependencies": { 2544 + "argparse": "^2.0.1" 2545 + }, 2546 + "bin": { 2547 + "js-yaml": "bin/js-yaml.js" 2548 + } 2549 + }, 2550 + "node_modules/json-buffer": { 2551 + "version": "3.0.1", 2552 + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 2553 + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 2554 + "dev": true, 2555 + "license": "MIT" 2556 + }, 2557 + "node_modules/json-schema-traverse": { 2558 + "version": "0.4.1", 2559 + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 2560 + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 2561 + "dev": true, 2562 + "license": "MIT" 2563 + }, 2564 + "node_modules/json-stable-stringify-without-jsonify": { 2565 + "version": "1.0.1", 2566 + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 2567 + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", 2568 + "dev": true, 2569 + "license": "MIT" 2570 + }, 2571 + "node_modules/keyv": { 2572 + "version": "4.5.4", 2573 + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", 2574 + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 2575 + "dev": true, 2576 + "license": "MIT", 2577 + "dependencies": { 2578 + "json-buffer": "3.0.1" 2579 + } 2580 + }, 2581 + "node_modules/levn": { 2582 + "version": "0.4.1", 2583 + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 2584 + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 2585 + "dev": true, 2586 + "license": "MIT", 2587 + "dependencies": { 2588 + "prelude-ls": "^1.2.1", 2589 + "type-check": "~0.4.0" 2590 + }, 2591 + "engines": { 2592 + "node": ">= 0.8.0" 2593 + } 2594 + }, 2595 + "node_modules/lilconfig": { 2596 + "version": "3.1.3", 2597 + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", 2598 + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", 2599 + "dev": true, 2600 + "license": "MIT", 2601 + "engines": { 2602 + "node": ">=14" 2603 + }, 2604 + "funding": { 2605 + "url": "https://github.com/sponsors/antonk52" 2606 + } 2607 + }, 2608 + "node_modules/lint-staged": { 2609 + "version": "15.5.2", 2610 + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.2.tgz", 2611 + "integrity": "sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==", 2612 + "dev": true, 2613 + "license": "MIT", 2614 + "dependencies": { 2615 + "chalk": "^5.4.1", 2616 + "commander": "^13.1.0", 2617 + "debug": "^4.4.0", 2618 + "execa": "^8.0.1", 2619 + "lilconfig": "^3.1.3", 2620 + "listr2": "^8.2.5", 2621 + "micromatch": "^4.0.8", 2622 + "pidtree": "^0.6.0", 2623 + "string-argv": "^0.3.2", 2624 + "yaml": "^2.7.0" 2625 + }, 2626 + "bin": { 2627 + "lint-staged": "bin/lint-staged.js" 2628 + }, 2629 + "engines": { 2630 + "node": ">=18.12.0" 2631 + }, 2632 + "funding": { 2633 + "url": "https://opencollective.com/lint-staged" 2634 + } 2635 + }, 2636 + "node_modules/lint-staged/node_modules/chalk": { 2637 + "version": "5.6.2", 2638 + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", 2639 + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", 2640 + "dev": true, 2641 + "license": "MIT", 2642 + "engines": { 2643 + "node": "^12.17.0 || ^14.13 || >=16.0.0" 2644 + }, 2645 + "funding": { 2646 + "url": "https://github.com/chalk/chalk?sponsor=1" 2647 + } 2648 + }, 2649 + "node_modules/listr2": { 2650 + "version": "8.3.3", 2651 + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.3.tgz", 2652 + "integrity": "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==", 2653 + "dev": true, 2654 + "license": "MIT", 2655 + "dependencies": { 2656 + "cli-truncate": "^4.0.0", 2657 + "colorette": "^2.0.20", 2658 + "eventemitter3": "^5.0.1", 2659 + "log-update": "^6.1.0", 2660 + "rfdc": "^1.4.1", 2661 + "wrap-ansi": "^9.0.0" 2662 + }, 2663 + "engines": { 2664 + "node": ">=18.0.0" 2665 + } 2666 + }, 2667 + "node_modules/locate-path": { 2668 + "version": "6.0.0", 2669 + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 2670 + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 2671 + "dev": true, 2672 + "license": "MIT", 2673 + "dependencies": { 2674 + "p-locate": "^5.0.0" 2675 + }, 2676 + "engines": { 2677 + "node": ">=10" 2678 + }, 2679 + "funding": { 2680 + "url": "https://github.com/sponsors/sindresorhus" 2681 + } 2682 + }, 2683 + "node_modules/lodash.defaults": { 2684 + "version": "4.2.0", 2685 + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", 2686 + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", 2687 + "license": "MIT" 2688 + }, 2689 + "node_modules/lodash.isarguments": { 2690 + "version": "3.1.0", 2691 + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", 2692 + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", 2693 + "license": "MIT" 2694 + }, 2695 + "node_modules/lodash.merge": { 2696 + "version": "4.6.2", 2697 + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 2698 + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 2699 + "dev": true, 2700 + "license": "MIT" 2701 + }, 2702 + "node_modules/log-update": { 2703 + "version": "6.1.0", 2704 + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", 2705 + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", 2706 + "dev": true, 2707 + "license": "MIT", 2708 + "dependencies": { 2709 + "ansi-escapes": "^7.0.0", 2710 + "cli-cursor": "^5.0.0", 2711 + "slice-ansi": "^7.1.0", 2712 + "strip-ansi": "^7.1.0", 2713 + "wrap-ansi": "^9.0.0" 2714 + }, 2715 + "engines": { 2716 + "node": ">=18" 2717 + }, 2718 + "funding": { 2719 + "url": "https://github.com/sponsors/sindresorhus" 2720 + } 2721 + }, 2722 + "node_modules/log-update/node_modules/ansi-styles": { 2723 + "version": "6.2.3", 2724 + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", 2725 + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", 2726 + "dev": true, 2727 + "license": "MIT", 2728 + "engines": { 2729 + "node": ">=12" 2730 + }, 2731 + "funding": { 2732 + "url": "https://github.com/chalk/ansi-styles?sponsor=1" 2733 + } 2734 + }, 2735 + "node_modules/log-update/node_modules/is-fullwidth-code-point": { 2736 + "version": "5.1.0", 2737 + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", 2738 + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", 2739 + "dev": true, 2740 + "license": "MIT", 2741 + "dependencies": { 2742 + "get-east-asian-width": "^1.3.1" 2743 + }, 2744 + "engines": { 2745 + "node": ">=18" 2746 + }, 2747 + "funding": { 2748 + "url": "https://github.com/sponsors/sindresorhus" 2749 + } 2750 + }, 2751 + "node_modules/log-update/node_modules/slice-ansi": { 2752 + "version": "7.1.2", 2753 + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", 2754 + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", 2755 + "dev": true, 2756 + "license": "MIT", 2757 + "dependencies": { 2758 + "ansi-styles": "^6.2.1", 2759 + "is-fullwidth-code-point": "^5.0.0" 2760 + }, 2761 + "engines": { 2762 + "node": ">=18" 2763 + }, 2764 + "funding": { 2765 + "url": "https://github.com/chalk/slice-ansi?sponsor=1" 2766 + } 2767 + }, 2768 + "node_modules/lru-cache": { 2769 + "version": "10.4.3", 2770 + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", 2771 + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", 2772 + "license": "ISC" 2773 + }, 2774 + "node_modules/merge-stream": { 2775 + "version": "2.0.0", 2776 + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", 2777 + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", 2778 + "dev": true, 2779 + "license": "MIT" 2780 + }, 2781 + "node_modules/micromatch": { 2782 + "version": "4.0.8", 2783 + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", 2784 + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", 2785 + "dev": true, 2786 + "license": "MIT", 2787 + "dependencies": { 2788 + "braces": "^3.0.3", 2789 + "picomatch": "^2.3.1" 2790 + }, 2791 + "engines": { 2792 + "node": ">=8.6" 2793 + } 2794 + }, 2795 + "node_modules/mimic-fn": { 2796 + "version": "4.0.0", 2797 + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", 2798 + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", 2799 + "dev": true, 2800 + "license": "MIT", 2801 + "engines": { 2802 + "node": ">=12" 2803 + }, 2804 + "funding": { 2805 + "url": "https://github.com/sponsors/sindresorhus" 2806 + } 2807 + }, 2808 + "node_modules/mimic-function": { 2809 + "version": "5.0.1", 2810 + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", 2811 + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", 2812 + "dev": true, 2813 + "license": "MIT", 2814 + "engines": { 2815 + "node": ">=18" 2816 + }, 2817 + "funding": { 2818 + "url": "https://github.com/sponsors/sindresorhus" 2819 + } 2820 + }, 2821 + "node_modules/minimatch": { 2822 + "version": "3.1.4", 2823 + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.4.tgz", 2824 + "integrity": "sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==", 2825 + "dev": true, 2826 + "license": "ISC", 2827 + "dependencies": { 2828 + "brace-expansion": "^1.1.7" 2829 + }, 2830 + "engines": { 2831 + "node": "*" 2832 + } 2833 + }, 2834 + "node_modules/ms": { 2835 + "version": "2.1.3", 2836 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 2837 + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 2838 + "license": "MIT" 2839 + }, 2840 + "node_modules/multiformats": { 2841 + "version": "9.9.0", 2842 + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 2843 + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 2844 + "license": "(Apache-2.0 AND MIT)" 2845 + }, 2846 + "node_modules/natural-compare": { 2847 + "version": "1.4.0", 2848 + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 2849 + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", 2850 + "dev": true, 2851 + "license": "MIT" 2852 + }, 2853 + "node_modules/npm-run-path": { 2854 + "version": "5.3.0", 2855 + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", 2856 + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", 2857 + "dev": true, 2858 + "license": "MIT", 2859 + "dependencies": { 2860 + "path-key": "^4.0.0" 2861 + }, 2862 + "engines": { 2863 + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 2864 + }, 2865 + "funding": { 2866 + "url": "https://github.com/sponsors/sindresorhus" 2867 + } 2868 + }, 2869 + "node_modules/npm-run-path/node_modules/path-key": { 2870 + "version": "4.0.0", 2871 + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", 2872 + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", 2873 + "dev": true, 2874 + "license": "MIT", 2875 + "engines": { 2876 + "node": ">=12" 2877 + }, 2878 + "funding": { 2879 + "url": "https://github.com/sponsors/sindresorhus" 2880 + } 2881 + }, 2882 + "node_modules/on-exit-leak-free": { 2883 + "version": "2.1.2", 2884 + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", 2885 + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", 2886 + "license": "MIT", 2887 + "engines": { 2888 + "node": ">=14.0.0" 2889 + } 2890 + }, 2891 + "node_modules/onetime": { 2892 + "version": "6.0.0", 2893 + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", 2894 + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", 2895 + "dev": true, 2896 + "license": "MIT", 2897 + "dependencies": { 2898 + "mimic-fn": "^4.0.0" 2899 + }, 2900 + "engines": { 2901 + "node": ">=12" 2902 + }, 2903 + "funding": { 2904 + "url": "https://github.com/sponsors/sindresorhus" 2905 + } 2906 + }, 2907 + "node_modules/optionator": { 2908 + "version": "0.9.4", 2909 + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", 2910 + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", 2911 + "dev": true, 2912 + "license": "MIT", 2913 + "dependencies": { 2914 + "deep-is": "^0.1.3", 2915 + "fast-levenshtein": "^2.0.6", 2916 + "levn": "^0.4.1", 2917 + "prelude-ls": "^1.2.1", 2918 + "type-check": "^0.4.0", 2919 + "word-wrap": "^1.2.5" 2920 + }, 2921 + "engines": { 2922 + "node": ">= 0.8.0" 2923 + } 2924 + }, 2925 + "node_modules/p-limit": { 2926 + "version": "3.1.0", 2927 + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 2928 + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 2929 + "dev": true, 2930 + "license": "MIT", 2931 + "dependencies": { 2932 + "yocto-queue": "^0.1.0" 2933 + }, 2934 + "engines": { 2935 + "node": ">=10" 2936 + }, 2937 + "funding": { 2938 + "url": "https://github.com/sponsors/sindresorhus" 2939 + } 2940 + }, 2941 + "node_modules/p-locate": { 2942 + "version": "5.0.0", 2943 + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 2944 + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 2945 + "dev": true, 2946 + "license": "MIT", 2947 + "dependencies": { 2948 + "p-limit": "^3.0.2" 2949 + }, 2950 + "engines": { 2951 + "node": ">=10" 2952 + }, 2953 + "funding": { 2954 + "url": "https://github.com/sponsors/sindresorhus" 2955 + } 2956 + }, 2957 + "node_modules/parent-module": { 2958 + "version": "1.0.1", 2959 + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 2960 + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 2961 + "dev": true, 2962 + "license": "MIT", 2963 + "dependencies": { 2964 + "callsites": "^3.0.0" 2965 + }, 2966 + "engines": { 2967 + "node": ">=6" 2968 + } 2969 + }, 2970 + "node_modules/path-browserify": { 2971 + "version": "1.0.1", 2972 + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", 2973 + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", 2974 + "license": "MIT" 2975 + }, 2976 + "node_modules/path-exists": { 2977 + "version": "4.0.0", 2978 + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 2979 + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 2980 + "dev": true, 2981 + "license": "MIT", 2982 + "engines": { 2983 + "node": ">=8" 2984 + } 2985 + }, 2986 + "node_modules/path-key": { 2987 + "version": "3.1.1", 2988 + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 2989 + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 2990 + "dev": true, 2991 + "license": "MIT", 2992 + "engines": { 2993 + "node": ">=8" 2994 + } 2995 + }, 2996 + "node_modules/picomatch": { 2997 + "version": "2.3.1", 2998 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 2999 + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 3000 + "dev": true, 3001 + "license": "MIT", 3002 + "engines": { 3003 + "node": ">=8.6" 3004 + }, 3005 + "funding": { 3006 + "url": "https://github.com/sponsors/jonschlinkert" 3007 + } 3008 + }, 3009 + "node_modules/pidtree": { 3010 + "version": "0.6.0", 3011 + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", 3012 + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", 3013 + "dev": true, 3014 + "license": "MIT", 3015 + "bin": { 3016 + "pidtree": "bin/pidtree.js" 3017 + }, 3018 + "engines": { 3019 + "node": ">=0.10" 3020 + } 3021 + }, 3022 + "node_modules/pino": { 3023 + "version": "8.21.0", 3024 + "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz", 3025 + "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==", 3026 + "license": "MIT", 3027 + "dependencies": { 3028 + "atomic-sleep": "^1.0.0", 3029 + "fast-redact": "^3.1.1", 3030 + "on-exit-leak-free": "^2.1.0", 3031 + "pino-abstract-transport": "^1.2.0", 3032 + "pino-std-serializers": "^6.0.0", 3033 + "process-warning": "^3.0.0", 3034 + "quick-format-unescaped": "^4.0.3", 3035 + "real-require": "^0.2.0", 3036 + "safe-stable-stringify": "^2.3.1", 3037 + "sonic-boom": "^3.7.0", 3038 + "thread-stream": "^2.6.0" 3039 + }, 3040 + "bin": { 3041 + "pino": "bin.js" 3042 + } 3043 + }, 3044 + "node_modules/pino-abstract-transport": { 3045 + "version": "1.2.0", 3046 + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", 3047 + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", 3048 + "license": "MIT", 3049 + "dependencies": { 3050 + "readable-stream": "^4.0.0", 3051 + "split2": "^4.0.0" 3052 + } 3053 + }, 3054 + "node_modules/pino-std-serializers": { 3055 + "version": "6.2.2", 3056 + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", 3057 + "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==", 3058 + "license": "MIT" 3059 + }, 3060 + "node_modules/prelude-ls": { 3061 + "version": "1.2.1", 3062 + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 3063 + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 3064 + "dev": true, 3065 + "license": "MIT", 3066 + "engines": { 3067 + "node": ">= 0.8.0" 3068 + } 3069 + }, 3070 + "node_modules/prettier": { 3071 + "version": "3.8.1", 3072 + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", 3073 + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", 3074 + "license": "MIT", 3075 + "bin": { 3076 + "prettier": "bin/prettier.cjs" 3077 + }, 3078 + "engines": { 3079 + "node": ">=14" 3080 + }, 3081 + "funding": { 3082 + "url": "https://github.com/prettier/prettier?sponsor=1" 3083 + } 3084 + }, 3085 + "node_modules/process": { 3086 + "version": "0.11.10", 3087 + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", 3088 + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", 3089 + "license": "MIT", 3090 + "engines": { 3091 + "node": ">= 0.6.0" 3092 + } 3093 + }, 3094 + "node_modules/process-warning": { 3095 + "version": "3.0.0", 3096 + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", 3097 + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", 3098 + "license": "MIT" 3099 + }, 3100 + "node_modules/proto": { 3101 + "resolved": "proto", 3102 + "link": true 3103 + }, 3104 + "node_modules/punycode": { 3105 + "version": "2.3.1", 3106 + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 3107 + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 3108 + "dev": true, 3109 + "license": "MIT", 3110 + "engines": { 3111 + "node": ">=6" 3112 + } 3113 + }, 3114 + "node_modules/quick-format-unescaped": { 3115 + "version": "4.0.4", 3116 + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", 3117 + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", 3118 + "license": "MIT" 3119 + }, 3120 + "node_modules/readable-stream": { 3121 + "version": "4.7.0", 3122 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", 3123 + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", 3124 + "license": "MIT", 3125 + "dependencies": { 3126 + "abort-controller": "^3.0.0", 3127 + "buffer": "^6.0.3", 3128 + "events": "^3.3.0", 3129 + "process": "^0.11.10", 3130 + "string_decoder": "^1.3.0" 3131 + }, 3132 + "engines": { 3133 + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 3134 + } 3135 + }, 3136 + "node_modules/real-require": { 3137 + "version": "0.2.0", 3138 + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", 3139 + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", 3140 + "license": "MIT", 3141 + "engines": { 3142 + "node": ">= 12.13.0" 3143 + } 3144 + }, 3145 + "node_modules/redis-errors": { 3146 + "version": "1.2.0", 3147 + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", 3148 + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", 3149 + "license": "MIT", 3150 + "engines": { 3151 + "node": ">=4" 3152 + } 3153 + }, 3154 + "node_modules/redis-parser": { 3155 + "version": "3.0.0", 3156 + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", 3157 + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", 3158 + "license": "MIT", 3159 + "dependencies": { 3160 + "redis-errors": "^1.0.0" 3161 + }, 3162 + "engines": { 3163 + "node": ">=4" 3164 + } 3165 + }, 3166 + "node_modules/require-directory": { 3167 + "version": "2.1.1", 3168 + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 3169 + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 3170 + "license": "MIT", 3171 + "engines": { 3172 + "node": ">=0.10.0" 3173 + } 3174 + }, 3175 + "node_modules/resolve-from": { 3176 + "version": "4.0.0", 3177 + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 3178 + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 3179 + "dev": true, 3180 + "license": "MIT", 3181 + "engines": { 3182 + "node": ">=4" 3183 + } 3184 + }, 3185 + "node_modules/resolve-pkg-maps": { 3186 + "version": "1.0.0", 3187 + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", 3188 + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", 3189 + "dev": true, 3190 + "license": "MIT", 3191 + "funding": { 3192 + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" 3193 + } 3194 + }, 3195 + "node_modules/restore-cursor": { 3196 + "version": "5.1.0", 3197 + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", 3198 + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", 3199 + "dev": true, 3200 + "license": "MIT", 3201 + "dependencies": { 3202 + "onetime": "^7.0.0", 3203 + "signal-exit": "^4.1.0" 3204 + }, 3205 + "engines": { 3206 + "node": ">=18" 3207 + }, 3208 + "funding": { 3209 + "url": "https://github.com/sponsors/sindresorhus" 3210 + } 3211 + }, 3212 + "node_modules/restore-cursor/node_modules/onetime": { 3213 + "version": "7.0.0", 3214 + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", 3215 + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", 3216 + "dev": true, 3217 + "license": "MIT", 3218 + "dependencies": { 3219 + "mimic-function": "^5.0.0" 3220 + }, 3221 + "engines": { 3222 + "node": ">=18" 3223 + }, 3224 + "funding": { 3225 + "url": "https://github.com/sponsors/sindresorhus" 3226 + } 3227 + }, 3228 + "node_modules/rfdc": { 3229 + "version": "1.4.1", 3230 + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", 3231 + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", 3232 + "dev": true, 3233 + "license": "MIT" 3234 + }, 3235 + "node_modules/safe-buffer": { 3236 + "version": "5.2.1", 3237 + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 3238 + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 3239 + "funding": [ 3240 + { 3241 + "type": "github", 3242 + "url": "https://github.com/sponsors/feross" 3243 + }, 3244 + { 3245 + "type": "patreon", 3246 + "url": "https://www.patreon.com/feross" 3247 + }, 3248 + { 3249 + "type": "consulting", 3250 + "url": "https://feross.org/support" 3251 + } 3252 + ], 3253 + "license": "MIT" 3254 + }, 3255 + "node_modules/safe-stable-stringify": { 3256 + "version": "2.5.0", 3257 + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", 3258 + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", 3259 + "license": "MIT", 3260 + "engines": { 3261 + "node": ">=10" 3262 + } 3263 + }, 3264 + "node_modules/shebang-command": { 3265 + "version": "2.0.0", 3266 + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 3267 + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 3268 + "dev": true, 3269 + "license": "MIT", 3270 + "dependencies": { 3271 + "shebang-regex": "^3.0.0" 3272 + }, 3273 + "engines": { 3274 + "node": ">=8" 3275 + } 3276 + }, 3277 + "node_modules/shebang-regex": { 3278 + "version": "3.0.0", 3279 + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 3280 + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 3281 + "dev": true, 3282 + "license": "MIT", 3283 + "engines": { 3284 + "node": ">=8" 3285 + } 3286 + }, 3287 + "node_modules/signal-exit": { 3288 + "version": "4.1.0", 3289 + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", 3290 + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", 3291 + "dev": true, 3292 + "license": "ISC", 3293 + "engines": { 3294 + "node": ">=14" 3295 + }, 3296 + "funding": { 3297 + "url": "https://github.com/sponsors/isaacs" 3298 + } 3299 + }, 3300 + "node_modules/slice-ansi": { 3301 + "version": "5.0.0", 3302 + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", 3303 + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", 3304 + "dev": true, 3305 + "license": "MIT", 3306 + "dependencies": { 3307 + "ansi-styles": "^6.0.0", 3308 + "is-fullwidth-code-point": "^4.0.0" 3309 + }, 3310 + "engines": { 3311 + "node": ">=12" 3312 + }, 3313 + "funding": { 3314 + "url": "https://github.com/chalk/slice-ansi?sponsor=1" 3315 + } 3316 + }, 3317 + "node_modules/slice-ansi/node_modules/ansi-styles": { 3318 + "version": "6.2.3", 3319 + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", 3320 + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", 3321 + "dev": true, 3322 + "license": "MIT", 3323 + "engines": { 3324 + "node": ">=12" 3325 + }, 3326 + "funding": { 3327 + "url": "https://github.com/chalk/ansi-styles?sponsor=1" 3328 + } 3329 + }, 3330 + "node_modules/sonic-boom": { 3331 + "version": "3.8.1", 3332 + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", 3333 + "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==", 3334 + "license": "MIT", 3335 + "dependencies": { 3336 + "atomic-sleep": "^1.0.0" 3337 + } 3338 + }, 3339 + "node_modules/split2": { 3340 + "version": "4.2.0", 3341 + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", 3342 + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", 3343 + "license": "ISC", 3344 + "engines": { 3345 + "node": ">= 10.x" 3346 + } 3347 + }, 3348 + "node_modules/standard-as-callback": { 3349 + "version": "2.1.0", 3350 + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", 3351 + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", 3352 + "license": "MIT" 3353 + }, 3354 + "node_modules/string_decoder": { 3355 + "version": "1.3.0", 3356 + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 3357 + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 3358 + "license": "MIT", 3359 + "dependencies": { 3360 + "safe-buffer": "~5.2.0" 3361 + } 3362 + }, 3363 + "node_modules/string-argv": { 3364 + "version": "0.3.2", 3365 + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", 3366 + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", 3367 + "dev": true, 3368 + "license": "MIT", 3369 + "engines": { 3370 + "node": ">=0.6.19" 3371 + } 3372 + }, 3373 + "node_modules/string-width": { 3374 + "version": "7.2.0", 3375 + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", 3376 + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", 3377 + "dev": true, 3378 + "license": "MIT", 3379 + "dependencies": { 3380 + "emoji-regex": "^10.3.0", 3381 + "get-east-asian-width": "^1.0.0", 3382 + "strip-ansi": "^7.1.0" 3383 + }, 3384 + "engines": { 3385 + "node": ">=18" 3386 + }, 3387 + "funding": { 3388 + "url": "https://github.com/sponsors/sindresorhus" 3389 + } 3390 + }, 3391 + "node_modules/string-width/node_modules/emoji-regex": { 3392 + "version": "10.6.0", 3393 + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", 3394 + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", 3395 + "dev": true, 3396 + "license": "MIT" 3397 + }, 3398 + "node_modules/strip-ansi": { 3399 + "version": "7.1.2", 3400 + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", 3401 + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", 3402 + "dev": true, 3403 + "license": "MIT", 3404 + "dependencies": { 3405 + "ansi-regex": "^6.0.1" 3406 + }, 3407 + "engines": { 3408 + "node": ">=12" 3409 + }, 3410 + "funding": { 3411 + "url": "https://github.com/chalk/strip-ansi?sponsor=1" 3412 + } 3413 + }, 3414 + "node_modules/strip-final-newline": { 3415 + "version": "3.0.0", 3416 + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", 3417 + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", 3418 + "dev": true, 3419 + "license": "MIT", 3420 + "engines": { 3421 + "node": ">=12" 3422 + }, 3423 + "funding": { 3424 + "url": "https://github.com/sponsors/sindresorhus" 3425 + } 3426 + }, 3427 + "node_modules/strip-json-comments": { 3428 + "version": "3.1.1", 3429 + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 3430 + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 3431 + "dev": true, 3432 + "license": "MIT", 3433 + "engines": { 3434 + "node": ">=8" 3435 + }, 3436 + "funding": { 3437 + "url": "https://github.com/sponsors/sindresorhus" 3438 + } 3439 + }, 3440 + "node_modules/supports-color": { 3441 + "version": "7.2.0", 3442 + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 3443 + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 3444 + "dev": true, 3445 + "license": "MIT", 3446 + "dependencies": { 3447 + "has-flag": "^4.0.0" 3448 + }, 3449 + "engines": { 3450 + "node": ">=8" 3451 + } 3452 + }, 3453 + "node_modules/thread-stream": { 3454 + "version": "2.7.0", 3455 + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", 3456 + "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==", 3457 + "license": "MIT", 3458 + "dependencies": { 3459 + "real-require": "^0.2.0" 3460 + } 3461 + }, 3462 + "node_modules/tinyglobby": { 3463 + "version": "0.2.15", 3464 + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", 3465 + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", 3466 + "license": "MIT", 3467 + "dependencies": { 3468 + "fdir": "^6.5.0", 3469 + "picomatch": "^4.0.3" 3470 + }, 3471 + "engines": { 3472 + "node": ">=12.0.0" 3473 + }, 3474 + "funding": { 3475 + "url": "https://github.com/sponsors/SuperchupuDev" 3476 + } 3477 + }, 3478 + "node_modules/tinyglobby/node_modules/fdir": { 3479 + "version": "6.5.0", 3480 + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", 3481 + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", 3482 + "license": "MIT", 3483 + "engines": { 3484 + "node": ">=12.0.0" 3485 + }, 3486 + "peerDependencies": { 3487 + "picomatch": "^3 || ^4" 3488 + }, 3489 + "peerDependenciesMeta": { 3490 + "picomatch": { 3491 + "optional": true 3492 + } 3493 + } 3494 + }, 3495 + "node_modules/tinyglobby/node_modules/picomatch": { 3496 + "version": "4.0.3", 3497 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 3498 + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 3499 + "license": "MIT", 3500 + "engines": { 3501 + "node": ">=12" 3502 + }, 3503 + "funding": { 3504 + "url": "https://github.com/sponsors/jonschlinkert" 3505 + } 3506 + }, 3507 + "node_modules/to-regex-range": { 3508 + "version": "5.0.1", 3509 + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 3510 + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 3511 + "dev": true, 3512 + "license": "MIT", 3513 + "dependencies": { 3514 + "is-number": "^7.0.0" 3515 + }, 3516 + "engines": { 3517 + "node": ">=8.0" 3518 + } 3519 + }, 3520 + "node_modules/ts-api-utils": { 3521 + "version": "2.4.0", 3522 + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", 3523 + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", 3524 + "dev": true, 3525 + "license": "MIT", 3526 + "engines": { 3527 + "node": ">=18.12" 3528 + }, 3529 + "peerDependencies": { 3530 + "typescript": ">=4.8.4" 3531 + } 3532 + }, 3533 + "node_modules/ts-morph": { 3534 + "version": "27.0.2", 3535 + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-27.0.2.tgz", 3536 + "integrity": "sha512-fhUhgeljcrdZ+9DZND1De1029PrE+cMkIP7ooqkLRTrRLTqcki2AstsyJm0vRNbTbVCNJ0idGlbBrfqc7/nA8w==", 3537 + "license": "MIT", 3538 + "dependencies": { 3539 + "@ts-morph/common": "~0.28.1", 3540 + "code-block-writer": "^13.0.3" 3541 + } 3542 + }, 3543 + "node_modules/tslib": { 3544 + "version": "2.8.1", 3545 + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 3546 + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 3547 + "license": "0BSD" 3548 + }, 3549 + "node_modules/tsx": { 3550 + "version": "4.21.0", 3551 + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", 3552 + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", 3553 + "dev": true, 3554 + "license": "MIT", 3555 + "dependencies": { 3556 + "esbuild": "~0.27.0", 3557 + "get-tsconfig": "^4.7.5" 3558 + }, 3559 + "bin": { 3560 + "tsx": "dist/cli.mjs" 3561 + }, 3562 + "engines": { 3563 + "node": ">=18.0.0" 3564 + }, 3565 + "optionalDependencies": { 3566 + "fsevents": "~2.3.3" 3567 + } 3568 + }, 3569 + "node_modules/type-check": { 3570 + "version": "0.4.0", 3571 + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 3572 + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 3573 + "dev": true, 3574 + "license": "MIT", 3575 + "dependencies": { 3576 + "prelude-ls": "^1.2.1" 3577 + }, 3578 + "engines": { 3579 + "node": ">= 0.8.0" 3580 + } 3581 + }, 3582 + "node_modules/typescript": { 3583 + "version": "5.9.3", 3584 + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", 3585 + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", 3586 + "dev": true, 3587 + "license": "Apache-2.0", 3588 + "bin": { 3589 + "tsc": "bin/tsc", 3590 + "tsserver": "bin/tsserver" 3591 + }, 3592 + "engines": { 3593 + "node": ">=14.17" 3594 + } 3595 + }, 3596 + "node_modules/typescript-eslint": { 3597 + "version": "8.56.1", 3598 + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.56.1.tgz", 3599 + "integrity": "sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ==", 3600 + "dev": true, 3601 + "license": "MIT", 3602 + "dependencies": { 3603 + "@typescript-eslint/eslint-plugin": "8.56.1", 3604 + "@typescript-eslint/parser": "8.56.1", 3605 + "@typescript-eslint/typescript-estree": "8.56.1", 3606 + "@typescript-eslint/utils": "8.56.1" 3607 + }, 3608 + "engines": { 3609 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 3610 + }, 3611 + "funding": { 3612 + "type": "opencollective", 3613 + "url": "https://opencollective.com/typescript-eslint" 3614 + }, 3615 + "peerDependencies": { 3616 + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", 3617 + "typescript": ">=4.8.4 <6.0.0" 3618 + } 3619 + }, 3620 + "node_modules/uint8arrays": { 3621 + "version": "3.0.0", 3622 + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz", 3623 + "integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==", 3624 + "license": "MIT", 3625 + "dependencies": { 3626 + "multiformats": "^9.4.2" 3627 + } 3628 + }, 3629 + "node_modules/undici-types": { 3630 + "version": "7.18.2", 3631 + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", 3632 + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", 3633 + "dev": true, 3634 + "license": "MIT" 3635 + }, 3636 + "node_modules/unicode-segmenter": { 3637 + "version": "0.14.5", 3638 + "resolved": "https://registry.npmjs.org/unicode-segmenter/-/unicode-segmenter-0.14.5.tgz", 3639 + "integrity": "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==", 3640 + "license": "MIT" 3641 + }, 3642 + "node_modules/uri-js": { 3643 + "version": "4.4.1", 3644 + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 3645 + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 3646 + "dev": true, 3647 + "license": "BSD-2-Clause", 3648 + "dependencies": { 3649 + "punycode": "^2.1.0" 3650 + } 3651 + }, 3652 + "node_modules/varint": { 3653 + "version": "6.0.0", 3654 + "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", 3655 + "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", 3656 + "license": "MIT" 3657 + }, 3658 + "node_modules/which": { 3659 + "version": "2.0.2", 3660 + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 3661 + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 3662 + "dev": true, 3663 + "license": "ISC", 3664 + "dependencies": { 3665 + "isexe": "^2.0.0" 3666 + }, 3667 + "bin": { 3668 + "node-which": "bin/node-which" 3669 + }, 3670 + "engines": { 3671 + "node": ">= 8" 3672 + } 3673 + }, 3674 + "node_modules/word-wrap": { 3675 + "version": "1.2.5", 3676 + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", 3677 + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", 3678 + "dev": true, 3679 + "license": "MIT", 3680 + "engines": { 3681 + "node": ">=0.10.0" 3682 + } 3683 + }, 3684 + "node_modules/wrap-ansi": { 3685 + "version": "9.0.2", 3686 + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", 3687 + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", 3688 + "dev": true, 3689 + "license": "MIT", 3690 + "dependencies": { 3691 + "ansi-styles": "^6.2.1", 3692 + "string-width": "^7.0.0", 3693 + "strip-ansi": "^7.1.0" 3694 + }, 3695 + "engines": { 3696 + "node": ">=18" 3697 + }, 3698 + "funding": { 3699 + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 3700 + } 3701 + }, 3702 + "node_modules/wrap-ansi/node_modules/ansi-styles": { 3703 + "version": "6.2.3", 3704 + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", 3705 + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", 3706 + "dev": true, 3707 + "license": "MIT", 3708 + "engines": { 3709 + "node": ">=12" 3710 + }, 3711 + "funding": { 3712 + "url": "https://github.com/chalk/ansi-styles?sponsor=1" 3713 + } 3714 + }, 3715 + "node_modules/ws": { 3716 + "version": "8.19.0", 3717 + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", 3718 + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", 3719 + "license": "MIT", 3720 + "engines": { 3721 + "node": ">=10.0.0" 3722 + }, 3723 + "peerDependencies": { 3724 + "bufferutil": "^4.0.1", 3725 + "utf-8-validate": ">=5.0.2" 3726 + }, 3727 + "peerDependenciesMeta": { 3728 + "bufferutil": { 3729 + "optional": true 3730 + }, 3731 + "utf-8-validate": { 3732 + "optional": true 3733 + } 3734 + } 3735 + }, 3736 + "node_modules/y18n": { 3737 + "version": "5.0.8", 3738 + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 3739 + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 3740 + "license": "ISC", 3741 + "engines": { 3742 + "node": ">=10" 3743 + } 3744 + }, 3745 + "node_modules/yaml": { 3746 + "version": "2.8.2", 3747 + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", 3748 + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", 3749 + "dev": true, 3750 + "license": "ISC", 3751 + "bin": { 3752 + "yaml": "bin.mjs" 3753 + }, 3754 + "engines": { 3755 + "node": ">= 14.6" 3756 + }, 3757 + "funding": { 3758 + "url": "https://github.com/sponsors/eemeli" 3759 + } 3760 + }, 3761 + "node_modules/yargs": { 3762 + "version": "17.7.2", 3763 + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", 3764 + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", 3765 + "license": "MIT", 3766 + "dependencies": { 3767 + "cliui": "^8.0.1", 3768 + "escalade": "^3.1.1", 3769 + "get-caller-file": "^2.0.5", 3770 + "require-directory": "^2.1.1", 3771 + "string-width": "^4.2.3", 3772 + "y18n": "^5.0.5", 3773 + "yargs-parser": "^21.1.1" 3774 + }, 3775 + "engines": { 3776 + "node": ">=12" 3777 + } 3778 + }, 3779 + "node_modules/yargs-parser": { 3780 + "version": "21.1.1", 3781 + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", 3782 + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", 3783 + "license": "ISC", 3784 + "engines": { 3785 + "node": ">=12" 3786 + } 3787 + }, 3788 + "node_modules/yargs/node_modules/ansi-regex": { 3789 + "version": "5.0.1", 3790 + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 3791 + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 3792 + "license": "MIT", 3793 + "engines": { 3794 + "node": ">=8" 3795 + } 3796 + }, 3797 + "node_modules/yargs/node_modules/emoji-regex": { 3798 + "version": "8.0.0", 3799 + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 3800 + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 3801 + "license": "MIT" 3802 + }, 3803 + "node_modules/yargs/node_modules/is-fullwidth-code-point": { 3804 + "version": "3.0.0", 3805 + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 3806 + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 3807 + "license": "MIT", 3808 + "engines": { 3809 + "node": ">=8" 3810 + } 3811 + }, 3812 + "node_modules/yargs/node_modules/string-width": { 3813 + "version": "4.2.3", 3814 + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 3815 + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 3816 + "license": "MIT", 3817 + "dependencies": { 3818 + "emoji-regex": "^8.0.0", 3819 + "is-fullwidth-code-point": "^3.0.0", 3820 + "strip-ansi": "^6.0.1" 3821 + }, 3822 + "engines": { 3823 + "node": ">=8" 3824 + } 3825 + }, 3826 + "node_modules/yargs/node_modules/strip-ansi": { 3827 + "version": "6.0.1", 3828 + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 3829 + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 3830 + "license": "MIT", 3831 + "dependencies": { 3832 + "ansi-regex": "^5.0.1" 3833 + }, 3834 + "engines": { 3835 + "node": ">=8" 3836 + } 3837 + }, 3838 + "node_modules/yocto-queue": { 3839 + "version": "0.1.0", 3840 + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 3841 + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 3842 + "dev": true, 3843 + "license": "MIT", 3844 + "engines": { 3845 + "node": ">=10" 3846 + }, 3847 + "funding": { 3848 + "url": "https://github.com/sponsors/sindresorhus" 3849 + } 3850 + }, 3851 + "node_modules/zod": { 3852 + "version": "3.25.76", 3853 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 3854 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 3855 + "license": "MIT", 3856 + "funding": { 3857 + "url": "https://github.com/sponsors/colinhacks" 3858 + } 3859 + }, 3860 + "packages/@inlay/cache": { 3861 + "version": "0.0.1", 3862 + "license": "MIT", 3863 + "devDependencies": { 3864 + "typescript": "^5.9.0" 3865 + } 3866 + }, 3867 + "packages/@inlay/core": { 3868 + "version": "0.0.12", 3869 + "license": "MIT", 3870 + "dependencies": { 3871 + "@atproto/lex": "^0.0.18", 3872 + "@atproto/syntax": "^0.4.3" 3873 + }, 3874 + "devDependencies": { 3875 + "typescript": "^5.9.0" 3876 + } 3877 + }, 3878 + "packages/@inlay/render": { 3879 + "version": "0.0.1", 3880 + "dependencies": { 3881 + "@atproto/lexicon": "^0.6.1", 3882 + "@atproto/syntax": "^0.4.3", 3883 + "@inlay/core": "*" 3884 + }, 3885 + "devDependencies": { 3886 + "typescript": "^5.9.0" 3887 + } 3888 + }, 3889 + "proto": { 3890 + "dependencies": { 3891 + "@atproto/syntax": "^0.3.0", 3892 + "@hono/node-server": "^1.19.9", 3893 + "@inlay/core": "*", 3894 + "@inlay/render": "*", 3895 + "dotenv": "^16.5.0", 3896 + "hono": "^4.12.2", 3897 + "ioredis": "^5.6.0" 3898 + }, 3899 + "devDependencies": { 3900 + "tsx": "^4.0.0", 3901 + "typescript": "^5.9.0" 3902 + } 3903 + }, 3904 + "proto/node_modules/@atproto/syntax": { 3905 + "version": "0.3.4", 3906 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.3.4.tgz", 3907 + "integrity": "sha512-8CNmi5DipOLaVeSMPggMe7FCksVag0aO6XZy9WflbduTKM4dFZVCs4686UeMLfGRXX+X966XgwECHoLYrovMMg==", 3908 + "license": "MIT" 3909 + } 3910 + } 3911 + }
+40
package.json
··· 1 + { 2 + "name": "inlay", 3 + "private": true, 4 + "type": "module", 5 + "workspaces": [ 6 + "packages/@inlay/core", 7 + "packages/@inlay/render", 8 + "packages/@inlay/cache", 9 + "proto", 10 + "invalidator" 11 + ], 12 + "scripts": { 13 + "test": "npm run test --workspace=packages/@inlay/core && npm run test --workspace=packages/@inlay/render", 14 + "build": "npm run build --workspace=packages/@inlay/core", 15 + "typecheck": "tsc --noEmit", 16 + "lint": "eslint . --ext .ts,.tsx", 17 + "lint:fix": "eslint . --ext .ts,.tsx --fix", 18 + "format": "prettier --write .", 19 + "format:check": "prettier --check .", 20 + "prepare": "husky", 21 + "generate:lexicons": "npx lex build --lexicons lexicons --out generated --clear --indexFile --importExt \"\"", 22 + "deploy": "railway service proto && railway up -d && railway service invalidator && railway up -d", 23 + "deploy:proto": "railway service proto && railway up -d", 24 + "deploy:invalidator": "railway service invalidator && railway up -d" 25 + }, 26 + "devDependencies": { 27 + "@atproto/lex": "^0.0.18", 28 + "@atproto/lex-resolver": "^0.0.15", 29 + "eslint": "^9.0.0", 30 + "typescript-eslint": "^8.0.0", 31 + "husky": "^9.0.0", 32 + "lint-staged": "^15.0.0", 33 + "prettier": "^3.0.0", 34 + "tsx": "^4.0.0", 35 + "typescript": "^5.9.0" 36 + }, 37 + "engines": { 38 + "node": ">=22.0.0" 39 + } 40 + }
+25
packages/@inlay/cache/package.json
··· 1 + { 2 + "name": "@inlay/cache", 3 + "version": "0.0.1", 4 + "type": "module", 5 + "author": "Dan Abramov <dan.abramov@gmail.com>", 6 + "license": "MIT", 7 + "main": "./dist/index.js", 8 + "types": "./dist/index.d.ts", 9 + "exports": { 10 + ".": { 11 + "types": "./dist/index.d.ts", 12 + "import": "./dist/index.js" 13 + } 14 + }, 15 + "files": [ 16 + "dist" 17 + ], 18 + "scripts": { 19 + "build": "tsc", 20 + "prepublishOnly": "npm run build" 21 + }, 22 + "devDependencies": { 23 + "typescript": "^5.9.0" 24 + } 25 + }
+72
packages/@inlay/cache/src/index.ts
··· 1 + // @inlay/cache — Cache declaration primitives for inlay components. 2 + // 3 + // Pure JavaScript, no dependencies. Reads from a well-known global 4 + // set up by the inlay server SDK at runtime. Crashes if the global 5 + // is absent — calling cache functions outside an inlay server is a bug. 6 + // 7 + // Types mirror at.inlay.defs (cachePolicy, tagRecord, tagLink). 8 + 9 + // -- Types (from at.inlay.defs lexicon) -- 10 + 11 + type Life = "seconds" | "minutes" | "hours" | "max"; 12 + 13 + type TagRecord = { 14 + $type: "at.inlay.defs#tagRecord"; 15 + uri: string; 16 + }; 17 + 18 + type TagLink = { 19 + $type: "at.inlay.defs#tagLink"; 20 + subject: string; 21 + from?: string; 22 + }; 23 + 24 + type CacheTag = TagRecord | TagLink; 25 + 26 + // -- Dispatcher (set by server SDK) -- 27 + 28 + interface Dispatcher { 29 + cacheLife(life: Life): void; 30 + cacheTag(tag: CacheTag): void; 31 + } 32 + 33 + const GLOBAL_KEY = Symbol.for("inlay.cache"); 34 + 35 + function getDispatcher(): Dispatcher { 36 + const d = (globalThis as Record<symbol, Dispatcher | undefined>)[GLOBAL_KEY]; 37 + if (!d) { 38 + throw new Error( 39 + "@inlay/cache: server SDK not initialized. " + 40 + "Cache functions can only be called inside an inlay component handler." 41 + ); 42 + } 43 + return d; 44 + } 45 + 46 + // -- Public API -- 47 + 48 + /** 49 + * Declare how frequently the component's data changes. 50 + * Multiple calls are collected — the strictest (shortest) life wins. 51 + */ 52 + export function cacheLife(life: Life): void { 53 + getDispatcher().cacheLife(life); 54 + } 55 + 56 + /** 57 + * Declare a cache dependency on a record, collection, or identity. 58 + */ 59 + export function cacheTagRecord(uri: string): void { 60 + getDispatcher().cacheTag({ $type: "at.inlay.defs#tagRecord", uri }); 61 + } 62 + 63 + /** 64 + * Declare a cache dependency on backlinks to a subject. 65 + */ 66 + export function cacheTagLink(subject: string, from?: string): void { 67 + const tag: TagLink = { $type: "at.inlay.defs#tagLink", subject }; 68 + if (from) tag.from = from; 69 + getDispatcher().cacheTag(tag); 70 + } 71 + 72 + export type { Life, CacheTag, TagRecord, TagLink, Dispatcher };
+14
packages/@inlay/cache/tsconfig.json
··· 1 + { 2 + "compilerOptions": { 3 + "target": "ES2022", 4 + "module": "ES2022", 5 + "moduleResolution": "bundler", 6 + "declaration": true, 7 + "outDir": "dist", 8 + "rootDir": "src", 9 + "strict": true, 10 + "esModuleInterop": true, 11 + "skipLibCheck": true 12 + }, 13 + "include": ["src"] 14 + }
+992
packages/@inlay/core/package-lock.json
··· 1 + { 2 + "name": "@inlay/core", 3 + "version": "0.0.5", 4 + "lockfileVersion": 3, 5 + "requires": true, 6 + "packages": { 7 + "": { 8 + "name": "@inlay/core", 9 + "version": "0.0.5", 10 + "license": "MIT", 11 + "dependencies": { 12 + "@atproto/lex": "^0.0.12", 13 + "@atproto/syntax": "^0.4.3" 14 + }, 15 + "devDependencies": { 16 + "typescript": "^5.9.0" 17 + } 18 + }, 19 + "node_modules/@atproto-labs/did-resolver": { 20 + "version": "0.2.5", 21 + "resolved": "https://registry.npmjs.org/@atproto-labs/did-resolver/-/did-resolver-0.2.5.tgz", 22 + "integrity": "sha512-he7EC6OMSifNs01a4RT9mta/yYitoKDzlK9ty2TFV5Uj/+HpB4vYMRdIDFrRW0Hcsehy90E2t/dw0t7361MEKQ==", 23 + "license": "MIT", 24 + "dependencies": { 25 + "@atproto-labs/fetch": "0.2.3", 26 + "@atproto-labs/pipe": "0.1.1", 27 + "@atproto-labs/simple-store": "0.3.0", 28 + "@atproto-labs/simple-store-memory": "0.1.4", 29 + "@atproto/did": "0.2.4", 30 + "zod": "^3.23.8" 31 + } 32 + }, 33 + "node_modules/@atproto-labs/fetch": { 34 + "version": "0.2.3", 35 + "resolved": "https://registry.npmjs.org/@atproto-labs/fetch/-/fetch-0.2.3.tgz", 36 + "integrity": "sha512-NZtbJOCbxKUFRFKMpamT38PUQMY0hX0p7TG5AEYOPhZKZEP7dHZ1K2s1aB8MdVH0qxmqX7nQleNrrvLf09Zfdw==", 37 + "license": "MIT", 38 + "dependencies": { 39 + "@atproto-labs/pipe": "0.1.1" 40 + } 41 + }, 42 + "node_modules/@atproto-labs/pipe": { 43 + "version": "0.1.1", 44 + "resolved": "https://registry.npmjs.org/@atproto-labs/pipe/-/pipe-0.1.1.tgz", 45 + "integrity": "sha512-hdNw2oUs2B6BN1lp+32pF7cp8EMKuIN5Qok2Vvv/aOpG/3tNSJ9YkvfI0k6Zd188LeDDYRUpYpxcoFIcGH/FNg==", 46 + "license": "MIT" 47 + }, 48 + "node_modules/@atproto-labs/simple-store": { 49 + "version": "0.3.0", 50 + "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store/-/simple-store-0.3.0.tgz", 51 + "integrity": "sha512-nOb6ONKBRJHRlukW1sVawUkBqReLlLx6hT35VS3imaNPwiXDxLnTK7lxw3Lrl9k5yugSBDQAkZAq3MPTEFSUBQ==", 52 + "license": "MIT" 53 + }, 54 + "node_modules/@atproto-labs/simple-store-memory": { 55 + "version": "0.1.4", 56 + "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store-memory/-/simple-store-memory-0.1.4.tgz", 57 + "integrity": "sha512-3mKY4dP8I7yKPFj9VKpYyCRzGJOi5CEpOLPlRhoJyLmgs3J4RzDrjn323Oakjz2Aj2JzRU/AIvWRAZVhpYNJHw==", 58 + "license": "MIT", 59 + "dependencies": { 60 + "@atproto-labs/simple-store": "0.3.0", 61 + "lru-cache": "^10.2.0" 62 + } 63 + }, 64 + "node_modules/@atproto/common": { 65 + "version": "0.5.9", 66 + "resolved": "https://registry.npmjs.org/@atproto/common/-/common-0.5.9.tgz", 67 + "integrity": "sha512-rzl8dB7ErpA/VUgCidahUtbxEph50J4g7j68bZmlwwrHlrtvTe8DjrwH5EUFEcegl9dadIhcVJ3qi0kPKEUr+g==", 68 + "license": "MIT", 69 + "dependencies": { 70 + "@atproto/common-web": "^0.4.13", 71 + "@atproto/lex-cbor": "0.0.9", 72 + "@atproto/lex-data": "0.0.9", 73 + "iso-datestring-validator": "^2.2.2", 74 + "multiformats": "^9.9.0", 75 + "pino": "^8.21.0" 76 + }, 77 + "engines": { 78 + "node": ">=18.7.0" 79 + } 80 + }, 81 + "node_modules/@atproto/common-web": { 82 + "version": "0.4.13", 83 + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.13.tgz", 84 + "integrity": "sha512-TewRUyB/dVJ5PtI3QmJzEgT3wDsvpnLJ+48hPl+LuUueJPamZevXKJN6dFjtbKAMFRnl2bKfdsf79qwvdSaLKQ==", 85 + "license": "MIT", 86 + "dependencies": { 87 + "@atproto/lex-data": "0.0.9", 88 + "@atproto/lex-json": "0.0.9", 89 + "@atproto/syntax": "0.4.3", 90 + "zod": "^3.23.8" 91 + } 92 + }, 93 + "node_modules/@atproto/crypto": { 94 + "version": "0.4.5", 95 + "resolved": "https://registry.npmjs.org/@atproto/crypto/-/crypto-0.4.5.tgz", 96 + "integrity": "sha512-n40aKkMoCatP0u9Yvhrdk6fXyOHFDDbkdm4h4HCyWW+KlKl8iXfD5iV+ECq+w5BM+QH25aIpt3/j6EUNerhLxw==", 97 + "license": "MIT", 98 + "dependencies": { 99 + "@noble/curves": "^1.7.0", 100 + "@noble/hashes": "^1.6.1", 101 + "uint8arrays": "3.0.0" 102 + }, 103 + "engines": { 104 + "node": ">=18.7.0" 105 + } 106 + }, 107 + "node_modules/@atproto/did": { 108 + "version": "0.2.4", 109 + "resolved": "https://registry.npmjs.org/@atproto/did/-/did-0.2.4.tgz", 110 + "integrity": "sha512-nxNiCgXeo7pfjojq9fpfZxCO0X0xUipNVKW+AHNZwQKiUDt6zYL0VXEfm8HBUwQOCmKvj2pRRSM1Cur+tUWk3g==", 111 + "license": "MIT", 112 + "dependencies": { 113 + "zod": "^3.23.8" 114 + } 115 + }, 116 + "node_modules/@atproto/lex": { 117 + "version": "0.0.12", 118 + "resolved": "https://registry.npmjs.org/@atproto/lex/-/lex-0.0.12.tgz", 119 + "integrity": "sha512-aoIRD/5kPbF2SM75i4s5qyfpXB9363OIIskKtTx5k8W4JhbRpW7I9TjfBKXvi63E7tOHH14crq2WgKlI3nipxg==", 120 + "license": "MIT", 121 + "dependencies": { 122 + "@atproto/lex-builder": "0.0.12", 123 + "@atproto/lex-client": "0.0.10", 124 + "@atproto/lex-data": "0.0.9", 125 + "@atproto/lex-installer": "0.0.12", 126 + "@atproto/lex-json": "0.0.9", 127 + "@atproto/lex-schema": "0.0.10", 128 + "tslib": "^2.8.1", 129 + "yargs": "^17.0.0" 130 + }, 131 + "bin": { 132 + "lex": "bin/lex", 133 + "ts-lex": "bin/lex" 134 + } 135 + }, 136 + "node_modules/@atproto/lex-builder": { 137 + "version": "0.0.12", 138 + "resolved": "https://registry.npmjs.org/@atproto/lex-builder/-/lex-builder-0.0.12.tgz", 139 + "integrity": "sha512-ObWnmsbkPwjKKIX/L0JyMptmggr3gvbZKPDcpr1eSBUWyWeqqX8OfIlHYLgm5veNuO776RV05CE7BdQFQUA+9Q==", 140 + "license": "MIT", 141 + "dependencies": { 142 + "@atproto/lex-document": "0.0.11", 143 + "@atproto/lex-schema": "0.0.10", 144 + "prettier": "^3.2.5", 145 + "ts-morph": "^27.0.0", 146 + "tslib": "^2.8.1" 147 + } 148 + }, 149 + "node_modules/@atproto/lex-cbor": { 150 + "version": "0.0.9", 151 + "resolved": "https://registry.npmjs.org/@atproto/lex-cbor/-/lex-cbor-0.0.9.tgz", 152 + "integrity": "sha512-szkS569j1eZsIxZKh2VZHVq7pSpewy1wHh8c6nVYekHfYcJhFkevQq/DjTeatZ7YZKNReGYthQulgaZq2ytfWQ==", 153 + "license": "MIT", 154 + "dependencies": { 155 + "@atproto/lex-data": "0.0.9", 156 + "tslib": "^2.8.1" 157 + } 158 + }, 159 + "node_modules/@atproto/lex-client": { 160 + "version": "0.0.10", 161 + "resolved": "https://registry.npmjs.org/@atproto/lex-client/-/lex-client-0.0.10.tgz", 162 + "integrity": "sha512-n3g9KoY5hw7W29mcR4TrjN5qOi6JiWty7r1heqLLfYiq5TxaRx9/QBa2hbN4h1p4xxICPZoDlNtuGq8YV4U8mg==", 163 + "license": "MIT", 164 + "dependencies": { 165 + "@atproto/lex-data": "0.0.9", 166 + "@atproto/lex-json": "0.0.9", 167 + "@atproto/lex-schema": "0.0.10", 168 + "tslib": "^2.8.1" 169 + } 170 + }, 171 + "node_modules/@atproto/lex-data": { 172 + "version": "0.0.9", 173 + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.9.tgz", 174 + "integrity": "sha512-1slwe4sG0cyWtsq16+rBoWIxNDqGPkkvN+PV6JuzA7dgUK9bjUmXBGQU4eZlUPSS43X1Nhmr/9VjgKmEzU9vDw==", 175 + "license": "MIT", 176 + "dependencies": { 177 + "multiformats": "^9.9.0", 178 + "tslib": "^2.8.1", 179 + "uint8arrays": "3.0.0", 180 + "unicode-segmenter": "^0.14.0" 181 + } 182 + }, 183 + "node_modules/@atproto/lex-document": { 184 + "version": "0.0.11", 185 + "resolved": "https://registry.npmjs.org/@atproto/lex-document/-/lex-document-0.0.11.tgz", 186 + "integrity": "sha512-ePtFOU7yYAp1IL1mPDrAyo+ajN9V7W8z6BY4xXEM/m9U3vCVNC+SIkgkfwumqSUqOtBy4gpz52ppK+R/9S8UWg==", 187 + "license": "MIT", 188 + "dependencies": { 189 + "@atproto/lex-schema": "0.0.10", 190 + "core-js": "^3", 191 + "tslib": "^2.8.1" 192 + } 193 + }, 194 + "node_modules/@atproto/lex-installer": { 195 + "version": "0.0.12", 196 + "resolved": "https://registry.npmjs.org/@atproto/lex-installer/-/lex-installer-0.0.12.tgz", 197 + "integrity": "sha512-Ycgwl2zPbuYhYXoRPDlNgq/WROrg1q3Rz5dOlI/g4x5gh/C63P32qf0+KJbngUqdVaQWj1P4XQAeVne4Gn1+dw==", 198 + "license": "MIT", 199 + "dependencies": { 200 + "@atproto/lex-builder": "0.0.12", 201 + "@atproto/lex-cbor": "0.0.9", 202 + "@atproto/lex-data": "0.0.9", 203 + "@atproto/lex-document": "0.0.11", 204 + "@atproto/lex-resolver": "0.0.11", 205 + "@atproto/lex-schema": "0.0.10", 206 + "@atproto/syntax": "0.4.3", 207 + "tslib": "^2.8.1" 208 + } 209 + }, 210 + "node_modules/@atproto/lex-json": { 211 + "version": "0.0.9", 212 + "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.9.tgz", 213 + "integrity": "sha512-Q2v1EVZcnd+ndyZj1r2UlGikA7q6It24CFPLbxokcf5Ba4RBupH8IkkQX7mqUDSRWPgQdmZYIdW9wUln+MKDqw==", 214 + "license": "MIT", 215 + "dependencies": { 216 + "@atproto/lex-data": "0.0.9", 217 + "tslib": "^2.8.1" 218 + } 219 + }, 220 + "node_modules/@atproto/lex-resolver": { 221 + "version": "0.0.11", 222 + "resolved": "https://registry.npmjs.org/@atproto/lex-resolver/-/lex-resolver-0.0.11.tgz", 223 + "integrity": "sha512-fzrr9W47+xkZDMYjeVG0wSq3mA5d/2MTQknieqeH0hbR+SNwW0PGf/tclH58TrWPEpWCcLY+Yv5ydDGuua8j+Q==", 224 + "license": "MIT", 225 + "dependencies": { 226 + "@atproto-labs/did-resolver": "0.2.5", 227 + "@atproto/crypto": "0.4.5", 228 + "@atproto/lex-client": "0.0.10", 229 + "@atproto/lex-data": "0.0.9", 230 + "@atproto/lex-document": "0.0.11", 231 + "@atproto/lex-schema": "0.0.10", 232 + "@atproto/repo": "0.8.12", 233 + "@atproto/syntax": "0.4.3", 234 + "tslib": "^2.8.1" 235 + } 236 + }, 237 + "node_modules/@atproto/lex-schema": { 238 + "version": "0.0.10", 239 + "resolved": "https://registry.npmjs.org/@atproto/lex-schema/-/lex-schema-0.0.10.tgz", 240 + "integrity": "sha512-970BZVHtsLn03k2wkpYzdY2o/oZycqUReG1UblOkWYkbhQd04WqliiGrpUie/ls25oJs37ymI+fCDPcYg9tuQg==", 241 + "license": "MIT", 242 + "dependencies": { 243 + "@atproto/lex-data": "0.0.9", 244 + "@atproto/syntax": "0.4.3", 245 + "tslib": "^2.8.1" 246 + } 247 + }, 248 + "node_modules/@atproto/lexicon": { 249 + "version": "0.6.1", 250 + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.6.1.tgz", 251 + "integrity": "sha512-/vI1kVlY50Si+5MXpvOucelnYwb0UJ6Qto5mCp+7Q5C+Jtp+SoSykAPVvjVtTnQUH2vrKOFOwpb3C375vSKzXw==", 252 + "license": "MIT", 253 + "dependencies": { 254 + "@atproto/common-web": "^0.4.13", 255 + "@atproto/syntax": "^0.4.3", 256 + "iso-datestring-validator": "^2.2.2", 257 + "multiformats": "^9.9.0", 258 + "zod": "^3.23.8" 259 + } 260 + }, 261 + "node_modules/@atproto/repo": { 262 + "version": "0.8.12", 263 + "resolved": "https://registry.npmjs.org/@atproto/repo/-/repo-0.8.12.tgz", 264 + "integrity": "sha512-QpVTVulgfz5PUiCTELlDBiRvnsnwrFWi+6CfY88VwXzrRHd9NE8GItK7sfxQ6U65vD/idH8ddCgFrlrsn1REPQ==", 265 + "license": "MIT", 266 + "dependencies": { 267 + "@atproto/common": "^0.5.3", 268 + "@atproto/common-web": "^0.4.7", 269 + "@atproto/crypto": "^0.4.5", 270 + "@atproto/lexicon": "^0.6.0", 271 + "@ipld/dag-cbor": "^7.0.0", 272 + "multiformats": "^9.9.0", 273 + "uint8arrays": "3.0.0", 274 + "varint": "^6.0.0", 275 + "zod": "^3.23.8" 276 + }, 277 + "engines": { 278 + "node": ">=18.7.0" 279 + } 280 + }, 281 + "node_modules/@atproto/syntax": { 282 + "version": "0.4.3", 283 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.3.tgz", 284 + "integrity": "sha512-YoZUz40YAJr5nPwvCDWgodEOlt5IftZqPJvA0JDWjuZKD8yXddTwSzXSaKQAzGOpuM+/A3uXRtPzJJqlScc+iA==", 285 + "license": "MIT", 286 + "dependencies": { 287 + "tslib": "^2.8.1" 288 + } 289 + }, 290 + "node_modules/@ipld/dag-cbor": { 291 + "version": "7.0.3", 292 + "resolved": "https://registry.npmjs.org/@ipld/dag-cbor/-/dag-cbor-7.0.3.tgz", 293 + "integrity": "sha512-1VVh2huHsuohdXC1bGJNE8WR72slZ9XE2T3wbBBq31dm7ZBatmKLLxrB+XAqafxfRFjv08RZmj/W/ZqaM13AuA==", 294 + "license": "(Apache-2.0 AND MIT)", 295 + "dependencies": { 296 + "cborg": "^1.6.0", 297 + "multiformats": "^9.5.4" 298 + } 299 + }, 300 + "node_modules/@isaacs/balanced-match": { 301 + "version": "4.0.1", 302 + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", 303 + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", 304 + "license": "MIT", 305 + "engines": { 306 + "node": "20 || >=22" 307 + } 308 + }, 309 + "node_modules/@isaacs/brace-expansion": { 310 + "version": "5.0.0", 311 + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", 312 + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", 313 + "license": "MIT", 314 + "dependencies": { 315 + "@isaacs/balanced-match": "^4.0.1" 316 + }, 317 + "engines": { 318 + "node": "20 || >=22" 319 + } 320 + }, 321 + "node_modules/@noble/curves": { 322 + "version": "1.9.7", 323 + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", 324 + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", 325 + "license": "MIT", 326 + "dependencies": { 327 + "@noble/hashes": "1.8.0" 328 + }, 329 + "engines": { 330 + "node": "^14.21.3 || >=16" 331 + }, 332 + "funding": { 333 + "url": "https://paulmillr.com/funding/" 334 + } 335 + }, 336 + "node_modules/@noble/hashes": { 337 + "version": "1.8.0", 338 + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", 339 + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", 340 + "license": "MIT", 341 + "engines": { 342 + "node": "^14.21.3 || >=16" 343 + }, 344 + "funding": { 345 + "url": "https://paulmillr.com/funding/" 346 + } 347 + }, 348 + "node_modules/@ts-morph/common": { 349 + "version": "0.28.1", 350 + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.28.1.tgz", 351 + "integrity": "sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==", 352 + "license": "MIT", 353 + "dependencies": { 354 + "minimatch": "^10.0.1", 355 + "path-browserify": "^1.0.1", 356 + "tinyglobby": "^0.2.14" 357 + } 358 + }, 359 + "node_modules/abort-controller": { 360 + "version": "3.0.0", 361 + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 362 + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 363 + "license": "MIT", 364 + "dependencies": { 365 + "event-target-shim": "^5.0.0" 366 + }, 367 + "engines": { 368 + "node": ">=6.5" 369 + } 370 + }, 371 + "node_modules/ansi-regex": { 372 + "version": "5.0.1", 373 + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 374 + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 375 + "license": "MIT", 376 + "engines": { 377 + "node": ">=8" 378 + } 379 + }, 380 + "node_modules/ansi-styles": { 381 + "version": "4.3.0", 382 + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 383 + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 384 + "license": "MIT", 385 + "dependencies": { 386 + "color-convert": "^2.0.1" 387 + }, 388 + "engines": { 389 + "node": ">=8" 390 + }, 391 + "funding": { 392 + "url": "https://github.com/chalk/ansi-styles?sponsor=1" 393 + } 394 + }, 395 + "node_modules/atomic-sleep": { 396 + "version": "1.0.0", 397 + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", 398 + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", 399 + "license": "MIT", 400 + "engines": { 401 + "node": ">=8.0.0" 402 + } 403 + }, 404 + "node_modules/base64-js": { 405 + "version": "1.5.1", 406 + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 407 + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 408 + "funding": [ 409 + { 410 + "type": "github", 411 + "url": "https://github.com/sponsors/feross" 412 + }, 413 + { 414 + "type": "patreon", 415 + "url": "https://www.patreon.com/feross" 416 + }, 417 + { 418 + "type": "consulting", 419 + "url": "https://feross.org/support" 420 + } 421 + ], 422 + "license": "MIT" 423 + }, 424 + "node_modules/buffer": { 425 + "version": "6.0.3", 426 + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", 427 + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", 428 + "funding": [ 429 + { 430 + "type": "github", 431 + "url": "https://github.com/sponsors/feross" 432 + }, 433 + { 434 + "type": "patreon", 435 + "url": "https://www.patreon.com/feross" 436 + }, 437 + { 438 + "type": "consulting", 439 + "url": "https://feross.org/support" 440 + } 441 + ], 442 + "license": "MIT", 443 + "dependencies": { 444 + "base64-js": "^1.3.1", 445 + "ieee754": "^1.2.1" 446 + } 447 + }, 448 + "node_modules/cborg": { 449 + "version": "1.10.2", 450 + "resolved": "https://registry.npmjs.org/cborg/-/cborg-1.10.2.tgz", 451 + "integrity": "sha512-b3tFPA9pUr2zCUiCfRd2+wok2/LBSNUMKOuRRok+WlvvAgEt/PlbgPTsZUcwCOs53IJvLgTp0eotwtosE6njug==", 452 + "license": "Apache-2.0", 453 + "bin": { 454 + "cborg": "cli.js" 455 + } 456 + }, 457 + "node_modules/cliui": { 458 + "version": "8.0.1", 459 + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", 460 + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", 461 + "license": "ISC", 462 + "dependencies": { 463 + "string-width": "^4.2.0", 464 + "strip-ansi": "^6.0.1", 465 + "wrap-ansi": "^7.0.0" 466 + }, 467 + "engines": { 468 + "node": ">=12" 469 + } 470 + }, 471 + "node_modules/code-block-writer": { 472 + "version": "13.0.3", 473 + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", 474 + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", 475 + "license": "MIT" 476 + }, 477 + "node_modules/color-convert": { 478 + "version": "2.0.1", 479 + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 480 + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 481 + "license": "MIT", 482 + "dependencies": { 483 + "color-name": "~1.1.4" 484 + }, 485 + "engines": { 486 + "node": ">=7.0.0" 487 + } 488 + }, 489 + "node_modules/color-name": { 490 + "version": "1.1.4", 491 + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 492 + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 493 + "license": "MIT" 494 + }, 495 + "node_modules/core-js": { 496 + "version": "3.48.0", 497 + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", 498 + "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", 499 + "hasInstallScript": true, 500 + "license": "MIT", 501 + "funding": { 502 + "type": "opencollective", 503 + "url": "https://opencollective.com/core-js" 504 + } 505 + }, 506 + "node_modules/emoji-regex": { 507 + "version": "8.0.0", 508 + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 509 + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 510 + "license": "MIT" 511 + }, 512 + "node_modules/escalade": { 513 + "version": "3.2.0", 514 + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", 515 + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", 516 + "license": "MIT", 517 + "engines": { 518 + "node": ">=6" 519 + } 520 + }, 521 + "node_modules/event-target-shim": { 522 + "version": "5.0.1", 523 + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 524 + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 525 + "license": "MIT", 526 + "engines": { 527 + "node": ">=6" 528 + } 529 + }, 530 + "node_modules/events": { 531 + "version": "3.3.0", 532 + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", 533 + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", 534 + "license": "MIT", 535 + "engines": { 536 + "node": ">=0.8.x" 537 + } 538 + }, 539 + "node_modules/fast-redact": { 540 + "version": "3.5.0", 541 + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", 542 + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", 543 + "license": "MIT", 544 + "engines": { 545 + "node": ">=6" 546 + } 547 + }, 548 + "node_modules/fdir": { 549 + "version": "6.5.0", 550 + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", 551 + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", 552 + "license": "MIT", 553 + "engines": { 554 + "node": ">=12.0.0" 555 + }, 556 + "peerDependencies": { 557 + "picomatch": "^3 || ^4" 558 + }, 559 + "peerDependenciesMeta": { 560 + "picomatch": { 561 + "optional": true 562 + } 563 + } 564 + }, 565 + "node_modules/get-caller-file": { 566 + "version": "2.0.5", 567 + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 568 + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 569 + "license": "ISC", 570 + "engines": { 571 + "node": "6.* || 8.* || >= 10.*" 572 + } 573 + }, 574 + "node_modules/ieee754": { 575 + "version": "1.2.1", 576 + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 577 + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 578 + "funding": [ 579 + { 580 + "type": "github", 581 + "url": "https://github.com/sponsors/feross" 582 + }, 583 + { 584 + "type": "patreon", 585 + "url": "https://www.patreon.com/feross" 586 + }, 587 + { 588 + "type": "consulting", 589 + "url": "https://feross.org/support" 590 + } 591 + ], 592 + "license": "BSD-3-Clause" 593 + }, 594 + "node_modules/is-fullwidth-code-point": { 595 + "version": "3.0.0", 596 + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 597 + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 598 + "license": "MIT", 599 + "engines": { 600 + "node": ">=8" 601 + } 602 + }, 603 + "node_modules/iso-datestring-validator": { 604 + "version": "2.2.2", 605 + "resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz", 606 + "integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==", 607 + "license": "MIT" 608 + }, 609 + "node_modules/lru-cache": { 610 + "version": "10.4.3", 611 + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", 612 + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", 613 + "license": "ISC" 614 + }, 615 + "node_modules/minimatch": { 616 + "version": "10.1.1", 617 + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", 618 + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", 619 + "license": "BlueOak-1.0.0", 620 + "dependencies": { 621 + "@isaacs/brace-expansion": "^5.0.0" 622 + }, 623 + "engines": { 624 + "node": "20 || >=22" 625 + }, 626 + "funding": { 627 + "url": "https://github.com/sponsors/isaacs" 628 + } 629 + }, 630 + "node_modules/multiformats": { 631 + "version": "9.9.0", 632 + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 633 + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 634 + "license": "(Apache-2.0 AND MIT)" 635 + }, 636 + "node_modules/on-exit-leak-free": { 637 + "version": "2.1.2", 638 + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", 639 + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", 640 + "license": "MIT", 641 + "engines": { 642 + "node": ">=14.0.0" 643 + } 644 + }, 645 + "node_modules/path-browserify": { 646 + "version": "1.0.1", 647 + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", 648 + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", 649 + "license": "MIT" 650 + }, 651 + "node_modules/picomatch": { 652 + "version": "4.0.3", 653 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 654 + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 655 + "license": "MIT", 656 + "engines": { 657 + "node": ">=12" 658 + }, 659 + "funding": { 660 + "url": "https://github.com/sponsors/jonschlinkert" 661 + } 662 + }, 663 + "node_modules/pino": { 664 + "version": "8.21.0", 665 + "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz", 666 + "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==", 667 + "license": "MIT", 668 + "dependencies": { 669 + "atomic-sleep": "^1.0.0", 670 + "fast-redact": "^3.1.1", 671 + "on-exit-leak-free": "^2.1.0", 672 + "pino-abstract-transport": "^1.2.0", 673 + "pino-std-serializers": "^6.0.0", 674 + "process-warning": "^3.0.0", 675 + "quick-format-unescaped": "^4.0.3", 676 + "real-require": "^0.2.0", 677 + "safe-stable-stringify": "^2.3.1", 678 + "sonic-boom": "^3.7.0", 679 + "thread-stream": "^2.6.0" 680 + }, 681 + "bin": { 682 + "pino": "bin.js" 683 + } 684 + }, 685 + "node_modules/pino-abstract-transport": { 686 + "version": "1.2.0", 687 + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", 688 + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", 689 + "license": "MIT", 690 + "dependencies": { 691 + "readable-stream": "^4.0.0", 692 + "split2": "^4.0.0" 693 + } 694 + }, 695 + "node_modules/pino-std-serializers": { 696 + "version": "6.2.2", 697 + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", 698 + "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==", 699 + "license": "MIT" 700 + }, 701 + "node_modules/prettier": { 702 + "version": "3.8.1", 703 + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", 704 + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", 705 + "license": "MIT", 706 + "bin": { 707 + "prettier": "bin/prettier.cjs" 708 + }, 709 + "engines": { 710 + "node": ">=14" 711 + }, 712 + "funding": { 713 + "url": "https://github.com/prettier/prettier?sponsor=1" 714 + } 715 + }, 716 + "node_modules/process": { 717 + "version": "0.11.10", 718 + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", 719 + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", 720 + "license": "MIT", 721 + "engines": { 722 + "node": ">= 0.6.0" 723 + } 724 + }, 725 + "node_modules/process-warning": { 726 + "version": "3.0.0", 727 + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", 728 + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", 729 + "license": "MIT" 730 + }, 731 + "node_modules/quick-format-unescaped": { 732 + "version": "4.0.4", 733 + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", 734 + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", 735 + "license": "MIT" 736 + }, 737 + "node_modules/readable-stream": { 738 + "version": "4.7.0", 739 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", 740 + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", 741 + "license": "MIT", 742 + "dependencies": { 743 + "abort-controller": "^3.0.0", 744 + "buffer": "^6.0.3", 745 + "events": "^3.3.0", 746 + "process": "^0.11.10", 747 + "string_decoder": "^1.3.0" 748 + }, 749 + "engines": { 750 + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 751 + } 752 + }, 753 + "node_modules/real-require": { 754 + "version": "0.2.0", 755 + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", 756 + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", 757 + "license": "MIT", 758 + "engines": { 759 + "node": ">= 12.13.0" 760 + } 761 + }, 762 + "node_modules/require-directory": { 763 + "version": "2.1.1", 764 + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 765 + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 766 + "license": "MIT", 767 + "engines": { 768 + "node": ">=0.10.0" 769 + } 770 + }, 771 + "node_modules/safe-buffer": { 772 + "version": "5.2.1", 773 + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 774 + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 775 + "funding": [ 776 + { 777 + "type": "github", 778 + "url": "https://github.com/sponsors/feross" 779 + }, 780 + { 781 + "type": "patreon", 782 + "url": "https://www.patreon.com/feross" 783 + }, 784 + { 785 + "type": "consulting", 786 + "url": "https://feross.org/support" 787 + } 788 + ], 789 + "license": "MIT" 790 + }, 791 + "node_modules/safe-stable-stringify": { 792 + "version": "2.5.0", 793 + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", 794 + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", 795 + "license": "MIT", 796 + "engines": { 797 + "node": ">=10" 798 + } 799 + }, 800 + "node_modules/sonic-boom": { 801 + "version": "3.8.1", 802 + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", 803 + "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==", 804 + "license": "MIT", 805 + "dependencies": { 806 + "atomic-sleep": "^1.0.0" 807 + } 808 + }, 809 + "node_modules/split2": { 810 + "version": "4.2.0", 811 + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", 812 + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", 813 + "license": "ISC", 814 + "engines": { 815 + "node": ">= 10.x" 816 + } 817 + }, 818 + "node_modules/string_decoder": { 819 + "version": "1.3.0", 820 + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 821 + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 822 + "license": "MIT", 823 + "dependencies": { 824 + "safe-buffer": "~5.2.0" 825 + } 826 + }, 827 + "node_modules/string-width": { 828 + "version": "4.2.3", 829 + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 830 + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 831 + "license": "MIT", 832 + "dependencies": { 833 + "emoji-regex": "^8.0.0", 834 + "is-fullwidth-code-point": "^3.0.0", 835 + "strip-ansi": "^6.0.1" 836 + }, 837 + "engines": { 838 + "node": ">=8" 839 + } 840 + }, 841 + "node_modules/strip-ansi": { 842 + "version": "6.0.1", 843 + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 844 + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 845 + "license": "MIT", 846 + "dependencies": { 847 + "ansi-regex": "^5.0.1" 848 + }, 849 + "engines": { 850 + "node": ">=8" 851 + } 852 + }, 853 + "node_modules/thread-stream": { 854 + "version": "2.7.0", 855 + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", 856 + "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==", 857 + "license": "MIT", 858 + "dependencies": { 859 + "real-require": "^0.2.0" 860 + } 861 + }, 862 + "node_modules/tinyglobby": { 863 + "version": "0.2.15", 864 + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", 865 + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", 866 + "license": "MIT", 867 + "dependencies": { 868 + "fdir": "^6.5.0", 869 + "picomatch": "^4.0.3" 870 + }, 871 + "engines": { 872 + "node": ">=12.0.0" 873 + }, 874 + "funding": { 875 + "url": "https://github.com/sponsors/SuperchupuDev" 876 + } 877 + }, 878 + "node_modules/ts-morph": { 879 + "version": "27.0.2", 880 + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-27.0.2.tgz", 881 + "integrity": "sha512-fhUhgeljcrdZ+9DZND1De1029PrE+cMkIP7ooqkLRTrRLTqcki2AstsyJm0vRNbTbVCNJ0idGlbBrfqc7/nA8w==", 882 + "license": "MIT", 883 + "dependencies": { 884 + "@ts-morph/common": "~0.28.1", 885 + "code-block-writer": "^13.0.3" 886 + } 887 + }, 888 + "node_modules/tslib": { 889 + "version": "2.8.1", 890 + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 891 + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 892 + "license": "0BSD" 893 + }, 894 + "node_modules/typescript": { 895 + "version": "5.9.3", 896 + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", 897 + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", 898 + "dev": true, 899 + "license": "Apache-2.0", 900 + "bin": { 901 + "tsc": "bin/tsc", 902 + "tsserver": "bin/tsserver" 903 + }, 904 + "engines": { 905 + "node": ">=14.17" 906 + } 907 + }, 908 + "node_modules/uint8arrays": { 909 + "version": "3.0.0", 910 + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz", 911 + "integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==", 912 + "license": "MIT", 913 + "dependencies": { 914 + "multiformats": "^9.4.2" 915 + } 916 + }, 917 + "node_modules/unicode-segmenter": { 918 + "version": "0.14.5", 919 + "resolved": "https://registry.npmjs.org/unicode-segmenter/-/unicode-segmenter-0.14.5.tgz", 920 + "integrity": "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==", 921 + "license": "MIT" 922 + }, 923 + "node_modules/varint": { 924 + "version": "6.0.0", 925 + "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", 926 + "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", 927 + "license": "MIT" 928 + }, 929 + "node_modules/wrap-ansi": { 930 + "version": "7.0.0", 931 + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 932 + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 933 + "license": "MIT", 934 + "dependencies": { 935 + "ansi-styles": "^4.0.0", 936 + "string-width": "^4.1.0", 937 + "strip-ansi": "^6.0.0" 938 + }, 939 + "engines": { 940 + "node": ">=10" 941 + }, 942 + "funding": { 943 + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 944 + } 945 + }, 946 + "node_modules/y18n": { 947 + "version": "5.0.8", 948 + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 949 + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 950 + "license": "ISC", 951 + "engines": { 952 + "node": ">=10" 953 + } 954 + }, 955 + "node_modules/yargs": { 956 + "version": "17.7.2", 957 + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", 958 + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", 959 + "license": "MIT", 960 + "dependencies": { 961 + "cliui": "^8.0.1", 962 + "escalade": "^3.1.1", 963 + "get-caller-file": "^2.0.5", 964 + "require-directory": "^2.1.1", 965 + "string-width": "^4.2.3", 966 + "y18n": "^5.0.5", 967 + "yargs-parser": "^21.1.1" 968 + }, 969 + "engines": { 970 + "node": ">=12" 971 + } 972 + }, 973 + "node_modules/yargs-parser": { 974 + "version": "21.1.1", 975 + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", 976 + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", 977 + "license": "ISC", 978 + "engines": { 979 + "node": ">=12" 980 + } 981 + }, 982 + "node_modules/zod": { 983 + "version": "3.25.76", 984 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 985 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 986 + "license": "MIT", 987 + "funding": { 988 + "url": "https://github.com/sponsors/colinhacks" 989 + } 990 + } 991 + } 992 + }
+42
packages/@inlay/core/package.json
··· 1 + { 2 + "name": "@inlay/core", 3 + "version": "0.0.12", 4 + "type": "module", 5 + "author": "Dan Abramov <dan.abramov@gmail.com>", 6 + "license": "MIT", 7 + "main": "./dist/index.js", 8 + "types": "./dist/index.d.ts", 9 + "exports": { 10 + ".": { 11 + "source": "./src/index.ts", 12 + "types": "./dist/index.d.ts", 13 + "import": "./dist/index.js" 14 + }, 15 + "./jsx-runtime": { 16 + "source": "./src/jsx-runtime.ts", 17 + "types": "./dist/jsx-runtime.d.ts", 18 + "import": "./dist/jsx-runtime.js" 19 + }, 20 + "./jsx-dev-runtime": { 21 + "source": "./src/jsx-dev-runtime.ts", 22 + "types": "./dist/jsx-dev-runtime.d.ts", 23 + "import": "./dist/jsx-dev-runtime.js" 24 + } 25 + }, 26 + "files": [ 27 + "dist" 28 + ], 29 + "scripts": { 30 + "build": "tsc", 31 + "dev": "tsc --watch", 32 + "test": "tsx --conditions source --test test/*.test.ts", 33 + "prepublishOnly": "npm run build" 34 + }, 35 + "dependencies": { 36 + "@atproto/lex": "^0.0.18", 37 + "@atproto/syntax": "^0.4.3" 38 + }, 39 + "devDependencies": { 40 + "typescript": "^5.9.0" 41 + } 42 + }
+46
packages/@inlay/core/src/element.ts
··· 1 + import type { l } from "@atproto/lex"; 2 + 3 + export const BRAND = Symbol.for("$"); 4 + 5 + export type Element = { 6 + type: l.NsidString; 7 + key?: string; 8 + props?: l.LexMap; 9 + }; 10 + 11 + export type LexiconComponent = { 12 + readonly $nsid: l.NsidString; 13 + }; 14 + 15 + export function createElement( 16 + type: l.NsidString, 17 + key?: string, 18 + props?: Record<string, unknown> | null 19 + ): Element { 20 + const el: Record<string, unknown> = { $: BRAND, type }; 21 + if (key != null) el.key = key; 22 + if (props != null) el.props = props; 23 + return el as Element; 24 + } 25 + 26 + export function isValidElement(value: unknown): value is Element { 27 + return ( 28 + typeof value === "object" && 29 + value !== null && 30 + (value as { $?: unknown }).$ === BRAND 31 + ); 32 + } 33 + 34 + export function keyStaticChildren<T>(children: T): T { 35 + if (Array.isArray(children)) { 36 + return children.map((child, i) => 37 + isValidElement(child) && child.key == null 38 + ? { ...child, key: String(i) } 39 + : child 40 + ) as T; 41 + } 42 + if (isValidElement(children) && children.key == null) { 43 + return { ...children, key: "0" } as T; 44 + } 45 + return children; 46 + }
+187
packages/@inlay/core/src/index.ts
··· 1 + // Minimal helper to create elements from generated lexicon modules 2 + // 3 + // Usage: 4 + // import { Stack } from "@/generated/org/design"; 5 + // import { Post } from "@/generated/com/example"; 6 + // import { $, serializeTree, deserializeTree } from "@inlay/core"; 7 + // 8 + // $(Stack, { gap: "medium" }, 9 + // $(Post, { uri: 'at://...' }), 10 + // $(Post, { uri: 'at://...' }) 11 + // ) 12 + // 13 + // Serialization: 14 + // serializeTree(tree) 15 + // deserializeTree(tree) 16 + // 17 + // Element types are NSIDs (e.g., "com.example.Post") 18 + 19 + import type { l } from "@atproto/lex"; 20 + 21 + import { ensureValidNsid } from "@atproto/syntax"; 22 + import { 23 + BRAND, 24 + type Element, 25 + type LexiconComponent, 26 + createElement, 27 + keyStaticChildren, 28 + } from "./element.ts"; 29 + 30 + export { type Element, isValidElement } from "./element.ts"; 31 + export { BRAND } from "./element.ts"; 32 + 33 + // --- Base types --- 34 + 35 + type LexiconModule = LexiconComponent & { 36 + readonly main: l.Procedure; 37 + }; 38 + 39 + type Child = Element | string | number | boolean | null | undefined | Child[]; 40 + 41 + // --- JSX support --- 42 + 43 + type Props<M extends LexiconModule> = Omit< 44 + l.InferMethodInputBody<M["main"]>, 45 + "children" 46 + > & { 47 + children?: Child; 48 + }; 49 + 50 + export type Component<M extends LexiconModule> = M & 51 + ((props: Props<M>) => Element); 52 + 53 + export function jslex<T>(namespace: T): { 54 + [K in keyof T]: T[K] extends LexiconModule ? Component<T[K]> : T[K]; 55 + } { 56 + return namespace as any; 57 + } 58 + 59 + // --- $ function --- 60 + 61 + type Config<M extends LexiconModule> = Omit< 62 + l.InferMethodInputBody<M["main"]>, 63 + "children" 64 + > & { key?: string }; 65 + 66 + // Overload: typed lexicon module 67 + export function $<M extends LexiconModule>( 68 + mod: M, 69 + config?: Config<M>, 70 + ...children: Child[] 71 + ): Element; 72 + 73 + // Overload: string NSID (untyped) 74 + export function $( 75 + nsid: string, 76 + config?: l.LexMap & { key?: string }, 77 + ...children: Child[] 78 + ): Element; 79 + 80 + // Implementation 81 + export function $( 82 + modOrNsid: LexiconModule | string, 83 + config?: l.LexMap & { key?: string }, 84 + ...children: Child[] 85 + ): Element { 86 + const nsid = 87 + typeof modOrNsid === "string" 88 + ? (ensureValidNsid(modOrNsid), modOrNsid) 89 + : modOrNsid.$nsid; 90 + const { key, ...rest } = config ?? {}; 91 + const props = 92 + children.length > 0 93 + ? { ...rest, children: keyStaticChildren(children) } 94 + : rest; 95 + return createElement(nsid, key, props); 96 + } 97 + 98 + export function walkTree<T>( 99 + tree: T, 100 + mapObject: ( 101 + obj: Record<string, unknown>, 102 + walk: (v: unknown) => unknown 103 + ) => unknown 104 + ): T { 105 + return walk(tree) as T; 106 + 107 + function walk(value: unknown): unknown { 108 + if (value == null || typeof value !== "object") { 109 + return value; 110 + } 111 + if (Array.isArray(value)) { 112 + return value.map(walk); 113 + } 114 + return mapObject(value as Record<string, unknown>, walk); 115 + } 116 + } 117 + 118 + export type ElementVisitor = (element: Element) => unknown; 119 + 120 + export function serializeTree<T>(tree: T, visitor?: ElementVisitor): T { 121 + return walkTree(tree, (obj, walk) => { 122 + if (visitor && obj.$ === BRAND) { 123 + const result = visitor(obj as Element); 124 + if (result !== obj) { 125 + return walk(result); 126 + } 127 + } 128 + const out: Record<string, unknown> = {}; 129 + for (const [k, v] of Object.entries(obj)) { 130 + if (v === BRAND) { 131 + out[k] = "$"; 132 + } else if (typeof v === "string" && v.startsWith("$")) { 133 + out[k] = "$" + v; 134 + } else { 135 + out[k] = walk(v); 136 + } 137 + } 138 + return out; 139 + }); 140 + } 141 + 142 + export function deserializeTree<T>(tree: T, visitor?: ElementVisitor): T { 143 + return walkTree(tree, (obj, walk) => { 144 + const out: Record<string, unknown> = {}; 145 + for (const [k, v] of Object.entries(obj)) { 146 + if (v === "$") { 147 + out[k] = BRAND; 148 + } else if (typeof v === "string" && v.startsWith("$$")) { 149 + out[k] = v.slice(1); 150 + } else { 151 + out[k] = walk(v); 152 + } 153 + } 154 + if (visitor && out.$ === BRAND) { 155 + const result = visitor(out as Element); 156 + if (result !== out) { 157 + return result; 158 + } 159 + } 160 + return out; 161 + }); 162 + } 163 + 164 + /** 165 + * Walk a value tree resolving Binding elements via a caller-provided resolver. 166 + * Recurses into plain objects and arrays. Non-Binding elements are opaque — 167 + * they are NOT recursed into (their props are rendered separately by renderNode). 168 + */ 169 + export function resolveBindings<T>( 170 + tree: T, 171 + resolve: (path: string[]) => unknown 172 + ): T { 173 + return walkTree(tree, (obj, walk) => { 174 + if (obj.$ === BRAND) { 175 + const el = obj as Element; 176 + if (el.type === "at.inlay.Binding") { 177 + const path = (el.props as Record<string, unknown> | undefined)?.path; 178 + if (!Array.isArray(path)) throw new Error("Binding missing path"); 179 + return resolve(path as string[]); 180 + } 181 + return obj; // non-Binding element: opaque 182 + } 183 + const out: Record<string, unknown> = {}; 184 + for (const [k, v] of Object.entries(obj)) out[k] = walk(v); 185 + return out; 186 + }); 187 + }
+14
packages/@inlay/core/src/jsx-dev-runtime.ts
··· 1 + import type { l } from "@atproto/lex"; 2 + import type { Element, LexiconComponent } from "./element.ts"; 3 + import { jsx, jsxs, Fragment } from "./jsx-runtime.ts"; 4 + 5 + export { Fragment }; 6 + 7 + export function jsxDEV( 8 + type: LexiconComponent, 9 + props: l.LexMap | null, 10 + key?: string, 11 + isStaticChildren?: boolean 12 + ): Element { 13 + return isStaticChildren ? jsxs(type, props, key) : jsx(type, props, key); 14 + }
+48
packages/@inlay/core/src/jsx-runtime.ts
··· 1 + import type { l } from "@atproto/lex"; 2 + import { 3 + type Element, 4 + type LexiconComponent, 5 + createElement, 6 + keyStaticChildren, 7 + } from "./element.ts"; 8 + 9 + // Support both string NSIDs and lexicon objects with $nsid 10 + type ComponentType = LexiconComponent | string; 11 + 12 + function getNsid(type: ComponentType): l.NsidString { 13 + return (typeof type === "string" ? type : type.$nsid) as l.NsidString; 14 + } 15 + 16 + export function jsx( 17 + type: ComponentType, 18 + props: l.LexMap | null, 19 + key?: string 20 + ): Element { 21 + const nsid = getNsid(type); 22 + if (props?.children !== undefined) { 23 + return createElement(nsid, key, { 24 + ...props, 25 + children: Array.isArray(props.children) 26 + ? props.children 27 + : keyStaticChildren([props.children]), 28 + }); 29 + } 30 + return createElement(nsid, key, props); 31 + } 32 + 33 + export function jsxs( 34 + type: ComponentType, 35 + props: l.LexMap | null, 36 + key?: string 37 + ): Element { 38 + const nsid = getNsid(type); 39 + if (props?.children === undefined) { 40 + return createElement(nsid, key, props); 41 + } 42 + return createElement(nsid, key, { 43 + ...props, 44 + children: keyStaticChildren(props.children), 45 + }); 46 + } 47 + 48 + export const Fragment = "at.inlay.Fragment";
+255
packages/@inlay/core/test/index.test.ts
··· 1 + import { describe, it } from "node:test"; 2 + import assert from "node:assert/strict"; 3 + 4 + import { 5 + $, 6 + serializeTree, 7 + deserializeTree, 8 + resolveBindings, 9 + isValidElement, 10 + type Element, 11 + } from "@inlay/core"; 12 + 13 + // --- Branding --- 14 + 15 + describe("branding", () => { 16 + it("only $-created objects are valid elements", () => { 17 + assert.equal(isValidElement($("com.example.Text", { value: "hi" })), true); 18 + assert.equal( 19 + isValidElement({ type: "com.example.Text", props: { value: "hi" } }), 20 + false 21 + ); 22 + }); 23 + 24 + it("primitives and nulls are never elements", () => { 25 + for (const v of [null, undefined, 0, "", false, 42, "hello"]) { 26 + assert.equal(isValidElement(v), false); 27 + } 28 + }); 29 + }); 30 + 31 + // --- Round-trip fidelity --- 32 + 33 + describe("round-trip", () => { 34 + it("serialize then deserialize is identity", () => { 35 + const original = $("com.example.Text", { value: "hello", count: 42 }); 36 + assert.deepEqual(deserializeTree(serializeTree(original)), original); 37 + }); 38 + 39 + it("survives JSON in between", () => { 40 + const original = $( 41 + "com.example.Stack", 42 + {}, 43 + $("com.example.Text", { value: "a" }), 44 + $("com.example.Text", { value: "b" }) 45 + ); 46 + const wire = serializeTree(original); 47 + assert.deepEqual( 48 + deserializeTree(JSON.parse(JSON.stringify(wire))), 49 + original 50 + ); 51 + }); 52 + 53 + it("preserves keys", () => { 54 + const original = $("com.example.Text", { key: "my-key", value: "hi" }); 55 + assert.deepEqual(deserializeTree(serializeTree(original)), original); 56 + }); 57 + 58 + it("handles deep nesting", () => { 59 + let tree = $("com.example.Text", { value: "leaf" }); 60 + for (let i = 0; i < 5; i++) { 61 + tree = $("com.example.Box", {}, tree); 62 + } 63 + assert.deepEqual(deserializeTree(serializeTree(tree)), tree); 64 + }); 65 + 66 + it("elements nested in plain objects survive", () => { 67 + const tree = $("com.example.Card", { 68 + header: { 69 + title: "Hello", 70 + icon: $("com.example.Icon", { name: "star" }), 71 + }, 72 + }); 73 + assert.deepEqual(deserializeTree(serializeTree(tree)), tree); 74 + }); 75 + 76 + it("mixed arrays of elements and primitives survive", () => { 77 + const tree = $("com.example.RichText", { 78 + children: ["Hello ", $("com.example.Bold", { children: "world" }), "!"], 79 + }); 80 + assert.deepEqual(deserializeTree(serializeTree(tree)), tree); 81 + }); 82 + }); 83 + 84 + // --- $ function --- 85 + 86 + describe("$ function", () => { 87 + it("separates key from props", () => { 88 + const node = $("com.example.Text", { key: "k1", value: "hi" }); 89 + assert.equal(node.key, "k1"); 90 + assert.equal((node.props as Record<string, unknown>)?.key, undefined); 91 + assert.equal((node.props as Record<string, unknown>)?.value, "hi"); 92 + }); 93 + 94 + it("auto-keys static children", () => { 95 + const node = $( 96 + "com.example.Stack", 97 + {}, 98 + $("com.example.Text", { value: "a" }), 99 + $("com.example.Text", { value: "b" }) 100 + ); 101 + const children = (node.props as Record<string, unknown>).children as Array<{ 102 + key?: string; 103 + }>; 104 + assert.deepEqual( 105 + children.map((c) => c.key), 106 + ["0", "1"] 107 + ); 108 + }); 109 + 110 + it("rejects invalid NSIDs", () => { 111 + assert.throws(() => $("not-an-nsid", {})); 112 + assert.throws(() => $("", {})); 113 + }); 114 + }); 115 + 116 + // --- Binding resolution --- 117 + 118 + // Simple scope lookup for tests — optional chaining, returns undefined on miss 119 + function resolvePath(scope: Record<string, unknown>, path: string[]): unknown { 120 + let current: unknown = scope; 121 + for (const seg of path) { 122 + if (current == null || typeof current !== "object") return undefined; 123 + current = (current as Record<string, unknown>)[seg]; 124 + } 125 + return current; 126 + } 127 + 128 + // Test resolver: looks up path in scope, throws on miss 129 + function scopeResolver(scope: Record<string, unknown>) { 130 + return (path: string[]) => { 131 + const value = resolvePath(scope, path); 132 + if (value === undefined) { 133 + throw new Error(`MISSING:${path.join(".")}`); 134 + } 135 + return value; 136 + }; 137 + } 138 + 139 + describe("resolveBindings", () => { 140 + // resolveBindings operates on props (plain objects), not on elements directly. 141 + // Elements are opaque — it won't recurse into their props. 142 + 143 + it("resolves binding in prop", () => { 144 + const props = { 145 + value: $("at.inlay.Binding", { path: ["reply", "text"] }), 146 + }; 147 + assert.deepEqual( 148 + resolveBindings(props, scopeResolver({ reply: { text: "Hello" } })), 149 + { value: "Hello" } 150 + ); 151 + }); 152 + 153 + it("resolves binding nested in plain object", () => { 154 + const props = { 155 + heading: { 156 + caption: $("at.inlay.Binding", { path: ["reply", "text"] }), 157 + }, 158 + }; 159 + assert.deepEqual( 160 + resolveBindings(props, scopeResolver({ reply: { text: "Hello" } })), 161 + { heading: { caption: "Hello" } } 162 + ); 163 + }); 164 + 165 + it("resolves binding in array", () => { 166 + const props = { 167 + items: [$("at.inlay.Binding", { path: ["user", "name"] }), "literal"], 168 + }; 169 + assert.deepEqual( 170 + resolveBindings(props, scopeResolver({ user: { name: "Dan" } })), 171 + { items: ["Dan", "literal"] } 172 + ); 173 + }); 174 + 175 + it("non-Binding elements are opaque — bindings inside stay unresolved", () => { 176 + const binding = $("at.inlay.Binding", { path: ["reply", "text"] }); 177 + const text = $("com.example.Text", { value: binding }); 178 + const props = { children: [text] }; 179 + const result = resolveBindings( 180 + props, 181 + scopeResolver({ reply: { text: "Hello" } }) 182 + ) as Record<string, unknown>; 183 + // Text element passed through opaque — binding inside is untouched 184 + const children = result.children as Element[]; 185 + const textProps = children[0].props as Record<string, unknown>; 186 + assert.ok( 187 + isValidElement(textProps.value), 188 + "binding inside Text is still an element" 189 + ); 190 + assert.equal((textProps.value as Element).type, "at.inlay.Binding"); 191 + }); 192 + 193 + it("resolver controls missing behavior", () => { 194 + const props = { 195 + value: $("at.inlay.Binding", { path: ["nonexistent"] }), 196 + }; 197 + assert.throws( 198 + () => resolveBindings(props, scopeResolver({})), 199 + /MISSING:nonexistent/ 200 + ); 201 + }); 202 + 203 + it("resolver can return sentinel for missing", () => { 204 + const MISSING = Symbol("missing"); 205 + const props = { 206 + value: $("at.inlay.Binding", { path: ["nope"] }), 207 + }; 208 + const result = resolveBindings(props, (path) => { 209 + const v = resolvePath({}, path); 210 + return v === undefined ? MISSING : v; 211 + }) as Record<string, unknown>; 212 + assert.equal(result.value, MISSING); 213 + }); 214 + 215 + it("preserves null scope values", () => { 216 + const props = { 217 + value: $("at.inlay.Binding", { path: ["nothing"] }), 218 + }; 219 + assert.deepEqual( 220 + resolveBindings(props, (path) => resolvePath({ nothing: null }, path)), 221 + { value: null } 222 + ); 223 + }); 224 + 225 + it("preserves falsy scope values", () => { 226 + const scope = { zero: 0, no: false, empty: "" }; 227 + const props = { 228 + a: $("at.inlay.Binding", { path: ["zero"] }), 229 + b: $("at.inlay.Binding", { path: ["no"] }), 230 + c: $("at.inlay.Binding", { path: ["empty"] }), 231 + }; 232 + assert.deepEqual( 233 + resolveBindings(props, (path) => resolvePath(scope, path)), 234 + { a: 0, b: false, c: "" } 235 + ); 236 + }); 237 + 238 + it("no bindings — identity", () => { 239 + const props = { title: "hello", count: 42 }; 240 + const result = resolveBindings(props, () => { 241 + throw new Error("should not be called"); 242 + }); 243 + assert.deepEqual(result, props); 244 + }); 245 + 246 + it("throws on Binding missing path", () => { 247 + const props = { 248 + value: $("at.inlay.Binding", {}), 249 + }; 250 + assert.throws( 251 + () => resolveBindings(props, () => "x"), 252 + /Binding missing path/ 253 + ); 254 + }); 255 + });
+355
packages/@inlay/core/test/xrpc.test.ts
··· 1 + import { describe, it } from "node:test"; 2 + import assert from "node:assert/strict"; 3 + import type { l } from "@atproto/lex"; 4 + 5 + import { 6 + $, 7 + serializeTree, 8 + deserializeTree, 9 + resolveBindings, 10 + type Element, 11 + } from "../src/index.ts"; 12 + 13 + // Props from deserialized data are untyped — cast when passing back through $. 14 + type Props = l.LexMap & { key?: string }; 15 + 16 + function xrpc( 17 + tree: Element, 18 + component: (input: Record<string, unknown>) => Element 19 + ): Element { 20 + const props = (tree.props ?? {}) as Record<string, unknown>; 21 + 22 + const refs = new Map<string, Element>(); 23 + const refSlots = new Set<object>(); 24 + const requestBody = JSON.stringify( 25 + serializeTree(props, (el) => { 26 + if (el.type === "at.inlay.Slot") { 27 + if (refSlots.has(el as Element)) return el; 28 + throw new Error("Unexpected Slot in props"); 29 + } 30 + const id = String(refs.size); 31 + refs.set(id, el as Element); 32 + const slot = $("at.inlay.Slot", { id }); 33 + refSlots.add(slot); 34 + return slot; 35 + }) 36 + ); 37 + 38 + const slots = new Set<object>(); 39 + const input = deserializeTree(JSON.parse(requestBody), (el) => { 40 + if (el.type === "at.inlay.Slot" && el.props) { 41 + slots.add(el.props); 42 + return el; 43 + } 44 + throw new Error(`Unexpected element in component input: ${el.type}`); 45 + }) as Record<string, unknown>; 46 + 47 + const response = component(input); 48 + 49 + const responseBody = JSON.stringify( 50 + serializeTree(response, (el) => { 51 + if (el.type === "at.inlay.Slot") { 52 + if (!el.props || !slots.has(el.props)) { 53 + throw new Error(`Forged slot in component output`); 54 + } 55 + } 56 + return el; 57 + }) 58 + ); 59 + 60 + return deserializeTree(JSON.parse(responseBody), (el) => { 61 + if (el.type === "at.inlay.Slot" && el.props) { 62 + const id = (el.props as Record<string, unknown>).id as string; 63 + const stashed = refs.get(id); 64 + if (stashed) return stashed; 65 + throw new Error(`Unknown slot id: ${id}`); 66 + } 67 + return el; 68 + }) as Element; 69 + } 70 + 71 + describe("XRPC flow", () => { 72 + it("children round-trip through full flow", () => { 73 + const tree = $( 74 + "com.example.Card", 75 + {}, 76 + $("com.example.Text", { value: "hi" }) 77 + ); 78 + assert.deepEqual( 79 + xrpc(tree, (input) => 80 + $("com.example.Box", { children: input.children } as Props) 81 + ), 82 + $("com.example.Box", {}, $("com.example.Text", { key: "0", value: "hi" })) 83 + ); 84 + }); 85 + 86 + it("component can reorder children", () => { 87 + const tree = $( 88 + "com.example.Layout", 89 + {}, 90 + $("com.example.Header", { title: "Hi" }), 91 + $("com.example.Footer", { year: 2025 }) 92 + ); 93 + assert.deepEqual( 94 + xrpc(tree, (input) => { 95 + const [a, b] = input.children as unknown[]; 96 + return $("com.example.Box", { children: [b, a] } as Props); 97 + }), 98 + $( 99 + "com.example.Box", 100 + {}, 101 + $("com.example.Footer", { key: "1", year: 2025 }), 102 + $("com.example.Header", { key: "0", title: "Hi" }) 103 + ) 104 + ); 105 + }); 106 + 107 + it("component can duplicate a child", () => { 108 + const child = $("com.example.Text", { key: "0", value: "hi" }); 109 + const tree = $( 110 + "com.example.Card", 111 + {}, 112 + $("com.example.Text", { value: "hi" }) 113 + ); 114 + assert.deepEqual( 115 + xrpc(tree, (input) => { 116 + const c = (input.children as unknown[])[0]; 117 + return $("com.example.Box", { children: [c, c] } as Props); 118 + }), 119 + $("com.example.Box", {}, child, child) 120 + ); 121 + }); 122 + 123 + it("component can drop children", () => { 124 + const tree = $( 125 + "com.example.Layout", 126 + {}, 127 + $("com.example.Header"), 128 + $("com.example.Footer") 129 + ); 130 + assert.deepEqual( 131 + xrpc(tree, () => $("com.example.Box", { label: "empty" })), 132 + $("com.example.Box", { label: "empty" }) 133 + ); 134 + }); 135 + 136 + it("elements nested in plain object props round-trip", () => { 137 + const tree = $("com.example.Card", { 138 + header: { icon: $("com.example.Icon", { name: "star" }) }, 139 + }); 140 + assert.deepEqual( 141 + xrpc(tree, (input) => { 142 + const icon = (input.header as Record<string, unknown>).icon; 143 + return $("com.example.Box", { content: icon } as Props); 144 + }), 145 + $("com.example.Box", { 146 + content: $("com.example.Icon", { name: "star" }), 147 + }) 148 + ); 149 + }); 150 + 151 + it("primitive-only props pass through unchanged", () => { 152 + const tree = $("com.example.Spacer", { 153 + gap: "medium", 154 + count: 42, 155 + flag: true, 156 + }); 157 + assert.deepEqual( 158 + xrpc(tree, (input) => $("com.example.Box", input as Props)), 159 + $("com.example.Box", { gap: "medium", count: 42, flag: true }) 160 + ); 161 + }); 162 + 163 + it("data with $ keys survives full pipeline", () => { 164 + const tree = $("com.example.Card", { 165 + a: { $: "$", type: "app.bsky.feed.post" }, 166 + b: { $: "$weird$value" }, 167 + c: { $: "$$double" }, 168 + d: { $: "hello" }, 169 + }); 170 + assert.deepEqual( 171 + xrpc(tree, (input) => $("com.example.Box", input as Props)), 172 + $("com.example.Box", { 173 + a: { $: "$", type: "app.bsky.feed.post" }, 174 + b: { $: "$weird$value" }, 175 + c: { $: "$$double" }, 176 + d: { $: "hello" }, 177 + }) 178 + ); 179 + }); 180 + 181 + it("rejects forged slots even with valid-looking id", () => { 182 + const tree = $( 183 + "com.example.Card", 184 + {}, 185 + $("com.example.Text", { value: "hi" }) 186 + ); 187 + assert.throws( 188 + () => 189 + xrpc(tree, () => 190 + $("com.example.Box", { 191 + stolen: $("at.inlay.Slot", { id: "0" }), 192 + }) 193 + ), 194 + /slot/i 195 + ); 196 + }); 197 + 198 + it("named element props round-trip through XRPC", () => { 199 + const left = $("com.example.Text", { value: "hello" }); 200 + const right = $("com.example.Text", { value: "world" }); 201 + const tree = $("com.example.LeftRight", { left, right }); 202 + 203 + assert.deepEqual( 204 + xrpc(tree, (input) => 205 + $( 206 + "com.example.Row", 207 + { gap: "medium" }, 208 + $("com.example.Stack", {} as Props, input.left as unknown as Element), 209 + $("com.example.Stack", {} as Props, input.right as unknown as Element) 210 + ) 211 + ), 212 + $( 213 + "com.example.Row", 214 + { gap: "medium" }, 215 + $("com.example.Stack", { children: [left] } as Props), 216 + $("com.example.Stack", { children: [right] } as Props) 217 + ) 218 + ); 219 + }); 220 + 221 + it("same element passed to multiple props gets separate slots", () => { 222 + const foo = $("com.example.Text", { value: "shared" }); 223 + const tree = $("com.example.LeftRight", { left: foo, right: foo }); 224 + 225 + assert.deepEqual( 226 + xrpc(tree, (input) => 227 + $("com.example.Box", { 228 + first: input.left, 229 + second: input.right, 230 + } as Props) 231 + ), 232 + $("com.example.Box", { first: foo, second: foo }) 233 + ); 234 + }); 235 + 236 + it("component can return same slot multiple times", () => { 237 + const child = $("com.example.Text", { key: "0", value: "once" }); 238 + const tree = $( 239 + "com.example.Wrapper", 240 + {}, 241 + $("com.example.Text", { value: "once" }) 242 + ); 243 + 244 + assert.deepEqual( 245 + xrpc(tree, (input) => { 246 + const c = (input.children as unknown[])[0]; 247 + return $("com.example.Stack", { children: [c, c] } as Props); 248 + }), 249 + $("com.example.Stack", {}, child, child) 250 + ); 251 + }); 252 + 253 + it("nested XRPC calls produce independent refs", () => { 254 + const tree = $( 255 + "com.example.Outer", 256 + {}, 257 + $("com.example.Inner", {}, $("com.example.Text", { value: "leaf" })) 258 + ); 259 + 260 + const afterOuter = xrpc(tree, (input) => 261 + $("com.example.Box", { children: input.children } as Props) 262 + ); 263 + assert.deepEqual( 264 + afterOuter, 265 + $( 266 + "com.example.Box", 267 + {}, 268 + $( 269 + "com.example.Inner", 270 + { key: "0" }, 271 + $("com.example.Text", { key: "0", value: "leaf" }) 272 + ) 273 + ) 274 + ); 275 + 276 + const inner = ( 277 + (afterOuter.props as Record<string, unknown>).children as Element[] 278 + )[0]; 279 + assert.deepEqual( 280 + xrpc(inner, (input) => 281 + $("com.example.Wrapper", { children: input.children } as Props) 282 + ), 283 + $( 284 + "com.example.Wrapper", 285 + {}, 286 + $("com.example.Text", { key: "0", value: "leaf" }) 287 + ) 288 + ); 289 + }); 290 + }); 291 + 292 + // --- Full pipeline: resolve bindings → XRPC --- 293 + 294 + function resolvePath(scope: Record<string, unknown>, path: string[]): unknown { 295 + let current: unknown = scope; 296 + for (const seg of path) { 297 + if (current == null || typeof current !== "object") return undefined; 298 + current = (current as Record<string, unknown>)[seg]; 299 + } 300 + return current; 301 + } 302 + 303 + describe("resolve → XRPC pipeline", () => { 304 + it("bindings in props resolve before XRPC", () => { 305 + const scope = { user: { name: "Dan" } }; 306 + const props = { 307 + title: $("at.inlay.Binding", { path: ["user", "name"] }), 308 + }; 309 + 310 + // Step 1: resolve bindings in props 311 + const resolvedProps = resolveBindings(props, (path) => { 312 + const v = resolvePath(scope, path); 313 + if (v === undefined) throw new Error(`MISSING:${path.join(".")}`); 314 + return v; 315 + }) as Record<string, unknown>; 316 + 317 + // Step 2: XRPC with resolved props 318 + const tree = $("com.example.Card", resolvedProps as Props); 319 + const result = xrpc(tree, (input) => 320 + $("com.example.Box", { label: input.title } as Props) 321 + ); 322 + 323 + assert.deepEqual(result, $("com.example.Box", { label: "Dan" })); 324 + }); 325 + 326 + it("bindings in children resolve, child elements stash as slots", () => { 327 + const scope = { greeting: "Hello" }; 328 + const tree = $( 329 + "com.example.Card", 330 + {}, 331 + $("com.example.Text", { 332 + value: $("at.inlay.Binding", { path: ["greeting"] }), 333 + }) 334 + ); 335 + 336 + // resolveBindings is opaque to non-Binding elements — 337 + // the Binding inside Text's props is NOT resolved here. 338 + // In the real pipeline, host components resolve their own props. 339 + // For XRPC, the caller resolves ALL bindings before sending. 340 + const props = (tree.props ?? {}) as Record<string, unknown>; 341 + const resolved = resolveBindings(props, (path) => { 342 + const v = resolvePath(scope, path); 343 + if (v === undefined) throw new Error(`MISSING:${path.join(".")}`); 344 + return v; 345 + }) as Record<string, unknown>; 346 + 347 + // The Text element (with its Binding) passes through opaque 348 + const children = resolved.children as Element[]; 349 + const textValue = (children[0].props as Record<string, unknown>).value; 350 + assert.ok( 351 + typeof textValue === "object" && textValue !== null, 352 + "Binding inside Text is still an element (opaque)" 353 + ); 354 + }); 355 + });
+16
packages/@inlay/core/tsconfig.json
··· 1 + { 2 + "compilerOptions": { 3 + "target": "ES2022", 4 + "module": "NodeNext", 5 + "moduleResolution": "NodeNext", 6 + "declaration": true, 7 + "declarationMap": true, 8 + "outDir": "./dist", 9 + "rootDir": "./src", 10 + "strict": true, 11 + "skipLibCheck": true, 12 + "esModuleInterop": true, 13 + "rewriteRelativeImportExtensions": true 14 + }, 15 + "include": ["src"] 16 + }
+1046
packages/@inlay/render/package-lock.json
··· 1 + { 2 + "name": "@inlay/render", 3 + "version": "0.0.1", 4 + "lockfileVersion": 3, 5 + "requires": true, 6 + "packages": { 7 + "": { 8 + "name": "@inlay/render", 9 + "version": "0.0.1", 10 + "dependencies": { 11 + "@atproto/lexicon": "^0.6.1", 12 + "@atproto/syntax": "^0.4.3", 13 + "@inlay/core": "file:../../../../../../../tmp/inlay-core-0.0.12.tgz" 14 + }, 15 + "devDependencies": { 16 + "typescript": "^5.9.0" 17 + } 18 + }, 19 + "node_modules/@atproto-labs/did-resolver": { 20 + "version": "0.2.5", 21 + "resolved": "https://registry.npmjs.org/@atproto-labs/did-resolver/-/did-resolver-0.2.5.tgz", 22 + "integrity": "sha512-he7EC6OMSifNs01a4RT9mta/yYitoKDzlK9ty2TFV5Uj/+HpB4vYMRdIDFrRW0Hcsehy90E2t/dw0t7361MEKQ==", 23 + "license": "MIT", 24 + "dependencies": { 25 + "@atproto-labs/fetch": "0.2.3", 26 + "@atproto-labs/pipe": "0.1.1", 27 + "@atproto-labs/simple-store": "0.3.0", 28 + "@atproto-labs/simple-store-memory": "0.1.4", 29 + "@atproto/did": "0.2.4", 30 + "zod": "^3.23.8" 31 + } 32 + }, 33 + "node_modules/@atproto-labs/fetch": { 34 + "version": "0.2.3", 35 + "resolved": "https://registry.npmjs.org/@atproto-labs/fetch/-/fetch-0.2.3.tgz", 36 + "integrity": "sha512-NZtbJOCbxKUFRFKMpamT38PUQMY0hX0p7TG5AEYOPhZKZEP7dHZ1K2s1aB8MdVH0qxmqX7nQleNrrvLf09Zfdw==", 37 + "license": "MIT", 38 + "dependencies": { 39 + "@atproto-labs/pipe": "0.1.1" 40 + } 41 + }, 42 + "node_modules/@atproto-labs/pipe": { 43 + "version": "0.1.1", 44 + "resolved": "https://registry.npmjs.org/@atproto-labs/pipe/-/pipe-0.1.1.tgz", 45 + "integrity": "sha512-hdNw2oUs2B6BN1lp+32pF7cp8EMKuIN5Qok2Vvv/aOpG/3tNSJ9YkvfI0k6Zd188LeDDYRUpYpxcoFIcGH/FNg==", 46 + "license": "MIT" 47 + }, 48 + "node_modules/@atproto-labs/simple-store": { 49 + "version": "0.3.0", 50 + "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store/-/simple-store-0.3.0.tgz", 51 + "integrity": "sha512-nOb6ONKBRJHRlukW1sVawUkBqReLlLx6hT35VS3imaNPwiXDxLnTK7lxw3Lrl9k5yugSBDQAkZAq3MPTEFSUBQ==", 52 + "license": "MIT" 53 + }, 54 + "node_modules/@atproto-labs/simple-store-memory": { 55 + "version": "0.1.4", 56 + "resolved": "https://registry.npmjs.org/@atproto-labs/simple-store-memory/-/simple-store-memory-0.1.4.tgz", 57 + "integrity": "sha512-3mKY4dP8I7yKPFj9VKpYyCRzGJOi5CEpOLPlRhoJyLmgs3J4RzDrjn323Oakjz2Aj2JzRU/AIvWRAZVhpYNJHw==", 58 + "license": "MIT", 59 + "dependencies": { 60 + "@atproto-labs/simple-store": "0.3.0", 61 + "lru-cache": "^10.2.0" 62 + } 63 + }, 64 + "node_modules/@atproto/common": { 65 + "version": "0.5.13", 66 + "resolved": "https://registry.npmjs.org/@atproto/common/-/common-0.5.13.tgz", 67 + "integrity": "sha512-+5c3wlvZVCCReoPSwvkPhKz3Y2FZlJzm69BrfkHMccAH0Rs0KLwnWtoE34zyzTLNH7hhy5okx5qJS6+ZlgE9Sg==", 68 + "license": "MIT", 69 + "dependencies": { 70 + "@atproto/common-web": "^0.4.17", 71 + "@atproto/lex-cbor": "^0.0.13", 72 + "@atproto/lex-data": "^0.0.12", 73 + "iso-datestring-validator": "^2.2.2", 74 + "multiformats": "^9.9.0", 75 + "pino": "^8.21.0" 76 + }, 77 + "engines": { 78 + "node": ">=18.7.0" 79 + } 80 + }, 81 + "node_modules/@atproto/common-web": { 82 + "version": "0.4.17", 83 + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.17.tgz", 84 + "integrity": "sha512-sfxD8NGxyoxhxmM9EUshEFbWcJ3+JHEOZF4Quk6HsCh1UxpHBmLabT/vEsAkDWl+C/8U0ine0+c/gHyE/OZiQQ==", 85 + "license": "MIT", 86 + "dependencies": { 87 + "@atproto/lex-data": "^0.0.12", 88 + "@atproto/lex-json": "^0.0.12", 89 + "@atproto/syntax": "^0.4.3", 90 + "zod": "^3.23.8" 91 + } 92 + }, 93 + "node_modules/@atproto/common-web/node_modules/@atproto/lex-data": { 94 + "version": "0.0.12", 95 + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.12.tgz", 96 + "integrity": "sha512-aekJudcK1p6sbTqUv2bJMJBAGZaOJS0mgDclpK3U6VuBREK/au4B6ffunBFWgrDfg0Vwj2JGyEA7E51WZkJcRw==", 97 + "license": "MIT", 98 + "dependencies": { 99 + "multiformats": "^9.9.0", 100 + "tslib": "^2.8.1", 101 + "uint8arrays": "3.0.0", 102 + "unicode-segmenter": "^0.14.0" 103 + } 104 + }, 105 + "node_modules/@atproto/common-web/node_modules/@atproto/lex-json": { 106 + "version": "0.0.12", 107 + "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.12.tgz", 108 + "integrity": "sha512-XlEpnWWZdDJ5BIgG25GyH+6iBfyrFL18BI5JSE6rUfMObbFMrQRaCuRLQfryRXNysVz3L3U+Qb9y8KcXbE8AcA==", 109 + "license": "MIT", 110 + "dependencies": { 111 + "@atproto/lex-data": "^0.0.12", 112 + "tslib": "^2.8.1" 113 + } 114 + }, 115 + "node_modules/@atproto/common/node_modules/@atproto/lex-cbor": { 116 + "version": "0.0.13", 117 + "resolved": "https://registry.npmjs.org/@atproto/lex-cbor/-/lex-cbor-0.0.13.tgz", 118 + "integrity": "sha512-63nbzXJnQwV02XGpEa8WZxt7Zu87dnbzrUVL0Mqr55S1EGCzEF9U7Dauc9tKKLoZ88GmYrJN0irBsXtSi0VeWg==", 119 + "license": "MIT", 120 + "dependencies": { 121 + "@atproto/lex-data": "^0.0.12", 122 + "tslib": "^2.8.1" 123 + } 124 + }, 125 + "node_modules/@atproto/common/node_modules/@atproto/lex-data": { 126 + "version": "0.0.12", 127 + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.12.tgz", 128 + "integrity": "sha512-aekJudcK1p6sbTqUv2bJMJBAGZaOJS0mgDclpK3U6VuBREK/au4B6ffunBFWgrDfg0Vwj2JGyEA7E51WZkJcRw==", 129 + "license": "MIT", 130 + "dependencies": { 131 + "multiformats": "^9.9.0", 132 + "tslib": "^2.8.1", 133 + "uint8arrays": "3.0.0", 134 + "unicode-segmenter": "^0.14.0" 135 + } 136 + }, 137 + "node_modules/@atproto/crypto": { 138 + "version": "0.4.5", 139 + "resolved": "https://registry.npmjs.org/@atproto/crypto/-/crypto-0.4.5.tgz", 140 + "integrity": "sha512-n40aKkMoCatP0u9Yvhrdk6fXyOHFDDbkdm4h4HCyWW+KlKl8iXfD5iV+ECq+w5BM+QH25aIpt3/j6EUNerhLxw==", 141 + "license": "MIT", 142 + "dependencies": { 143 + "@noble/curves": "^1.7.0", 144 + "@noble/hashes": "^1.6.1", 145 + "uint8arrays": "3.0.0" 146 + }, 147 + "engines": { 148 + "node": ">=18.7.0" 149 + } 150 + }, 151 + "node_modules/@atproto/did": { 152 + "version": "0.2.4", 153 + "resolved": "https://registry.npmjs.org/@atproto/did/-/did-0.2.4.tgz", 154 + "integrity": "sha512-nxNiCgXeo7pfjojq9fpfZxCO0X0xUipNVKW+AHNZwQKiUDt6zYL0VXEfm8HBUwQOCmKvj2pRRSM1Cur+tUWk3g==", 155 + "license": "MIT", 156 + "dependencies": { 157 + "zod": "^3.23.8" 158 + } 159 + }, 160 + "node_modules/@atproto/lex": { 161 + "version": "0.0.12", 162 + "resolved": "https://registry.npmjs.org/@atproto/lex/-/lex-0.0.12.tgz", 163 + "integrity": "sha512-aoIRD/5kPbF2SM75i4s5qyfpXB9363OIIskKtTx5k8W4JhbRpW7I9TjfBKXvi63E7tOHH14crq2WgKlI3nipxg==", 164 + "license": "MIT", 165 + "dependencies": { 166 + "@atproto/lex-builder": "0.0.12", 167 + "@atproto/lex-client": "0.0.10", 168 + "@atproto/lex-data": "0.0.9", 169 + "@atproto/lex-installer": "0.0.12", 170 + "@atproto/lex-json": "0.0.9", 171 + "@atproto/lex-schema": "0.0.10", 172 + "tslib": "^2.8.1", 173 + "yargs": "^17.0.0" 174 + }, 175 + "bin": { 176 + "lex": "bin/lex", 177 + "ts-lex": "bin/lex" 178 + } 179 + }, 180 + "node_modules/@atproto/lex-builder": { 181 + "version": "0.0.12", 182 + "resolved": "https://registry.npmjs.org/@atproto/lex-builder/-/lex-builder-0.0.12.tgz", 183 + "integrity": "sha512-ObWnmsbkPwjKKIX/L0JyMptmggr3gvbZKPDcpr1eSBUWyWeqqX8OfIlHYLgm5veNuO776RV05CE7BdQFQUA+9Q==", 184 + "license": "MIT", 185 + "dependencies": { 186 + "@atproto/lex-document": "0.0.11", 187 + "@atproto/lex-schema": "0.0.10", 188 + "prettier": "^3.2.5", 189 + "ts-morph": "^27.0.0", 190 + "tslib": "^2.8.1" 191 + } 192 + }, 193 + "node_modules/@atproto/lex-cbor": { 194 + "version": "0.0.9", 195 + "resolved": "https://registry.npmjs.org/@atproto/lex-cbor/-/lex-cbor-0.0.9.tgz", 196 + "integrity": "sha512-szkS569j1eZsIxZKh2VZHVq7pSpewy1wHh8c6nVYekHfYcJhFkevQq/DjTeatZ7YZKNReGYthQulgaZq2ytfWQ==", 197 + "license": "MIT", 198 + "dependencies": { 199 + "@atproto/lex-data": "0.0.9", 200 + "tslib": "^2.8.1" 201 + } 202 + }, 203 + "node_modules/@atproto/lex-client": { 204 + "version": "0.0.10", 205 + "resolved": "https://registry.npmjs.org/@atproto/lex-client/-/lex-client-0.0.10.tgz", 206 + "integrity": "sha512-n3g9KoY5hw7W29mcR4TrjN5qOi6JiWty7r1heqLLfYiq5TxaRx9/QBa2hbN4h1p4xxICPZoDlNtuGq8YV4U8mg==", 207 + "license": "MIT", 208 + "dependencies": { 209 + "@atproto/lex-data": "0.0.9", 210 + "@atproto/lex-json": "0.0.9", 211 + "@atproto/lex-schema": "0.0.10", 212 + "tslib": "^2.8.1" 213 + } 214 + }, 215 + "node_modules/@atproto/lex-data": { 216 + "version": "0.0.9", 217 + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.9.tgz", 218 + "integrity": "sha512-1slwe4sG0cyWtsq16+rBoWIxNDqGPkkvN+PV6JuzA7dgUK9bjUmXBGQU4eZlUPSS43X1Nhmr/9VjgKmEzU9vDw==", 219 + "license": "MIT", 220 + "dependencies": { 221 + "multiformats": "^9.9.0", 222 + "tslib": "^2.8.1", 223 + "uint8arrays": "3.0.0", 224 + "unicode-segmenter": "^0.14.0" 225 + } 226 + }, 227 + "node_modules/@atproto/lex-document": { 228 + "version": "0.0.11", 229 + "resolved": "https://registry.npmjs.org/@atproto/lex-document/-/lex-document-0.0.11.tgz", 230 + "integrity": "sha512-ePtFOU7yYAp1IL1mPDrAyo+ajN9V7W8z6BY4xXEM/m9U3vCVNC+SIkgkfwumqSUqOtBy4gpz52ppK+R/9S8UWg==", 231 + "license": "MIT", 232 + "dependencies": { 233 + "@atproto/lex-schema": "0.0.10", 234 + "core-js": "^3", 235 + "tslib": "^2.8.1" 236 + } 237 + }, 238 + "node_modules/@atproto/lex-installer": { 239 + "version": "0.0.12", 240 + "resolved": "https://registry.npmjs.org/@atproto/lex-installer/-/lex-installer-0.0.12.tgz", 241 + "integrity": "sha512-Ycgwl2zPbuYhYXoRPDlNgq/WROrg1q3Rz5dOlI/g4x5gh/C63P32qf0+KJbngUqdVaQWj1P4XQAeVne4Gn1+dw==", 242 + "license": "MIT", 243 + "dependencies": { 244 + "@atproto/lex-builder": "0.0.12", 245 + "@atproto/lex-cbor": "0.0.9", 246 + "@atproto/lex-data": "0.0.9", 247 + "@atproto/lex-document": "0.0.11", 248 + "@atproto/lex-resolver": "0.0.11", 249 + "@atproto/lex-schema": "0.0.10", 250 + "@atproto/syntax": "0.4.3", 251 + "tslib": "^2.8.1" 252 + } 253 + }, 254 + "node_modules/@atproto/lex-json": { 255 + "version": "0.0.9", 256 + "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.9.tgz", 257 + "integrity": "sha512-Q2v1EVZcnd+ndyZj1r2UlGikA7q6It24CFPLbxokcf5Ba4RBupH8IkkQX7mqUDSRWPgQdmZYIdW9wUln+MKDqw==", 258 + "license": "MIT", 259 + "dependencies": { 260 + "@atproto/lex-data": "0.0.9", 261 + "tslib": "^2.8.1" 262 + } 263 + }, 264 + "node_modules/@atproto/lex-resolver": { 265 + "version": "0.0.11", 266 + "resolved": "https://registry.npmjs.org/@atproto/lex-resolver/-/lex-resolver-0.0.11.tgz", 267 + "integrity": "sha512-fzrr9W47+xkZDMYjeVG0wSq3mA5d/2MTQknieqeH0hbR+SNwW0PGf/tclH58TrWPEpWCcLY+Yv5ydDGuua8j+Q==", 268 + "license": "MIT", 269 + "dependencies": { 270 + "@atproto-labs/did-resolver": "0.2.5", 271 + "@atproto/crypto": "0.4.5", 272 + "@atproto/lex-client": "0.0.10", 273 + "@atproto/lex-data": "0.0.9", 274 + "@atproto/lex-document": "0.0.11", 275 + "@atproto/lex-schema": "0.0.10", 276 + "@atproto/repo": "0.8.12", 277 + "@atproto/syntax": "0.4.3", 278 + "tslib": "^2.8.1" 279 + } 280 + }, 281 + "node_modules/@atproto/lex-schema": { 282 + "version": "0.0.10", 283 + "resolved": "https://registry.npmjs.org/@atproto/lex-schema/-/lex-schema-0.0.10.tgz", 284 + "integrity": "sha512-970BZVHtsLn03k2wkpYzdY2o/oZycqUReG1UblOkWYkbhQd04WqliiGrpUie/ls25oJs37ymI+fCDPcYg9tuQg==", 285 + "license": "MIT", 286 + "dependencies": { 287 + "@atproto/lex-data": "0.0.9", 288 + "@atproto/syntax": "0.4.3", 289 + "tslib": "^2.8.1" 290 + } 291 + }, 292 + "node_modules/@atproto/lexicon": { 293 + "version": "0.6.1", 294 + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.6.1.tgz", 295 + "integrity": "sha512-/vI1kVlY50Si+5MXpvOucelnYwb0UJ6Qto5mCp+7Q5C+Jtp+SoSykAPVvjVtTnQUH2vrKOFOwpb3C375vSKzXw==", 296 + "license": "MIT", 297 + "dependencies": { 298 + "@atproto/common-web": "^0.4.13", 299 + "@atproto/syntax": "^0.4.3", 300 + "iso-datestring-validator": "^2.2.2", 301 + "multiformats": "^9.9.0", 302 + "zod": "^3.23.8" 303 + } 304 + }, 305 + "node_modules/@atproto/repo": { 306 + "version": "0.8.12", 307 + "resolved": "https://registry.npmjs.org/@atproto/repo/-/repo-0.8.12.tgz", 308 + "integrity": "sha512-QpVTVulgfz5PUiCTELlDBiRvnsnwrFWi+6CfY88VwXzrRHd9NE8GItK7sfxQ6U65vD/idH8ddCgFrlrsn1REPQ==", 309 + "license": "MIT", 310 + "dependencies": { 311 + "@atproto/common": "^0.5.3", 312 + "@atproto/common-web": "^0.4.7", 313 + "@atproto/crypto": "^0.4.5", 314 + "@atproto/lexicon": "^0.6.0", 315 + "@ipld/dag-cbor": "^7.0.0", 316 + "multiformats": "^9.9.0", 317 + "uint8arrays": "3.0.0", 318 + "varint": "^6.0.0", 319 + "zod": "^3.23.8" 320 + }, 321 + "engines": { 322 + "node": ">=18.7.0" 323 + } 324 + }, 325 + "node_modules/@atproto/syntax": { 326 + "version": "0.4.3", 327 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.3.tgz", 328 + "integrity": "sha512-YoZUz40YAJr5nPwvCDWgodEOlt5IftZqPJvA0JDWjuZKD8yXddTwSzXSaKQAzGOpuM+/A3uXRtPzJJqlScc+iA==", 329 + "license": "MIT", 330 + "dependencies": { 331 + "tslib": "^2.8.1" 332 + } 333 + }, 334 + "node_modules/@inlay/core": { 335 + "version": "0.0.12", 336 + "resolved": "file:../../../../../../../tmp/inlay-core-0.0.12.tgz", 337 + "integrity": "sha512-CSAgm+mNLggC8VQuqMJYQ4ExDlH5UwxF6Vdj+8PF1HeyLF1rioePL3la0Mwuh22fO4alSGYO47MzYsoKkqCXXg==", 338 + "license": "MIT", 339 + "dependencies": { 340 + "@atproto/lex": "^0.0.12", 341 + "@atproto/syntax": "^0.4.3" 342 + } 343 + }, 344 + "node_modules/@ipld/dag-cbor": { 345 + "version": "7.0.3", 346 + "resolved": "https://registry.npmjs.org/@ipld/dag-cbor/-/dag-cbor-7.0.3.tgz", 347 + "integrity": "sha512-1VVh2huHsuohdXC1bGJNE8WR72slZ9XE2T3wbBBq31dm7ZBatmKLLxrB+XAqafxfRFjv08RZmj/W/ZqaM13AuA==", 348 + "license": "(Apache-2.0 AND MIT)", 349 + "dependencies": { 350 + "cborg": "^1.6.0", 351 + "multiformats": "^9.5.4" 352 + } 353 + }, 354 + "node_modules/@noble/curves": { 355 + "version": "1.9.7", 356 + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", 357 + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", 358 + "license": "MIT", 359 + "dependencies": { 360 + "@noble/hashes": "1.8.0" 361 + }, 362 + "engines": { 363 + "node": "^14.21.3 || >=16" 364 + }, 365 + "funding": { 366 + "url": "https://paulmillr.com/funding/" 367 + } 368 + }, 369 + "node_modules/@noble/hashes": { 370 + "version": "1.8.0", 371 + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", 372 + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", 373 + "license": "MIT", 374 + "engines": { 375 + "node": "^14.21.3 || >=16" 376 + }, 377 + "funding": { 378 + "url": "https://paulmillr.com/funding/" 379 + } 380 + }, 381 + "node_modules/@ts-morph/common": { 382 + "version": "0.28.1", 383 + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.28.1.tgz", 384 + "integrity": "sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==", 385 + "license": "MIT", 386 + "dependencies": { 387 + "minimatch": "^10.0.1", 388 + "path-browserify": "^1.0.1", 389 + "tinyglobby": "^0.2.14" 390 + } 391 + }, 392 + "node_modules/abort-controller": { 393 + "version": "3.0.0", 394 + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 395 + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 396 + "license": "MIT", 397 + "dependencies": { 398 + "event-target-shim": "^5.0.0" 399 + }, 400 + "engines": { 401 + "node": ">=6.5" 402 + } 403 + }, 404 + "node_modules/ansi-regex": { 405 + "version": "5.0.1", 406 + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 407 + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 408 + "license": "MIT", 409 + "engines": { 410 + "node": ">=8" 411 + } 412 + }, 413 + "node_modules/ansi-styles": { 414 + "version": "4.3.0", 415 + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 416 + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 417 + "license": "MIT", 418 + "dependencies": { 419 + "color-convert": "^2.0.1" 420 + }, 421 + "engines": { 422 + "node": ">=8" 423 + }, 424 + "funding": { 425 + "url": "https://github.com/chalk/ansi-styles?sponsor=1" 426 + } 427 + }, 428 + "node_modules/atomic-sleep": { 429 + "version": "1.0.0", 430 + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", 431 + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", 432 + "license": "MIT", 433 + "engines": { 434 + "node": ">=8.0.0" 435 + } 436 + }, 437 + "node_modules/balanced-match": { 438 + "version": "4.0.3", 439 + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.3.tgz", 440 + "integrity": "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==", 441 + "license": "MIT", 442 + "engines": { 443 + "node": "20 || >=22" 444 + } 445 + }, 446 + "node_modules/base64-js": { 447 + "version": "1.5.1", 448 + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 449 + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 450 + "funding": [ 451 + { 452 + "type": "github", 453 + "url": "https://github.com/sponsors/feross" 454 + }, 455 + { 456 + "type": "patreon", 457 + "url": "https://www.patreon.com/feross" 458 + }, 459 + { 460 + "type": "consulting", 461 + "url": "https://feross.org/support" 462 + } 463 + ], 464 + "license": "MIT" 465 + }, 466 + "node_modules/brace-expansion": { 467 + "version": "5.0.2", 468 + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz", 469 + "integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==", 470 + "license": "MIT", 471 + "dependencies": { 472 + "balanced-match": "^4.0.2" 473 + }, 474 + "engines": { 475 + "node": "20 || >=22" 476 + } 477 + }, 478 + "node_modules/buffer": { 479 + "version": "6.0.3", 480 + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", 481 + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", 482 + "funding": [ 483 + { 484 + "type": "github", 485 + "url": "https://github.com/sponsors/feross" 486 + }, 487 + { 488 + "type": "patreon", 489 + "url": "https://www.patreon.com/feross" 490 + }, 491 + { 492 + "type": "consulting", 493 + "url": "https://feross.org/support" 494 + } 495 + ], 496 + "license": "MIT", 497 + "dependencies": { 498 + "base64-js": "^1.3.1", 499 + "ieee754": "^1.2.1" 500 + } 501 + }, 502 + "node_modules/cborg": { 503 + "version": "1.10.2", 504 + "resolved": "https://registry.npmjs.org/cborg/-/cborg-1.10.2.tgz", 505 + "integrity": "sha512-b3tFPA9pUr2zCUiCfRd2+wok2/LBSNUMKOuRRok+WlvvAgEt/PlbgPTsZUcwCOs53IJvLgTp0eotwtosE6njug==", 506 + "license": "Apache-2.0", 507 + "bin": { 508 + "cborg": "cli.js" 509 + } 510 + }, 511 + "node_modules/cliui": { 512 + "version": "8.0.1", 513 + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", 514 + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", 515 + "license": "ISC", 516 + "dependencies": { 517 + "string-width": "^4.2.0", 518 + "strip-ansi": "^6.0.1", 519 + "wrap-ansi": "^7.0.0" 520 + }, 521 + "engines": { 522 + "node": ">=12" 523 + } 524 + }, 525 + "node_modules/code-block-writer": { 526 + "version": "13.0.3", 527 + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", 528 + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", 529 + "license": "MIT" 530 + }, 531 + "node_modules/color-convert": { 532 + "version": "2.0.1", 533 + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 534 + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 535 + "license": "MIT", 536 + "dependencies": { 537 + "color-name": "~1.1.4" 538 + }, 539 + "engines": { 540 + "node": ">=7.0.0" 541 + } 542 + }, 543 + "node_modules/color-name": { 544 + "version": "1.1.4", 545 + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 546 + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 547 + "license": "MIT" 548 + }, 549 + "node_modules/core-js": { 550 + "version": "3.48.0", 551 + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", 552 + "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", 553 + "hasInstallScript": true, 554 + "license": "MIT", 555 + "funding": { 556 + "type": "opencollective", 557 + "url": "https://opencollective.com/core-js" 558 + } 559 + }, 560 + "node_modules/emoji-regex": { 561 + "version": "8.0.0", 562 + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 563 + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 564 + "license": "MIT" 565 + }, 566 + "node_modules/escalade": { 567 + "version": "3.2.0", 568 + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", 569 + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", 570 + "license": "MIT", 571 + "engines": { 572 + "node": ">=6" 573 + } 574 + }, 575 + "node_modules/event-target-shim": { 576 + "version": "5.0.1", 577 + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 578 + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 579 + "license": "MIT", 580 + "engines": { 581 + "node": ">=6" 582 + } 583 + }, 584 + "node_modules/events": { 585 + "version": "3.3.0", 586 + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", 587 + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", 588 + "license": "MIT", 589 + "engines": { 590 + "node": ">=0.8.x" 591 + } 592 + }, 593 + "node_modules/fast-redact": { 594 + "version": "3.5.0", 595 + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", 596 + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", 597 + "license": "MIT", 598 + "engines": { 599 + "node": ">=6" 600 + } 601 + }, 602 + "node_modules/fdir": { 603 + "version": "6.5.0", 604 + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", 605 + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", 606 + "license": "MIT", 607 + "engines": { 608 + "node": ">=12.0.0" 609 + }, 610 + "peerDependencies": { 611 + "picomatch": "^3 || ^4" 612 + }, 613 + "peerDependenciesMeta": { 614 + "picomatch": { 615 + "optional": true 616 + } 617 + } 618 + }, 619 + "node_modules/get-caller-file": { 620 + "version": "2.0.5", 621 + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 622 + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 623 + "license": "ISC", 624 + "engines": { 625 + "node": "6.* || 8.* || >= 10.*" 626 + } 627 + }, 628 + "node_modules/ieee754": { 629 + "version": "1.2.1", 630 + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 631 + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 632 + "funding": [ 633 + { 634 + "type": "github", 635 + "url": "https://github.com/sponsors/feross" 636 + }, 637 + { 638 + "type": "patreon", 639 + "url": "https://www.patreon.com/feross" 640 + }, 641 + { 642 + "type": "consulting", 643 + "url": "https://feross.org/support" 644 + } 645 + ], 646 + "license": "BSD-3-Clause" 647 + }, 648 + "node_modules/is-fullwidth-code-point": { 649 + "version": "3.0.0", 650 + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 651 + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 652 + "license": "MIT", 653 + "engines": { 654 + "node": ">=8" 655 + } 656 + }, 657 + "node_modules/iso-datestring-validator": { 658 + "version": "2.2.2", 659 + "resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz", 660 + "integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==", 661 + "license": "MIT" 662 + }, 663 + "node_modules/lru-cache": { 664 + "version": "10.4.3", 665 + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", 666 + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", 667 + "license": "ISC" 668 + }, 669 + "node_modules/minimatch": { 670 + "version": "10.2.2", 671 + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", 672 + "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", 673 + "license": "BlueOak-1.0.0", 674 + "dependencies": { 675 + "brace-expansion": "^5.0.2" 676 + }, 677 + "engines": { 678 + "node": "18 || 20 || >=22" 679 + }, 680 + "funding": { 681 + "url": "https://github.com/sponsors/isaacs" 682 + } 683 + }, 684 + "node_modules/multiformats": { 685 + "version": "9.9.0", 686 + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 687 + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 688 + "license": "(Apache-2.0 AND MIT)" 689 + }, 690 + "node_modules/on-exit-leak-free": { 691 + "version": "2.1.2", 692 + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", 693 + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", 694 + "license": "MIT", 695 + "engines": { 696 + "node": ">=14.0.0" 697 + } 698 + }, 699 + "node_modules/path-browserify": { 700 + "version": "1.0.1", 701 + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", 702 + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", 703 + "license": "MIT" 704 + }, 705 + "node_modules/picomatch": { 706 + "version": "4.0.3", 707 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 708 + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 709 + "license": "MIT", 710 + "engines": { 711 + "node": ">=12" 712 + }, 713 + "funding": { 714 + "url": "https://github.com/sponsors/jonschlinkert" 715 + } 716 + }, 717 + "node_modules/pino": { 718 + "version": "8.21.0", 719 + "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz", 720 + "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==", 721 + "license": "MIT", 722 + "dependencies": { 723 + "atomic-sleep": "^1.0.0", 724 + "fast-redact": "^3.1.1", 725 + "on-exit-leak-free": "^2.1.0", 726 + "pino-abstract-transport": "^1.2.0", 727 + "pino-std-serializers": "^6.0.0", 728 + "process-warning": "^3.0.0", 729 + "quick-format-unescaped": "^4.0.3", 730 + "real-require": "^0.2.0", 731 + "safe-stable-stringify": "^2.3.1", 732 + "sonic-boom": "^3.7.0", 733 + "thread-stream": "^2.6.0" 734 + }, 735 + "bin": { 736 + "pino": "bin.js" 737 + } 738 + }, 739 + "node_modules/pino-abstract-transport": { 740 + "version": "1.2.0", 741 + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", 742 + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", 743 + "license": "MIT", 744 + "dependencies": { 745 + "readable-stream": "^4.0.0", 746 + "split2": "^4.0.0" 747 + } 748 + }, 749 + "node_modules/pino-std-serializers": { 750 + "version": "6.2.2", 751 + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", 752 + "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==", 753 + "license": "MIT" 754 + }, 755 + "node_modules/prettier": { 756 + "version": "3.8.1", 757 + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", 758 + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", 759 + "license": "MIT", 760 + "bin": { 761 + "prettier": "bin/prettier.cjs" 762 + }, 763 + "engines": { 764 + "node": ">=14" 765 + }, 766 + "funding": { 767 + "url": "https://github.com/prettier/prettier?sponsor=1" 768 + } 769 + }, 770 + "node_modules/process": { 771 + "version": "0.11.10", 772 + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", 773 + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", 774 + "license": "MIT", 775 + "engines": { 776 + "node": ">= 0.6.0" 777 + } 778 + }, 779 + "node_modules/process-warning": { 780 + "version": "3.0.0", 781 + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", 782 + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", 783 + "license": "MIT" 784 + }, 785 + "node_modules/quick-format-unescaped": { 786 + "version": "4.0.4", 787 + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", 788 + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", 789 + "license": "MIT" 790 + }, 791 + "node_modules/readable-stream": { 792 + "version": "4.7.0", 793 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", 794 + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", 795 + "license": "MIT", 796 + "dependencies": { 797 + "abort-controller": "^3.0.0", 798 + "buffer": "^6.0.3", 799 + "events": "^3.3.0", 800 + "process": "^0.11.10", 801 + "string_decoder": "^1.3.0" 802 + }, 803 + "engines": { 804 + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 805 + } 806 + }, 807 + "node_modules/real-require": { 808 + "version": "0.2.0", 809 + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", 810 + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", 811 + "license": "MIT", 812 + "engines": { 813 + "node": ">= 12.13.0" 814 + } 815 + }, 816 + "node_modules/require-directory": { 817 + "version": "2.1.1", 818 + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 819 + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 820 + "license": "MIT", 821 + "engines": { 822 + "node": ">=0.10.0" 823 + } 824 + }, 825 + "node_modules/safe-buffer": { 826 + "version": "5.2.1", 827 + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 828 + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 829 + "funding": [ 830 + { 831 + "type": "github", 832 + "url": "https://github.com/sponsors/feross" 833 + }, 834 + { 835 + "type": "patreon", 836 + "url": "https://www.patreon.com/feross" 837 + }, 838 + { 839 + "type": "consulting", 840 + "url": "https://feross.org/support" 841 + } 842 + ], 843 + "license": "MIT" 844 + }, 845 + "node_modules/safe-stable-stringify": { 846 + "version": "2.5.0", 847 + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", 848 + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", 849 + "license": "MIT", 850 + "engines": { 851 + "node": ">=10" 852 + } 853 + }, 854 + "node_modules/sonic-boom": { 855 + "version": "3.8.1", 856 + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", 857 + "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==", 858 + "license": "MIT", 859 + "dependencies": { 860 + "atomic-sleep": "^1.0.0" 861 + } 862 + }, 863 + "node_modules/split2": { 864 + "version": "4.2.0", 865 + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", 866 + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", 867 + "license": "ISC", 868 + "engines": { 869 + "node": ">= 10.x" 870 + } 871 + }, 872 + "node_modules/string_decoder": { 873 + "version": "1.3.0", 874 + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", 875 + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", 876 + "license": "MIT", 877 + "dependencies": { 878 + "safe-buffer": "~5.2.0" 879 + } 880 + }, 881 + "node_modules/string-width": { 882 + "version": "4.2.3", 883 + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 884 + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 885 + "license": "MIT", 886 + "dependencies": { 887 + "emoji-regex": "^8.0.0", 888 + "is-fullwidth-code-point": "^3.0.0", 889 + "strip-ansi": "^6.0.1" 890 + }, 891 + "engines": { 892 + "node": ">=8" 893 + } 894 + }, 895 + "node_modules/strip-ansi": { 896 + "version": "6.0.1", 897 + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 898 + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 899 + "license": "MIT", 900 + "dependencies": { 901 + "ansi-regex": "^5.0.1" 902 + }, 903 + "engines": { 904 + "node": ">=8" 905 + } 906 + }, 907 + "node_modules/thread-stream": { 908 + "version": "2.7.0", 909 + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", 910 + "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==", 911 + "license": "MIT", 912 + "dependencies": { 913 + "real-require": "^0.2.0" 914 + } 915 + }, 916 + "node_modules/tinyglobby": { 917 + "version": "0.2.15", 918 + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", 919 + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", 920 + "license": "MIT", 921 + "dependencies": { 922 + "fdir": "^6.5.0", 923 + "picomatch": "^4.0.3" 924 + }, 925 + "engines": { 926 + "node": ">=12.0.0" 927 + }, 928 + "funding": { 929 + "url": "https://github.com/sponsors/SuperchupuDev" 930 + } 931 + }, 932 + "node_modules/ts-morph": { 933 + "version": "27.0.2", 934 + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-27.0.2.tgz", 935 + "integrity": "sha512-fhUhgeljcrdZ+9DZND1De1029PrE+cMkIP7ooqkLRTrRLTqcki2AstsyJm0vRNbTbVCNJ0idGlbBrfqc7/nA8w==", 936 + "license": "MIT", 937 + "dependencies": { 938 + "@ts-morph/common": "~0.28.1", 939 + "code-block-writer": "^13.0.3" 940 + } 941 + }, 942 + "node_modules/tslib": { 943 + "version": "2.8.1", 944 + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 945 + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 946 + "license": "0BSD" 947 + }, 948 + "node_modules/typescript": { 949 + "version": "5.9.3", 950 + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", 951 + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", 952 + "dev": true, 953 + "license": "Apache-2.0", 954 + "bin": { 955 + "tsc": "bin/tsc", 956 + "tsserver": "bin/tsserver" 957 + }, 958 + "engines": { 959 + "node": ">=14.17" 960 + } 961 + }, 962 + "node_modules/uint8arrays": { 963 + "version": "3.0.0", 964 + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz", 965 + "integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==", 966 + "license": "MIT", 967 + "dependencies": { 968 + "multiformats": "^9.4.2" 969 + } 970 + }, 971 + "node_modules/unicode-segmenter": { 972 + "version": "0.14.5", 973 + "resolved": "https://registry.npmjs.org/unicode-segmenter/-/unicode-segmenter-0.14.5.tgz", 974 + "integrity": "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==", 975 + "license": "MIT" 976 + }, 977 + "node_modules/varint": { 978 + "version": "6.0.0", 979 + "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", 980 + "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", 981 + "license": "MIT" 982 + }, 983 + "node_modules/wrap-ansi": { 984 + "version": "7.0.0", 985 + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 986 + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 987 + "license": "MIT", 988 + "dependencies": { 989 + "ansi-styles": "^4.0.0", 990 + "string-width": "^4.1.0", 991 + "strip-ansi": "^6.0.0" 992 + }, 993 + "engines": { 994 + "node": ">=10" 995 + }, 996 + "funding": { 997 + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 998 + } 999 + }, 1000 + "node_modules/y18n": { 1001 + "version": "5.0.8", 1002 + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1003 + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1004 + "license": "ISC", 1005 + "engines": { 1006 + "node": ">=10" 1007 + } 1008 + }, 1009 + "node_modules/yargs": { 1010 + "version": "17.7.2", 1011 + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", 1012 + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", 1013 + "license": "MIT", 1014 + "dependencies": { 1015 + "cliui": "^8.0.1", 1016 + "escalade": "^3.1.1", 1017 + "get-caller-file": "^2.0.5", 1018 + "require-directory": "^2.1.1", 1019 + "string-width": "^4.2.3", 1020 + "y18n": "^5.0.5", 1021 + "yargs-parser": "^21.1.1" 1022 + }, 1023 + "engines": { 1024 + "node": ">=12" 1025 + } 1026 + }, 1027 + "node_modules/yargs-parser": { 1028 + "version": "21.1.1", 1029 + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", 1030 + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", 1031 + "license": "ISC", 1032 + "engines": { 1033 + "node": ">=12" 1034 + } 1035 + }, 1036 + "node_modules/zod": { 1037 + "version": "3.25.76", 1038 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 1039 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 1040 + "license": "MIT", 1041 + "funding": { 1042 + "url": "https://github.com/sponsors/colinhacks" 1043 + } 1044 + } 1045 + } 1046 + }
+31
packages/@inlay/render/package.json
··· 1 + { 2 + "name": "@inlay/render", 3 + "version": "0.0.1", 4 + "type": "module", 5 + "main": "./dist/index.js", 6 + "types": "./dist/index.d.ts", 7 + "exports": { 8 + ".": { 9 + "source": "./src/index.ts", 10 + "types": "./dist/index.d.ts", 11 + "import": "./dist/index.js" 12 + } 13 + }, 14 + "files": [ 15 + "dist" 16 + ], 17 + "scripts": { 18 + "build": "tsc", 19 + "dev": "tsc --watch", 20 + "test": "tsx --conditions source --test test/*.test.ts", 21 + "prepublishOnly": "npm run build" 22 + }, 23 + "dependencies": { 24 + "@atproto/lexicon": "^0.6.1", 25 + "@atproto/syntax": "^0.4.3", 26 + "@inlay/core": "*" 27 + }, 28 + "devDependencies": { 29 + "typescript": "^5.9.0" 30 + } 31 + }
+536
packages/@inlay/render/src/index.ts
··· 1 + // @inlay/render — Component resolution with pluggable I/O 2 + 3 + import { 4 + $, 5 + walkTree, 6 + resolveBindings, 7 + serializeTree, 8 + deserializeTree, 9 + isValidElement, 10 + } from "@inlay/core"; 11 + import type { Element } from "@inlay/core"; 12 + import { 13 + AtUri, 14 + AtUriString, 15 + AtIdentifierString, 16 + NsidString, 17 + ensureValidNsid, 18 + } from "@atproto/syntax"; 19 + import { validateProps } from "./validate.ts"; 20 + 21 + import type { Main as ComponentRecord } from "../../../../generated/at/inlay/component.defs.ts"; 22 + import type { Main as PackRecord } from "../../../../generated/at/inlay/pack.defs.ts"; 23 + import type { CachePolicy } from "../../../../generated/at/inlay/defs.defs.ts"; 24 + 25 + // --- Public types --- 26 + 27 + export type { Main as ComponentRecord } from "../../../../generated/at/inlay/component.defs.ts"; 28 + export type { CachePolicy } from "../../../../generated/at/inlay/defs.defs.ts"; 29 + 30 + export interface Resolver { 31 + fetchRecord(uri: AtUriString): Promise<unknown | null>; 32 + xrpc(params: { 33 + did: string; 34 + nsid: string; 35 + type: "query" | "procedure"; 36 + params?: Record<string, string>; 37 + body?: Record<string, unknown>; 38 + componentUri?: string; 39 + }): Promise<unknown>; 40 + resolveLexicon(nsid: string): Promise<unknown | null>; 41 + } 42 + 43 + export type RenderOptions = { 44 + resolver: Resolver; 45 + maxDepth?: number; 46 + validate?: boolean; 47 + }; 48 + 49 + /** 50 + * Plain, serializable render context. 51 + * 52 + * - `imports`: ordered pack URIs for type resolution 53 + * - `component`: when present, the first render uses this component directly 54 + * (root render). Stripped from child contexts automatically. 55 + * - `componentUri`: AT URI of the root component record. Passed to xrpc 56 + * for external components. 57 + * - `depth`: current nesting depth (default 0). Incremented on each 58 + * component boundary to prevent infinite recursion. 59 + * - `scope`: template variable bindings. Bindings in child element props 60 + * resolve against this at the next render boundary. 61 + */ 62 + export type RenderContext = { 63 + imports: AtUriString[]; 64 + component?: ComponentRecord; 65 + componentUri?: string; 66 + depth?: number; 67 + scope?: Record<string, unknown>; 68 + }; 69 + 70 + export type RenderResult = { 71 + resolved: boolean; 72 + node: unknown; 73 + context: RenderContext; 74 + cache?: CachePolicy; 75 + }; 76 + 77 + // Caller context overrides: elements passed as props to a component are tagged 78 + // so they resolve through the CALLER's imports, not the component's. Both 79 + // template and external components use this mechanism. render() checks the 80 + // WeakMap transparently before resolving any element. 81 + const slotContexts = new WeakMap<object, RenderContext>(); 82 + 83 + export class MissingError extends Error { 84 + kind = "missing" as const; 85 + path: string[]; 86 + constructor(path: string[]) { 87 + super(`Missing: ${path.join(".")}`); 88 + this.path = path; 89 + } 90 + } 91 + 92 + const DEFAULT_MAX_DEPTH = 30; 93 + 94 + // --- Public API --- 95 + 96 + export function createContext( 97 + component: ComponentRecord, 98 + componentUri?: string 99 + ): RenderContext { 100 + return { 101 + imports: component.imports ?? [], 102 + component, 103 + componentUri, 104 + }; 105 + } 106 + 107 + export async function render( 108 + element: Element, 109 + context: RenderContext, 110 + options: RenderOptions 111 + ): Promise<RenderResult> { 112 + const { resolver } = options; 113 + const effective = slotContexts.get(element) ?? context; 114 + const depth = effective.depth ?? 0; 115 + const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH; 116 + const validate = options.validate ?? true; 117 + 118 + try { 119 + const type = element.type; 120 + let props = (element.props ?? {}) as Record<string, unknown>; 121 + 122 + // Resolve any Bindings left in props by the parent template 123 + if (effective.scope) { 124 + props = resolveBindings(props, scopeResolver(effective.scope)); 125 + } 126 + 127 + // Root render: use component directly 128 + if (effective.component) { 129 + if (type !== effective.component.type) { 130 + throw new Error( 131 + `render was given ${effective.component.type}, cannot render ${type}` 132 + ); 133 + } 134 + return await renderComponent( 135 + resolver, 136 + effective.component, 137 + effective.componentUri, 138 + element, 139 + props, 140 + effective, 141 + validate 142 + ); 143 + } 144 + 145 + if (type.startsWith("at.inlay.")) { 146 + if (type === "at.inlay.Missing") { 147 + const path = props.path; 148 + if (!Array.isArray(path) || path.length === 0) { 149 + throw new Error( 150 + "at.inlay.Missing requires path with at least 1 item" 151 + ); 152 + } 153 + throw new MissingError(path as string[]); 154 + } 155 + return { 156 + resolved: true, 157 + node: $(type, { ...props, key: element.key }), 158 + context: { imports: effective.imports, scope: effective.scope }, 159 + }; 160 + } 161 + 162 + if (depth >= maxDepth) { 163 + throw Error("Component depth limit exceeded"); 164 + } 165 + 166 + const { component, componentUri } = await resolveType( 167 + type, 168 + effective.imports, 169 + resolver 170 + ); 171 + return await renderComponent( 172 + resolver, 173 + component, 174 + componentUri, 175 + element, 176 + props, 177 + effective, 178 + validate 179 + ); 180 + } catch (e) { 181 + if (e instanceof MissingError) throw e; 182 + const err = e as Error; 183 + const errProps: Record<string, unknown> = { message: err.message }; 184 + if (err.stack) errProps.stack = err.stack; 185 + return { 186 + resolved: true, 187 + node: $("at.inlay.Throw", errProps as Record<string, string>), 188 + context: { imports: effective.imports }, 189 + }; 190 + } 191 + } 192 + 193 + /** Build a resolve callback that looks up paths in a scope, throwing MissingError on null. */ 194 + function scopeResolver( 195 + scope: Record<string, unknown> 196 + ): (path: string[]) => unknown { 197 + return (path) => { 198 + const value = resolvePath(scope, path); 199 + if (value == null) throw new MissingError(path); 200 + return value; 201 + }; 202 + } 203 + 204 + export function resolvePath(obj: unknown, path: string[]): unknown { 205 + let current: unknown = obj; 206 + for (const seg of path) { 207 + if (current == null || typeof current !== "object") return undefined; 208 + if (!Object.hasOwn(current, seg)) return undefined; 209 + current = (current as Record<string, unknown>)[seg]; 210 + } 211 + return current; 212 + } 213 + 214 + // --- Internals --- 215 + 216 + async function renderComponent( 217 + resolver: Resolver, 218 + component: ComponentRecord, 219 + componentUri: string | undefined, 220 + element: Element, 221 + props: Record<string, unknown>, 222 + ctx: RenderContext, 223 + validate: boolean 224 + ): Promise<RenderResult> { 225 + const depth = ctx.depth ?? 0; 226 + 227 + // Primitive: no body, return element with resolved props 228 + if (!component.body) { 229 + return { 230 + resolved: true, 231 + node: $(element.type, { ...props, key: element.key }), 232 + context: { 233 + imports: component.imports?.length ? component.imports : ctx.imports, 234 + depth, 235 + scope: ctx.scope, 236 + }, 237 + }; 238 + } 239 + 240 + const type = element.type; 241 + let resolvedProps = props; 242 + if (component.view) { 243 + resolvedProps = await expandBareDid(props, component, resolver); 244 + } 245 + 246 + if (validate) { 247 + await validateProps(type, resolvedProps, component, resolver); 248 + } 249 + 250 + if (component.body.$type === "at.inlay.component#bodyTemplate") { 251 + return renderTemplate(resolver, component, resolvedProps, ctx); 252 + } 253 + 254 + if (component.body.$type === "at.inlay.component#bodyExternal") { 255 + return renderExternal( 256 + resolver, 257 + type, 258 + component, 259 + componentUri, 260 + resolvedProps, 261 + ctx 262 + ); 263 + } 264 + 265 + throw new Error(`Unknown body type: ${component.body.$type}`); 266 + } 267 + 268 + async function resolveType( 269 + nsid: string, 270 + importStack: AtUriString[], 271 + resolver: Resolver 272 + ): Promise<{ 273 + componentUri: string; 274 + packUri: string; 275 + component: ComponentRecord; 276 + }> { 277 + const packPromises = importStack.map((uri) => resolver.fetchRecord(uri)); 278 + 279 + for (let i = 0; i < importStack.length; i++) { 280 + const pack = (await packPromises[i]) as PackRecord | null; 281 + if (!pack || !pack.exports) continue; 282 + 283 + const entry = pack.exports.find((e) => e.type === nsid); 284 + if (!entry) continue; 285 + 286 + const component = (await resolver.fetchRecord( 287 + entry.component 288 + )) as ComponentRecord | null; 289 + if (!component) continue; 290 + 291 + return { 292 + componentUri: entry.component, 293 + packUri: importStack[i], 294 + component, 295 + }; 296 + } 297 + 298 + throw new Error(`No pack exports type: ${nsid}`); 299 + } 300 + 301 + async function renderTemplate( 302 + resolver: Resolver, 303 + component: ComponentRecord, 304 + props: Record<string, unknown>, 305 + ctx: RenderContext 306 + ): Promise<RenderResult> { 307 + const body = component.body as { $type: string; node: unknown }; 308 + const depth = ctx.depth ?? 0; 309 + 310 + // Eagerly prefetch child packs so they overlap with record fetch 311 + const childImports = component.imports ?? []; 312 + for (const uri of childImports) { 313 + resolver.fetchRecord(uri).catch(() => {}); 314 + } 315 + 316 + const tree = deserializeTree(body.node); 317 + 318 + // Replace caller-provided elements with Slots so they resolve through the 319 + // caller's imports, not the component's — same isolation as external slots. 320 + const callerCtx: RenderContext = { 321 + imports: ctx.imports, 322 + depth, 323 + scope: ctx.scope, 324 + }; 325 + const slottedProps = walkTree(props, (obj, walk) => { 326 + if (isValidElement(obj)) { 327 + slotContexts.set(obj, callerCtx); 328 + return obj; 329 + } 330 + const out: Record<string, unknown> = {}; 331 + for (const [k, v] of Object.entries(obj)) out[k] = walk(v); 332 + return out; 333 + }) as Record<string, unknown>; 334 + 335 + let scope: Record<string, unknown> = { ...slottedProps }; 336 + let cache: CachePolicy | undefined; 337 + const uri = props.uri as string | undefined; 338 + if (uri && uri.startsWith("at://")) { 339 + const parsed = new AtUri(uri); 340 + ensureValidNsid(parsed.collection); 341 + if (parsed.collection && parsed.rkey) { 342 + const viewRecord = component.view?.some( 343 + (v) => 344 + v.$type === "at.inlay.component#viewRecord" && 345 + (v as any).collection === parsed.collection 346 + ); 347 + if (viewRecord) { 348 + const built = await buildScope( 349 + parsed.host, 350 + parsed.collection, 351 + parsed.rkey, 352 + tree, 353 + resolver 354 + ); 355 + scope = { ...props, ...built.scope }; 356 + cache = built.cache; 357 + } 358 + } 359 + } 360 + 361 + const node = resolveBindings(tree, scopeResolver(scope)); 362 + return { 363 + resolved: false, 364 + node, 365 + context: { imports: component.imports ?? [], depth: depth + 1, scope }, 366 + cache, 367 + }; 368 + } 369 + 370 + async function buildScope( 371 + did: AtIdentifierString, 372 + collection: NsidString, 373 + rkey: string, 374 + tree: unknown, 375 + resolver: Resolver 376 + ): Promise<{ scope: Record<string, unknown>; cache?: CachePolicy }> { 377 + const recordUri: AtUriString = `at://${did}/${collection}/${rkey}`; 378 + const scope: Record<string, unknown> = { 379 + did, 380 + collection, 381 + rkey, 382 + uri: recordUri, 383 + }; 384 + 385 + // Skip record fetch if all bindings can be satisfied from URI parts alone 386 + if (tree && !needsRecord(tree, scope)) { 387 + return { scope }; 388 + } 389 + 390 + const record = await resolver.fetchRecord(recordUri); 391 + if (record && typeof record === "object") { 392 + const merged = Object.create(null) as Record<string, unknown>; 393 + Object.assign(merged, record, scope); 394 + return { 395 + scope: merged, 396 + cache: { 397 + tags: [{ $type: "at.inlay.defs#tagRecord", uri: recordUri }], 398 + }, 399 + }; 400 + } 401 + return { scope }; 402 + } 403 + 404 + /** Check if any Binding in the deserialized tree needs data beyond what scope provides. */ 405 + function needsRecord(tree: unknown, scope: Record<string, unknown>): boolean { 406 + let missed = false; 407 + walkTree(tree, (obj, walk) => { 408 + if (isValidElement(obj)) { 409 + const el = obj as Element; 410 + if (el.type === "at.inlay.Binding") { 411 + const path = (el.props as Record<string, unknown>)?.path; 412 + if ( 413 + Array.isArray(path) && 414 + resolvePath(scope, path as string[]) == null 415 + ) { 416 + missed = true; 417 + } 418 + return obj; 419 + } 420 + } 421 + for (const v of Object.values(obj)) walk(v); 422 + return obj; 423 + }); 424 + return missed; 425 + } 426 + 427 + async function renderExternal( 428 + resolver: Resolver, 429 + type: string, 430 + component: ComponentRecord, 431 + componentUri: string | undefined, 432 + props: Record<string, unknown>, 433 + ctx: RenderContext 434 + ): Promise<RenderResult> { 435 + const body = component.body as { $type: string; did: string }; 436 + const depth = ctx.depth ?? 0; 437 + 438 + // Replace child elements with Slot references for wire transport 439 + const refs = new Map<string, unknown>(); 440 + const refSlots = new Set<object>(); 441 + const wireProps = serializeTree(props, (el) => { 442 + if (el.type === "at.inlay.Slot") { 443 + if (refSlots.has(el)) return el; 444 + throw new Error("Unexpected Slot in props"); 445 + } 446 + const id = String(refs.size); 447 + refs.set(id, el); 448 + const slot = $("at.inlay.Slot", { id }); 449 + refSlots.add(slot); 450 + return slot; 451 + }); 452 + 453 + const response = (await resolver.xrpc({ 454 + did: body.did, 455 + nsid: type, 456 + type: "procedure", 457 + body: wireProps as Record<string, unknown>, 458 + componentUri, 459 + })) as { node: unknown; cache?: CachePolicy }; 460 + 461 + // Restore slots — register caller context in the WeakMap 462 + const callerCtx: RenderContext = { 463 + imports: ctx.imports, 464 + depth, 465 + scope: ctx.scope, 466 + }; 467 + 468 + const node = deserializeTree(response.node, (el) => { 469 + if (el.type === "at.inlay.Slot" && el.props) { 470 + const id = (el.props as Record<string, unknown>).id as string; 471 + const original = refs.get(id); 472 + if (original === undefined) { 473 + throw new Error(`${type}: XRPC response references unknown slot ${id}`); 474 + } 475 + const orig = original as Element; 476 + const restored = $(orig.type, { 477 + ...orig.props, 478 + key: el.key, 479 + }); 480 + const frag = $("at.inlay.Fragment", { 481 + key: orig.key, 482 + children: restored, 483 + }); 484 + slotContexts.set(frag, callerCtx); 485 + return frag as Element; 486 + } 487 + return el; 488 + }); 489 + 490 + return { 491 + resolved: false, 492 + node, 493 + context: { imports: component.imports ?? [], depth: depth + 1 }, 494 + cache: response.cache, 495 + }; 496 + } 497 + 498 + async function expandBareDid( 499 + props: Record<string, unknown>, 500 + component: ComponentRecord, 501 + resolver: Resolver 502 + ): Promise<Record<string, unknown>> { 503 + const uri = props.uri as string | undefined; 504 + if (!uri || !uri.startsWith("did:")) return props; 505 + 506 + const view = component.view!; 507 + const hasIdentityView = view.some( 508 + (v) => v.$type === "at.inlay.component#viewIdentity" 509 + ); 510 + if (!hasIdentityView) return props; 511 + 512 + const viewRecord = view.find( 513 + (v) => 514 + v.$type === "at.inlay.component#viewRecord" && !!(v as any).collection 515 + ) as { collection: string } | undefined; 516 + if (!viewRecord) return props; 517 + 518 + const viewLex = (await resolver.resolveLexicon(viewRecord.collection)) as { 519 + defs?: Record<string, { key?: string }>; 520 + } | null; 521 + 522 + let identityRkey: string | null = null; 523 + if (viewLex?.defs?.main?.key?.startsWith("literal:")) { 524 + identityRkey = viewLex.defs.main.key.slice("literal:".length); 525 + } else if (!viewLex) { 526 + identityRkey = "self"; 527 + } 528 + 529 + if (identityRkey) { 530 + return { 531 + ...props, 532 + uri: `at://${uri}/${viewRecord.collection}/${identityRkey}`, 533 + }; 534 + } 535 + return props; 536 + }
+169
packages/@inlay/render/src/validate.ts
··· 1 + import { Lexicons, type LexiconDoc } from "@atproto/lexicon"; 2 + import { AtUri } from "@atproto/syntax"; 3 + import type { Main as ComponentRecord } from "../../../../generated/at/inlay/component.defs.ts"; 4 + import type { Resolver } from "./index.ts"; 5 + 6 + // --- Lexicon cache (module-level) --- 7 + 8 + const lexiconCache = new Map<string, Lexicons>(); 9 + 10 + // --- Public API --- 11 + 12 + export async function validateProps( 13 + type: string, 14 + props: Record<string, unknown>, 15 + component: ComponentRecord, 16 + resolver: Resolver 17 + ): Promise<void> { 18 + const lex = await resolver.resolveLexicon(type); 19 + if (lex) { 20 + let lexicons = lexiconCache.get(type); 21 + if (!lexicons) { 22 + lexicons = await buildLexicons(lex as Record<string, unknown>, resolver); 23 + lexiconCache.set(type, lexicons); 24 + } 25 + lexicons.assertValidXrpcInput(type, props); 26 + } else if (component.accepts?.length) { 27 + const propEntries = new Map< 28 + string, 29 + { type: string; formats: Set<string> } 30 + >(); 31 + for (const accept of component.accepts) { 32 + if (!accept.prop) continue; 33 + const existing = propEntries.get(accept.prop); 34 + if (existing) { 35 + if (accept.format) existing.formats.add(accept.format); 36 + } else { 37 + const formats = new Set<string>(); 38 + if (accept.format) formats.add(accept.format); 39 + propEntries.set(accept.prop, { type: accept.type, formats }); 40 + } 41 + } 42 + const properties: Record<string, { type: string; format?: string }> = {}; 43 + const required = [...propEntries.keys()]; 44 + for (const [prop, { type: t, formats }] of propEntries) { 45 + const format = unionFormats(formats); 46 + properties[prop] = format ? { type: t, format } : { type: t }; 47 + } 48 + const syntheticLex = { 49 + lexicon: 1, 50 + id: type, 51 + defs: { 52 + main: { 53 + type: "procedure", 54 + input: { 55 + encoding: "application/json", 56 + schema: { type: "object", required, properties }, 57 + }, 58 + }, 59 + }, 60 + } as unknown as LexiconDoc; 61 + const lexicons = new Lexicons([syntheticLex]); 62 + lexicons.assertValidXrpcInput(type, props); 63 + } 64 + 65 + if (component.accepts?.length) { 66 + const allowedCollections = new Map<string, Set<string>>(); 67 + for (const accept of component.accepts) { 68 + if (!accept.prop || !accept.collection) continue; 69 + let set = allowedCollections.get(accept.prop); 70 + if (!set) { 71 + set = new Set(); 72 + allowedCollections.set(accept.prop, set); 73 + } 74 + set.add(accept.collection); 75 + } 76 + for (const [prop, allowed] of allowedCollections) { 77 + const value = props[prop]; 78 + if (typeof value !== "string" || !value.startsWith("at://")) continue; 79 + const parsed = new AtUri(value); 80 + if (!parsed.collection) continue; 81 + if (!allowed.has(parsed.collection)) { 82 + throw new Error( 83 + `${type}: ${prop} expects ${[...allowed].join(" or ")}, got ${parsed.collection}` 84 + ); 85 + } 86 + } 87 + } 88 + } 89 + 90 + // --- Internals --- 91 + 92 + const FORMAT_ANCESTORS: Record<string, string[]> = { 93 + did: ["uri", "at-identifier"], 94 + "at-uri": ["uri"], 95 + handle: ["at-identifier"], 96 + }; 97 + 98 + function unionFormats(formats: Set<string>): string | undefined { 99 + const arr = [...formats]; 100 + if (arr.length <= 1) return arr[0]; 101 + const ancestorSets = arr.map( 102 + (f) => new Set([f, ...(FORMAT_ANCESTORS[f] ?? [])]) 103 + ); 104 + const common = [...ancestorSets[0]].filter((f) => 105 + ancestorSets.every((s) => s.has(f)) 106 + ); 107 + if (common.length === 0) return undefined; 108 + if (common.length === 1) return common[0]; 109 + return common.find( 110 + (f) => 111 + !common.some((g) => g !== f && (FORMAT_ANCESTORS[g] ?? []).includes(f)) 112 + ); 113 + } 114 + 115 + function collectRefNsids(obj: unknown, out = new Set<string>()): Set<string> { 116 + if (!obj || typeof obj !== "object") return out; 117 + const o = obj as Record<string, unknown>; 118 + if (o.type === "ref" && typeof o.ref === "string") { 119 + const nsid = (o.ref as string).split("#")[0]; 120 + if (nsid) out.add(nsid); 121 + } 122 + if (o.type === "union" && Array.isArray(o.refs)) { 123 + for (const ref of o.refs) { 124 + if (typeof ref === "string") { 125 + const nsid = ref.split("#")[0]; 126 + if (nsid) out.add(nsid); 127 + } 128 + } 129 + } 130 + for (const val of Object.values(o)) { 131 + if (Array.isArray(val)) { 132 + val.forEach((v) => collectRefNsids(v, out)); 133 + } else if (val && typeof val === "object") { 134 + collectRefNsids(val, out); 135 + } 136 + } 137 + return out; 138 + } 139 + 140 + async function buildLexicons( 141 + root: Record<string, unknown>, 142 + resolver: Resolver 143 + ): Promise<Lexicons> { 144 + const loaded = new Map<string, Record<string, unknown>>(); 145 + loaded.set((root as { id: string }).id, root); 146 + 147 + let pending = collectRefNsids(root); 148 + while (pending.size > 0) { 149 + const newNsids = [...pending].filter((nsid) => !loaded.has(nsid)); 150 + if (newNsids.length === 0) break; 151 + const docs = await Promise.all( 152 + newNsids.map((nsid) => resolver.resolveLexicon(nsid)) 153 + ); 154 + const nextPending = new Set<string>(); 155 + for (let i = 0; i < newNsids.length; i++) { 156 + const doc = docs[i] as Record<string, unknown> | null; 157 + if (doc) { 158 + loaded.set(newNsids[i], doc); 159 + collectRefNsids(doc, nextPending); 160 + } 161 + } 162 + pending = nextPending; 163 + } 164 + 165 + const docs = [...loaded.values()].map( 166 + (d) => JSON.parse(JSON.stringify(d)) as LexiconDoc 167 + ); 168 + return new Lexicons(docs); 169 + }
+3540
packages/@inlay/render/test/render.test.ts
··· 1 + // ============================================================================ 2 + // inlay render — specification tests 3 + // ============================================================================ 4 + // 5 + // inlay is a UI component system on the AT Protocol. 6 + // 7 + // Elements reference components by NSID — <com.example.PostCard uri="..."> 8 + // is an element whose type is the NSID com.example.PostCard. An NSID is 9 + // just a name; it says nothing about where the implementation lives. 10 + // 11 + // A pack (at.inlay.pack) maps NSIDs to concrete implementations. Each 12 + // entry points an NSID at a component record URI (at://did/at.inlay.component/rkey), 13 + // so the same NSID can resolve to different implementations depending on context. 14 + // 15 + // A component record (at.inlay.component) defines what happens when the 16 + // element is rendered. It has one of three body kinds: 17 + // 18 + // - no body → a primitive; the host knows how to render this 19 + // - bodyTemplate → a stored element tree with Binding placeholders 20 + // - bodyExternal → an XRPC endpoint that returns an element tree 21 + // 22 + // Resolution algorithm: 23 + // 24 + // 1. Start with an element like <com.example.PostCard uri="at://...">. 25 + // 2. Scan imported packs for a component that implements com.example.PostCard. 26 + // 3. Resolve the component record and switch based on body kind: 27 + // a. no body → done, it's a primitive. 28 + // b. bodyTemplate → substitute Binding placeholders with the 29 + // element's props (and optionally a fetched record when the 30 + // component declares that it's a "view" for some records). 31 + // c. bodyExternal → call the XRPC service, passing props. 32 + // Children can be passed too, but they're opaque to the callee. 33 + // 4. Recurse into the expanded tree until everything resolves to 34 + // primitives. This can be done to completion or (better) streamingly. 35 + // 36 + // The host drives this loop. render() resolves one element at a time; 37 + // the host's walk loop calls render() repeatedly, dispatching resolved 38 + // primitives to handlers that produce the final output or a stream. 39 + // 40 + // These tests implement a minimal host that renders component elements 41 + // into an HTML-like output tree. I'll try to keep them mostly readable. 42 + 43 + import { describe, it } from "node:test"; 44 + import assert from "node:assert/strict"; 45 + import { $, serializeTree, deserializeTree, isValidElement } from "@inlay/core"; 46 + import type { Element } from "@inlay/core"; 47 + import { 48 + render, 49 + createContext, 50 + MissingError, 51 + type ComponentRecord, 52 + type CachePolicy, 53 + type Resolver, 54 + type RenderContext, 55 + type RenderOptions, 56 + } from "@inlay/render"; 57 + import type { Main as PackRecord } from "../../../../generated/at/inlay/pack.defs.ts"; 58 + import { 59 + tagRecord, 60 + tagLink, 61 + } from "../../../../generated/at/inlay/defs.defs.ts"; 62 + import type { AtUriString } from "@atproto/syntax"; 63 + import type { Response as ComponentResponse } from "../../../../generated/at/inlay/defs.defs.ts"; 64 + 65 + // ============================================================================ 66 + // AT Protocol constants 67 + // ============================================================================ 68 + 69 + const SERVICE_DID = "did:plc:service" as const; 70 + 71 + // Core element types — built into the renderer with special semantics. 72 + const Maybe = "at.inlay.Maybe" as const; 73 + const Fragment = "at.inlay.Fragment" as const; 74 + const Throw = "at.inlay.Throw" as const; 75 + const Binding = "at.inlay.Binding" as const; 76 + 77 + // Host primitives — component types the host renders natively. Each has 78 + // a corresponding component record with no body (primitive = no body) 79 + // registered in the host pack. These are the terminal nodes that 80 + // the host turns into output nodes (e.g. HTML, React, etc). 81 + const Stack = "test.host.Stack" as const; 82 + const Row = "test.host.Row" as const; 83 + const Grid = "test.host.Grid" as const; 84 + const Text = "test.host.Text" as const; 85 + const Link = "test.host.Link" as const; 86 + const List = "test.host.List" as const; 87 + const Avatar = "test.host.Avatar" as const; 88 + const Cover = "test.host.Cover" as const; 89 + 90 + // Application component types — user-defined, built on host primitives. 91 + // These can either be templates inside their records, or XRPC endpoints. 92 + const Greeting = "test.app.Greeting" as const; 93 + const PostCard = "test.app.PostCard" as const; 94 + const Root = "test.app.Root" as const; 95 + const Card = "test.app.Card" as const; 96 + const Layout = "test.app.Layout" as const; 97 + const Page = "test.app.Page" as const; 98 + const External = "test.app.External" as const; 99 + const Indirection = "test.app.Indirection" as const; 100 + const MissingCard = "test.app.MissingCard" as const; 101 + const View = "test.app.View" as const; 102 + const Loop = "test.infinite.Loop" as const; 103 + 104 + // ============================================================================ 105 + // Host output 106 + // ============================================================================ 107 + 108 + // The output format is host-specific. In this test suite, we emit HTML-like tags. 109 + class Output { 110 + constructor( 111 + readonly tag: string, 112 + readonly attrs: Record<string, unknown> = {} 113 + ) {} 114 + } 115 + 116 + function h(tag: string, attrs: Record<string, unknown> = {}): Output { 117 + const clean: Record<string, unknown> = {}; 118 + for (const [k, v] of Object.entries(attrs)) { 119 + if (v !== undefined) { 120 + clean[k] = v; 121 + } 122 + } 123 + return new Output(tag, clean); 124 + } 125 + 126 + // Each host primitive receives the resolved element, a walk function for 127 + // recursing into children, and the child render context. 128 + type Primitive = ( 129 + el: Element, 130 + walk: (node: unknown) => Promise<unknown>, 131 + ctx: RenderContext 132 + ) => Promise<unknown>; 133 + 134 + const HOST_PRIMITIVES: Record<string, Primitive> = { 135 + [Stack]: async (el, walk) => 136 + h("div", { 137 + ...el.props, 138 + key: el.key, 139 + children: await walk((el.props as any)?.children), 140 + }), 141 + [Row]: async (el, walk) => 142 + h("section", { 143 + ...el.props, 144 + key: el.key, 145 + children: await walk((el.props as any)?.children), 146 + }), 147 + [Grid]: async (el, walk) => 148 + h("table", { 149 + ...el.props, 150 + key: el.key, 151 + children: await walk((el.props as any)?.children), 152 + }), 153 + [Text]: async (el, walk) => 154 + h("span", { 155 + ...el.props, 156 + key: el.key, 157 + children: await walk((el.props as any)?.children), 158 + }), 159 + [Link]: async (el, walk) => 160 + h("a", { 161 + ...el.props, 162 + key: el.key, 163 + children: await walk((el.props as any)?.children), 164 + }), 165 + [List]: async (el, walk) => 166 + h("ul", { 167 + ...el.props, 168 + key: el.key, 169 + children: await walk((el.props as any)?.children), 170 + }), 171 + [Avatar]: async (el, walk) => 172 + h("img", { 173 + ...el.props, 174 + key: el.key, 175 + children: await walk((el.props as any)?.children), 176 + }), 177 + [Cover]: async (el, walk) => 178 + h("figure", { 179 + ...el.props, 180 + key: el.key, 181 + children: await walk((el.props as any)?.children), 182 + }), 183 + [Fragment]: async (el, walk) => 184 + h("frag", { 185 + key: el.key, 186 + children: await walk((el.props as any)?.children), 187 + }), 188 + [Throw]: async (el) => { 189 + throw new Error((el.props as Record<string, unknown>)?.message as string); 190 + }, 191 + [Maybe]: async (el, walk) => { 192 + const p = (el.props ?? {}) as Record<string, unknown>; 193 + try { 194 + return await walk(p.children); 195 + } catch (e) { 196 + if (e instanceof MissingError) { 197 + return p.fallback != null ? walk(p.fallback) : null; 198 + } 199 + throw e; 200 + } 201 + }, 202 + }; 203 + 204 + // ============================================================================ 205 + // Host walk loop 206 + // ============================================================================ 207 + // 208 + // The host drives rendering by walking the tree and calling render() on 209 + // each element. render() resolves one level: 210 + // 211 + // resolved=true → element is fully resolved, dispatch to host handler 212 + // resolved=false → component expanded into a subtree, keep walking 213 + // 214 + // walkNode recurses until the entire tree is reduced to Output nodes, 215 + // strings, nulls, and plain objects. 216 + 217 + async function renderToCompletion( 218 + node: unknown, 219 + options: RenderOptions, 220 + ctx: RenderContext, 221 + extra?: Record<string, Primitive> 222 + ): Promise<unknown> { 223 + const primitives = extra ? { ...HOST_PRIMITIVES, ...extra } : HOST_PRIMITIVES; 224 + return walkNode(node, options, ctx, primitives); 225 + } 226 + 227 + async function walkNode( 228 + node: unknown, 229 + options: RenderOptions, 230 + ctx: RenderContext, 231 + primitives: Record<string, Primitive> 232 + ): Promise<unknown> { 233 + if (node == null || typeof node !== "object") { 234 + return node; 235 + } else if (Array.isArray(node)) { 236 + return Promise.all(node.map((n) => walkNode(n, options, ctx, primitives))); 237 + } else if (isValidElement(node)) { 238 + const { 239 + resolved, 240 + node: out, 241 + context: outCtx, 242 + } = await render(node, ctx, options); 243 + if (resolved && isValidElement(out)) { 244 + const walk = (n: unknown) => walkNode(n, options, outCtx, primitives); 245 + try { 246 + return await primitives[out.type](out, walk, outCtx); 247 + } catch (e) { 248 + if (e instanceof MissingError) throw e; 249 + return `[Error: ${(e as any).message}]`; 250 + } 251 + } 252 + return walkNode(out, options, outCtx, primitives); 253 + } 254 + const obj = node as Record<string, unknown>; 255 + const entries = Object.entries(obj); 256 + const vals = await Promise.all( 257 + entries.map(([, v]) => walkNode(v, options, ctx, primitives)) 258 + ); 259 + return Object.fromEntries(entries.map(([k], i) => [k, vals[i]])); 260 + } 261 + 262 + // ============================================================================ 263 + // Fake atproto repository 264 + // ============================================================================ 265 + 266 + type TestMock = 267 + | ComponentRecord 268 + | PackRecord 269 + | Record<string, unknown> 270 + | ((...args: any[]) => unknown); // XRPC 271 + 272 + // Tests work against an in-memory record store. The host design system pack 273 + // is always present — it maps each host primitive NSID to a component record 274 + // with no body (no body = primitive = host renders directly). 275 + const HOST_PACK_URI: AtUriString = "at://did:plc:host/at.inlay.pack/host"; 276 + const HOST_RECORDS: Record<string, TestMock> = { 277 + // rkeys are semi-readable for tests, but are meant to be TIDs. 278 + ["at://did:plc:host/at.inlay.component/stck"]: { 279 + $type: "at.inlay.component", 280 + type: Stack, 281 + }, 282 + ["at://did:plc:host/at.inlay.component/rw"]: { 283 + $type: "at.inlay.component", 284 + type: Row, 285 + }, 286 + ["at://did:plc:host/at.inlay.component/grd"]: { 287 + $type: "at.inlay.component", 288 + type: Grid, 289 + }, 290 + ["at://did:plc:host/at.inlay.component/txt"]: { 291 + $type: "at.inlay.component", 292 + type: Text, 293 + }, 294 + ["at://did:plc:host/at.inlay.component/lnk"]: { 295 + $type: "at.inlay.component", 296 + type: Link, 297 + }, 298 + ["at://did:plc:host/at.inlay.component/lst"]: { 299 + $type: "at.inlay.component", 300 + type: List, 301 + }, 302 + ["at://did:plc:host/at.inlay.component/avtr"]: { 303 + $type: "at.inlay.component", 304 + type: Avatar, 305 + }, 306 + ["at://did:plc:host/at.inlay.component/cvr"]: { 307 + $type: "at.inlay.component", 308 + type: Cover, 309 + }, 310 + ["at://did:plc:host/at.inlay.component/myb"]: { 311 + $type: "at.inlay.component", 312 + type: Maybe, 313 + }, 314 + [HOST_PACK_URI]: { 315 + $type: "at.inlay.pack", 316 + name: "host", 317 + exports: [ 318 + { type: Stack, component: "at://did:plc:host/at.inlay.component/stck" }, 319 + { type: Row, component: "at://did:plc:host/at.inlay.component/rw" }, 320 + { type: Grid, component: "at://did:plc:host/at.inlay.component/grd" }, 321 + { type: Text, component: "at://did:plc:host/at.inlay.component/txt" }, 322 + { type: Link, component: "at://did:plc:host/at.inlay.component/lnk" }, 323 + { type: List, component: "at://did:plc:host/at.inlay.component/lst" }, 324 + { type: Avatar, component: "at://did:plc:host/at.inlay.component/avtr" }, 325 + { type: Cover, component: "at://did:plc:host/at.inlay.component/cvr" }, 326 + { type: Maybe, component: "at://did:plc:host/at.inlay.component/myb" }, 327 + ], 328 + }, 329 + }; 330 + 331 + // Test resolver: in-memory record bag with async fetch logging. 332 + // Fetches are async (microtick gap) so concurrent access is visible 333 + // in the log — both starts appear before either end. 334 + function testResolver( 335 + records: Record<AtUriString | `xrpc:${string}:${string}`, TestMock> 336 + ) { 337 + const log: string[] = []; 338 + const perRequestCache = new Map<string, Promise<unknown | null>>(); 339 + 340 + const resolver: Resolver = { 341 + fetchRecord: (uri) => { 342 + if (perRequestCache.has(uri)) return perRequestCache.get(uri)!; 343 + const p = (async () => { 344 + log.push(`fetch:start ${uri}`); 345 + await new Promise((resolve) => resolve(null)); 346 + log.push(`fetch:end ${uri}`); 347 + return records[uri] ?? null; 348 + })(); 349 + perRequestCache.set(uri, p); 350 + return p; 351 + }, 352 + xrpc: async (params) => { 353 + log.push(`xrpc:start ${params.type} ${params.nsid} -> ${params.did}`); 354 + await new Promise((resolve) => resolve(null)); 355 + log.push(`xrpc:end ${params.type} ${params.nsid} -> ${params.did}`); 356 + const handler = records[`xrpc:${params.did}:${params.nsid}`] as 357 + | ((p: typeof params) => unknown) 358 + | undefined; 359 + if (!handler) { 360 + throw new Error(`No xrpc handler for ${params.nsid}`); 361 + } 362 + // Simulate wire roundtrip: JSON + deserialize on input, serialize + JSON on output. 363 + // Handlers receive real Elements (slots are opaque at.inlay.Slot), 364 + // return real Elements. The mock handles ser/de like a real XRPC transport. 365 + const serverBody = deserializeTree( 366 + JSON.parse(JSON.stringify(params.body)) 367 + ) as typeof params.body; 368 + const result = handler({ ...params, body: serverBody }) as { 369 + node: unknown; 370 + [k: string]: unknown; 371 + }; 372 + return JSON.parse( 373 + JSON.stringify({ ...result, node: serializeTree(result.node) }) 374 + ); 375 + }, 376 + resolveLexicon: async (nsid) => { 377 + log.push(`lexicon ${nsid}`); 378 + return null; 379 + }, 380 + }; 381 + 382 + const options: RenderOptions = { resolver }; 383 + return { resolver, options, log }; 384 + } 385 + 386 + // Convenience: host pack + app-specific records. 387 + function world( 388 + records: Record<AtUriString | `xrpc:${string}:${string}`, TestMock> = {} 389 + ) { 390 + return testResolver({ ...HOST_RECORDS, ...records }); 391 + } 392 + 393 + // Assert log entries. "fetch <uri>" and "xrpc <desc>" expand to 394 + // start+end pairs. Use the raw :start/:end forms to assert ordering. 395 + function assertLog(log: string[], expected: string[]) { 396 + const actual = log.splice(0, log.length); 397 + const expanded: string[] = []; 398 + for (const entry of expected) { 399 + if (entry.startsWith("fetch ")) { 400 + const rest = entry.slice("fetch ".length); 401 + expanded.push(`fetch:start ${rest}`); 402 + expanded.push(`fetch:end ${rest}`); 403 + } else if (entry.startsWith("xrpc ")) { 404 + const rest = entry.slice("xrpc ".length); 405 + expanded.push(`xrpc:start ${rest}`); 406 + expanded.push(`xrpc:end ${rest}`); 407 + } else { 408 + expanded.push(entry); 409 + } 410 + } 411 + 412 + assert.deepEqual(actual, expanded); 413 + } 414 + 415 + function assertError(actual: unknown, message: string) { 416 + assert.equal(actual, `[Error: ${message}]`); 417 + } 418 + 419 + // ============================================================================ 420 + // 1. Primitives — components with no body 421 + // ============================================================================ 422 + // 423 + // A component record with no body is a primitive. render() resolves its 424 + // props and returns it as-is. The host maps it to native output: 425 + // e.g. test.host.Stack → <div>, test.host.Text → <span>, etc. 426 + 427 + describe("primitives", () => { 428 + it("passes through with resolved props", async () => { 429 + const stackComponent: ComponentRecord = { 430 + $type: "at.inlay.component", 431 + type: Stack, 432 + }; 433 + const { options } = world(); 434 + const output = await renderToCompletion( 435 + $(Stack, { gap: "medium" }), 436 + options, 437 + createContext(stackComponent) 438 + ); 439 + assert.deepEqual(output, h("div", { gap: "medium" })); 440 + }); 441 + }); 442 + 443 + // ============================================================================ 444 + // 2. Templates — stored element trees 445 + // ============================================================================ 446 + // 447 + // A template component stores a serialized element tree in its body. 448 + // render() deserializes the tree, resolves Binding placeholders against 449 + // the element's props, and returns the expanded subtree for further walking. 450 + // 451 + // Templates can also fetch atproto records. When a component declares a 452 + // `viewRecord` for a collection, and the element's `uri` prop points to 453 + // that collection, the record is fetched and merged into the binding scope. 454 + // 455 + // TODO: Currently props and record fields share namespace and can collide. 456 + // I haven't decided what to do about that. We could do `record.foo` instead. 457 + 458 + describe("templates", () => { 459 + it("expands static element tree", async () => { 460 + const greetingComponent: ComponentRecord = { 461 + $type: "at.inlay.component", 462 + type: Greeting, 463 + body: { 464 + $type: "at.inlay.component#bodyTemplate", 465 + node: serializeTree($(Text, { value: "Hello world" })), 466 + }, 467 + imports: [HOST_PACK_URI], 468 + }; 469 + 470 + const { options, log } = world({ 471 + ["at://did:plc:test/at.inlay.component/grtng"]: greetingComponent, 472 + ["at://did:plc:test/at.inlay.pack/app"]: { 473 + $type: "at.inlay.pack", 474 + name: "app", 475 + exports: [ 476 + { 477 + type: Greeting, 478 + component: "at://did:plc:test/at.inlay.component/grtng", 479 + }, 480 + ], 481 + }, 482 + }); 483 + 484 + const output = await renderToCompletion( 485 + $(Greeting, {}), 486 + options, 487 + createContext(greetingComponent) 488 + ); 489 + 490 + assert.deepEqual(output, h("span", { value: "Hello world" })); 491 + assertLog(log, [ 492 + `lexicon ${Greeting}`, 493 + `fetch ${HOST_PACK_URI}`, 494 + `fetch ${"at://did:plc:host/at.inlay.component/txt"}`, 495 + ]); 496 + }); 497 + 498 + it("resolves bindings against element props", async () => { 499 + const greetingComponent: ComponentRecord = { 500 + $type: "at.inlay.component", 501 + type: Greeting, 502 + body: { 503 + $type: "at.inlay.component#bodyTemplate", 504 + node: serializeTree( 505 + $(Row, { 506 + children: [ 507 + $(Text, { value: "Hello, " }), 508 + $(Text, { value: $(Binding, { path: ["name"] }) }), 509 + ], 510 + }) 511 + ), 512 + }, 513 + imports: [HOST_PACK_URI], 514 + }; 515 + 516 + const { options } = world({ 517 + ["at://did:plc:test/at.inlay.component/grtng"]: greetingComponent, 518 + ["at://did:plc:test/at.inlay.pack/app"]: { 519 + $type: "at.inlay.pack", 520 + name: "app", 521 + exports: [ 522 + { 523 + type: Greeting, 524 + component: "at://did:plc:test/at.inlay.component/grtng", 525 + }, 526 + ], 527 + }, 528 + }); 529 + 530 + const output = await renderToCompletion( 531 + $(Greeting, { name: "world" }), 532 + options, 533 + createContext(greetingComponent) 534 + ); 535 + 536 + assert.deepEqual( 537 + output, 538 + h("section", { 539 + children: [ 540 + h("span", { value: "Hello, " }), 541 + h("span", { value: "world" }), 542 + ], 543 + }) 544 + ); 545 + }); 546 + 547 + it("resolves nested property paths", async () => { 548 + const greetingComponent: ComponentRecord = { 549 + $type: "at.inlay.component", 550 + type: Greeting, 551 + body: { 552 + $type: "at.inlay.component#bodyTemplate", 553 + node: serializeTree( 554 + $(Text, { value: $(Binding, { path: ["user", "displayName"] }) }) 555 + ), 556 + }, 557 + imports: [HOST_PACK_URI], 558 + }; 559 + 560 + const { options, log } = world({ 561 + ["at://did:plc:test/at.inlay.component/grtng"]: greetingComponent, 562 + ["at://did:plc:test/at.inlay.pack/app"]: { 563 + $type: "at.inlay.pack", 564 + name: "app", 565 + exports: [ 566 + { 567 + type: Greeting, 568 + component: "at://did:plc:test/at.inlay.component/grtng", 569 + }, 570 + ], 571 + }, 572 + }); 573 + 574 + const output = await renderToCompletion( 575 + $(Greeting, { user: { displayName: "Alice" } }), 576 + options, 577 + createContext(greetingComponent) 578 + ); 579 + 580 + assert.deepEqual(output, h("span", { value: "Alice" })); 581 + assertLog(log, [ 582 + `lexicon ${Greeting}`, 583 + `fetch ${HOST_PACK_URI}`, 584 + `fetch ${"at://did:plc:host/at.inlay.component/txt"}`, 585 + ]); 586 + }); 587 + 588 + // --- Element props (children and named slots) --- 589 + 590 + it("renders children passed as props", async () => { 591 + const layoutComponent: ComponentRecord = { 592 + $type: "at.inlay.component", 593 + type: Layout, 594 + body: { 595 + $type: "at.inlay.component#bodyTemplate", 596 + node: serializeTree( 597 + $(Stack, { children: $(Binding, { path: ["children"] }) }) 598 + ), 599 + }, 600 + imports: [HOST_PACK_URI], 601 + }; 602 + 603 + const { options } = world(); 604 + const output = await renderToCompletion( 605 + $(Layout, { children: $(Text, { value: "hello" }) }), 606 + options, 607 + createContext(layoutComponent) 608 + ); 609 + 610 + assert.deepEqual( 611 + output, 612 + h("div", { children: h("span", { value: "hello" }) }) 613 + ); 614 + }); 615 + 616 + it("renders named element props", async () => { 617 + const layoutComponent: ComponentRecord = { 618 + $type: "at.inlay.component", 619 + type: Layout, 620 + body: { 621 + $type: "at.inlay.component#bodyTemplate", 622 + node: serializeTree( 623 + $(Row, { 624 + children: [ 625 + $(Binding, { path: ["left"] }), 626 + $(Binding, { path: ["right"] }), 627 + ], 628 + }) 629 + ), 630 + }, 631 + imports: [HOST_PACK_URI], 632 + }; 633 + 634 + const { options } = world(); 635 + const output = await renderToCompletion( 636 + $(Layout, { 637 + left: $(Text, { value: "L" }), 638 + right: $(Text, { value: "R" }), 639 + }), 640 + options, 641 + createContext(layoutComponent) 642 + ); 643 + 644 + assert.deepEqual( 645 + output, 646 + h("section", { 647 + children: [h("span", { value: "L" }), h("span", { value: "R" })], 648 + }) 649 + ); 650 + }); 651 + 652 + it("renders element props nested in objects", async () => { 653 + const layoutComponent: ComponentRecord = { 654 + $type: "at.inlay.component", 655 + type: Layout, 656 + body: { 657 + $type: "at.inlay.component#bodyTemplate", 658 + node: serializeTree( 659 + $(Row, { 660 + children: [ 661 + $(Binding, { path: ["layout", "left"] }), 662 + $(Binding, { path: ["layout", "right"] }), 663 + ], 664 + }) 665 + ), 666 + }, 667 + imports: [HOST_PACK_URI], 668 + }; 669 + 670 + const { options } = world(); 671 + const output = await renderToCompletion( 672 + $(Layout, { 673 + layout: { 674 + left: $(Text, { value: "L" }), 675 + right: $(Text, { value: "R" }), 676 + }, 677 + }), 678 + options, 679 + createContext(layoutComponent) 680 + ); 681 + 682 + assert.deepEqual( 683 + output, 684 + h("section", { 685 + children: [h("span", { value: "L" }), h("span", { value: "R" })], 686 + }) 687 + ); 688 + }); 689 + 690 + it("can render same child twice", async () => { 691 + const layoutComponent: ComponentRecord = { 692 + $type: "at.inlay.component", 693 + type: Layout, 694 + body: { 695 + $type: "at.inlay.component#bodyTemplate", 696 + node: serializeTree( 697 + $(Row, { 698 + children: [ 699 + $(Binding, { path: ["children"] }), 700 + $(Binding, { path: ["children"] }), 701 + ], 702 + }) 703 + ), 704 + }, 705 + imports: [HOST_PACK_URI], 706 + }; 707 + 708 + const { options } = world(); 709 + const output = await renderToCompletion( 710 + $(Layout, { children: $(Text, { value: "x" }) }), 711 + options, 712 + createContext(layoutComponent) 713 + ); 714 + 715 + assert.deepEqual( 716 + output, 717 + h("section", { 718 + children: [h("span", { value: "x" }), h("span", { value: "x" })], 719 + }) 720 + ); 721 + }); 722 + 723 + it("can ignore passed children", async () => { 724 + const layoutComponent: ComponentRecord = { 725 + $type: "at.inlay.component", 726 + type: Layout, 727 + body: { 728 + $type: "at.inlay.component#bodyTemplate", 729 + node: serializeTree($(Text, { value: "static" })), 730 + }, 731 + imports: [HOST_PACK_URI], 732 + }; 733 + 734 + const { options } = world(); 735 + const output = await renderToCompletion( 736 + $(Layout, { children: $(Text, { value: "never seen" }) }), 737 + options, 738 + createContext(layoutComponent) 739 + ); 740 + 741 + assert.deepEqual(output, h("span", { value: "static" })); 742 + }); 743 + 744 + // --- Record binding (requires viewRecord) --- 745 + 746 + it("fetches record and resolves bindings from it", async () => { 747 + // When a component declares viewRecord for a collection and the 748 + // element's uri points to that collection, the record is fetched 749 + // and merged into the binding scope. 750 + const postCardComponent: ComponentRecord = { 751 + $type: "at.inlay.component", 752 + type: PostCard, 753 + body: { 754 + $type: "at.inlay.component#bodyTemplate", 755 + node: serializeTree($(Text, { value: $(Binding, { path: ["text"] }) })), 756 + }, 757 + imports: [HOST_PACK_URI], 758 + view: [ 759 + { 760 + $type: "at.inlay.component#viewRecord", 761 + collection: "app.bsky.feed.post", 762 + }, 763 + ], 764 + }; 765 + 766 + const { options, log } = world({ 767 + ["at://did:plc:alice/app.bsky.feed.post/123"]: { 768 + text: "Hello world", 769 + createdAt: "2026-02-22T00:00:00Z", 770 + }, 771 + ["at://did:plc:test/at.inlay.component/grtng"]: postCardComponent, 772 + ["at://did:plc:test/at.inlay.pack/app"]: { 773 + $type: "at.inlay.pack", 774 + name: "app", 775 + exports: [ 776 + { 777 + type: PostCard, 778 + component: "at://did:plc:test/at.inlay.component/grtng", 779 + }, 780 + ], 781 + }, 782 + }); 783 + 784 + const output = await renderToCompletion( 785 + $(PostCard, { uri: "at://did:plc:alice/app.bsky.feed.post/123" }), 786 + options, 787 + createContext(postCardComponent) 788 + ); 789 + 790 + assert.deepEqual(output, h("span", { value: "Hello world" })); 791 + assertLog(log, [ 792 + `lexicon ${PostCard}`, 793 + // Pack and record fetched concurrently 794 + `fetch:start ${HOST_PACK_URI}`, 795 + `fetch:start ${"at://did:plc:alice/app.bsky.feed.post/123"}`, 796 + `fetch:end ${HOST_PACK_URI}`, 797 + `fetch:end ${"at://did:plc:alice/app.bsky.feed.post/123"}`, 798 + `fetch ${"at://did:plc:host/at.inlay.component/txt"}`, 799 + ]); 800 + }); 801 + 802 + it("skips record fetch when bindings only use uri parts", async () => { 803 + // at://did:plc:alice/app.bsky.feed.post/123 is parsed into 804 + // { did, collection, rkey, uri }. A Binding for ["did"] is satisfied 805 + // from these parts alone — no record fetch needed. 806 + const postCardComponent: ComponentRecord = { 807 + $type: "at.inlay.component", 808 + type: PostCard, 809 + body: { 810 + $type: "at.inlay.component#bodyTemplate", 811 + node: serializeTree($(Text, { value: $(Binding, { path: ["did"] }) })), 812 + }, 813 + imports: [HOST_PACK_URI], 814 + view: [ 815 + { 816 + $type: "at.inlay.component#viewRecord", 817 + collection: "app.bsky.feed.post", 818 + }, 819 + ], 820 + }; 821 + 822 + const { options, log } = world({ 823 + ["at://did:plc:test/at.inlay.component/grtng"]: postCardComponent, 824 + ["at://did:plc:test/at.inlay.pack/app"]: { 825 + $type: "at.inlay.pack", 826 + name: "app", 827 + exports: [ 828 + { 829 + type: PostCard, 830 + component: "at://did:plc:test/at.inlay.component/grtng", 831 + }, 832 + ], 833 + }, 834 + }); 835 + 836 + const output = await renderToCompletion( 837 + $(PostCard, { uri: "at://did:plc:alice/app.bsky.feed.post/123" }), 838 + options, 839 + createContext(postCardComponent) 840 + ); 841 + 842 + assert.deepEqual(output, h("span", { value: "did:plc:alice" })); 843 + assertLog(log, [ 844 + `lexicon ${PostCard}`, 845 + `fetch ${HOST_PACK_URI}`, 846 + `fetch ${"at://did:plc:host/at.inlay.component/txt"}`, 847 + ]); 848 + }); 849 + 850 + it("fetches record when binding needs data beyond uri parts", async () => { 851 + // A Binding for ["text"] can't be satisfied from uri parts alone 852 + // (did, collection, rkey, uri), so the record is fetched. 853 + const postCardComponent: ComponentRecord = { 854 + $type: "at.inlay.component", 855 + type: PostCard, 856 + body: { 857 + $type: "at.inlay.component#bodyTemplate", 858 + node: serializeTree( 859 + $(Stack, { 860 + children: $(Link, { uri: $(Binding, { path: ["text"] }) }), 861 + }) 862 + ), 863 + }, 864 + imports: [HOST_PACK_URI], 865 + view: [ 866 + { 867 + $type: "at.inlay.component#viewRecord", 868 + collection: "app.bsky.feed.post", 869 + }, 870 + ], 871 + }; 872 + 873 + const { options, log } = world({ 874 + ["at://did:plc:alice/app.bsky.feed.post/123"]: { 875 + text: "Hello world", 876 + }, 877 + ["at://did:plc:test/at.inlay.component/grtng"]: postCardComponent, 878 + ["at://did:plc:test/at.inlay.pack/app"]: { 879 + $type: "at.inlay.pack", 880 + name: "app", 881 + exports: [ 882 + { 883 + type: PostCard, 884 + component: "at://did:plc:test/at.inlay.component/grtng", 885 + }, 886 + ], 887 + }, 888 + }); 889 + 890 + const output = await renderToCompletion( 891 + $(PostCard, { uri: "at://did:plc:alice/app.bsky.feed.post/123" }), 892 + options, 893 + createContext(postCardComponent) 894 + ); 895 + 896 + assert.deepEqual( 897 + output, 898 + h("div", { children: h("a", { uri: "Hello world" }) }) 899 + ); 900 + assertLog(log, [ 901 + `lexicon ${PostCard}`, 902 + // Pack and record fetched concurrently 903 + `fetch:start ${HOST_PACK_URI}`, 904 + `fetch:start ${"at://did:plc:alice/app.bsky.feed.post/123"}`, 905 + `fetch:end ${HOST_PACK_URI}`, 906 + `fetch:end ${"at://did:plc:alice/app.bsky.feed.post/123"}`, 907 + `fetch ${"at://did:plc:host/at.inlay.component/stck"}`, 908 + `fetch ${"at://did:plc:host/at.inlay.component/lnk"}`, 909 + ]); 910 + }); 911 + }); 912 + 913 + // ============================================================================ 914 + // 3. Pack resolution order 915 + // ============================================================================ 916 + // 917 + // A component's `imports` is an ordered list of pack URIs. When resolving 918 + // a type NSID, packs are searched in order — the first pack that exports 919 + // the type wins. This lets authors override primitives or shadow types. 920 + 921 + describe("pack ordering", () => { 922 + // Two packs each export Greeting with different templates: 923 + // "fancy" → <Text value="Greetings, friend!" /> 924 + // "casual" → <Text value="Hi" /> 925 + // The import list order determines which one wins. 926 + 927 + const fancyGreeting: ComponentRecord = { 928 + $type: "at.inlay.component", 929 + type: Greeting, 930 + body: { 931 + $type: "at.inlay.component#bodyTemplate", 932 + node: serializeTree($(Text, { value: "Greetings, friend!" })), 933 + }, 934 + imports: [HOST_PACK_URI], 935 + }; 936 + 937 + const casualGreeting: ComponentRecord = { 938 + $type: "at.inlay.component", 939 + type: Greeting, 940 + body: { 941 + $type: "at.inlay.component#bodyTemplate", 942 + node: serializeTree($(Text, { value: "Hi" })), 943 + }, 944 + imports: [HOST_PACK_URI], 945 + }; 946 + 947 + it("first pack wins", async () => { 948 + // Fancy is listed first, so <Greeting> resolves to "Greetings, friend!" 949 + const rootComponent: ComponentRecord = { 950 + $type: "at.inlay.component", 951 + type: Root, 952 + body: { 953 + $type: "at.inlay.component#bodyTemplate", 954 + node: serializeTree($(Greeting, {})), 955 + }, 956 + imports: [ 957 + "at://did:plc:test/at.inlay.pack/fancy", 958 + "at://did:plc:test/at.inlay.pack/casual", 959 + ] as AtUriString[], 960 + }; 961 + 962 + const { options, log } = testResolver({ 963 + ...HOST_RECORDS, 964 + ["at://did:plc:test/at.inlay.component/fncy"]: fancyGreeting, 965 + ["at://did:plc:test/at.inlay.component/csl"]: casualGreeting, 966 + ["at://did:plc:test/at.inlay.pack/fancy"]: { 967 + $type: "at.inlay.pack", 968 + name: "fancy", 969 + exports: [ 970 + { 971 + type: Greeting, 972 + component: "at://did:plc:test/at.inlay.component/fncy", 973 + }, 974 + ], 975 + }, 976 + ["at://did:plc:test/at.inlay.pack/casual"]: { 977 + $type: "at.inlay.pack", 978 + name: "casual", 979 + exports: [ 980 + { 981 + type: Greeting, 982 + component: "at://did:plc:test/at.inlay.component/csl", 983 + }, 984 + ], 985 + }, 986 + }); 987 + 988 + const output = await renderToCompletion( 989 + $(Root, {}), 990 + options, 991 + createContext(rootComponent) 992 + ); 993 + 994 + assert.deepEqual(output, h("span", { value: "Greetings, friend!" })); 995 + assertLog(log, [ 996 + `lexicon ${Root}`, 997 + // Both packs fetched concurrently 998 + `fetch:start ${"at://did:plc:test/at.inlay.pack/fancy"}`, 999 + `fetch:start ${"at://did:plc:test/at.inlay.pack/casual"}`, 1000 + `fetch:end ${"at://did:plc:test/at.inlay.pack/fancy"}`, 1001 + `fetch:end ${"at://did:plc:test/at.inlay.pack/casual"}`, 1002 + // Fancy pack wins — its Greeting component is fetched 1003 + `fetch ${"at://did:plc:test/at.inlay.component/fncy"}`, 1004 + `lexicon ${Greeting}`, 1005 + `fetch ${HOST_PACK_URI}`, 1006 + `fetch ${"at://did:plc:host/at.inlay.component/txt"}`, 1007 + ]); 1008 + }); 1009 + 1010 + it("falls through to later packs", async () => { 1011 + // First pack doesn't export Greeting, so it falls through to casual. 1012 + const rootComponent: ComponentRecord = { 1013 + $type: "at.inlay.component", 1014 + type: Root, 1015 + body: { 1016 + $type: "at.inlay.component#bodyTemplate", 1017 + node: serializeTree($(Greeting, {})), 1018 + }, 1019 + imports: [ 1020 + "at://did:plc:test/at.inlay.pack/empty", 1021 + "at://did:plc:test/at.inlay.pack/casual", 1022 + ] as AtUriString[], 1023 + }; 1024 + 1025 + const { options, log } = testResolver({ 1026 + ...HOST_RECORDS, 1027 + ["at://did:plc:test/at.inlay.component/csl"]: casualGreeting, 1028 + ["at://did:plc:test/at.inlay.pack/empty"]: { 1029 + $type: "at.inlay.pack", 1030 + name: "empty", 1031 + exports: [], 1032 + }, 1033 + ["at://did:plc:test/at.inlay.pack/casual"]: { 1034 + $type: "at.inlay.pack", 1035 + name: "casual", 1036 + exports: [ 1037 + { 1038 + type: Greeting, 1039 + component: "at://did:plc:test/at.inlay.component/csl", 1040 + }, 1041 + ], 1042 + }, 1043 + }); 1044 + 1045 + const output = await renderToCompletion( 1046 + $(Root, {}), 1047 + options, 1048 + createContext(rootComponent) 1049 + ); 1050 + 1051 + assert.deepEqual(output, h("span", { value: "Hi" })); 1052 + assertLog(log, [ 1053 + `lexicon ${Root}`, 1054 + // Both packs fetched concurrently, empty one has no match 1055 + `fetch:start ${"at://did:plc:test/at.inlay.pack/empty"}`, 1056 + `fetch:start ${"at://did:plc:test/at.inlay.pack/casual"}`, 1057 + `fetch:end ${"at://did:plc:test/at.inlay.pack/empty"}`, 1058 + `fetch:end ${"at://did:plc:test/at.inlay.pack/casual"}`, 1059 + // Falls through to casual pack 1060 + `fetch ${"at://did:plc:test/at.inlay.component/csl"}`, 1061 + `lexicon ${Greeting}`, 1062 + `fetch ${HOST_PACK_URI}`, 1063 + `fetch ${"at://did:plc:host/at.inlay.component/txt"}`, 1064 + ]); 1065 + }); 1066 + 1067 + it("produces Throw when no pack exports the type", async () => { 1068 + const unknown = "test.app.DoesNotExist"; 1069 + const rootComponent: ComponentRecord = { 1070 + $type: "at.inlay.component", 1071 + type: Root, 1072 + body: { 1073 + $type: "at.inlay.component#bodyTemplate", 1074 + node: serializeTree($(unknown, {})), 1075 + }, 1076 + imports: [HOST_PACK_URI], 1077 + }; 1078 + 1079 + const { options } = world(); 1080 + 1081 + const output = await renderToCompletion( 1082 + $(Root, {}), 1083 + options, 1084 + createContext(rootComponent) 1085 + ); 1086 + assertError(output, `No pack exports type: ${unknown}`); 1087 + }); 1088 + }); 1089 + 1090 + // ============================================================================ 1091 + // 4. Import isolation — each component has its own package.json 1092 + // ============================================================================ 1093 + // 1094 + // Each component record carries its own imports list (like package.json). 1095 + // When a component renders, its children resolve types through the component's 1096 + // imports — not the caller's. One NSID can get resolved differently within a tree. 1097 + 1098 + describe("import isolation", () => { 1099 + // Two implementations of Card: vertical wraps in Stack, horizontal in Row. 1100 + const verticalCard: ComponentRecord = { 1101 + $type: "at.inlay.component", 1102 + type: Card, 1103 + body: { 1104 + $type: "at.inlay.component#bodyTemplate", 1105 + node: serializeTree( 1106 + $(Stack, { 1107 + children: $(Text, { value: $(Binding, { path: ["title"] }) }), 1108 + }) 1109 + ), 1110 + }, 1111 + imports: [HOST_PACK_URI], 1112 + }; 1113 + 1114 + const horizontalCard: ComponentRecord = { 1115 + $type: "at.inlay.component", 1116 + type: Card, 1117 + body: { 1118 + $type: "at.inlay.component#bodyTemplate", 1119 + node: serializeTree( 1120 + $(Row, { 1121 + children: $(Text, { value: $(Binding, { path: ["title"] }) }), 1122 + }) 1123 + ), 1124 + }, 1125 + imports: [HOST_PACK_URI], 1126 + }; 1127 + 1128 + const ISOLATION_RECORDS: Record<string, TestMock> = { 1129 + ...HOST_RECORDS, 1130 + ["at://did:plc:test/at.inlay.component/vcrd"]: verticalCard, 1131 + ["at://did:plc:test/at.inlay.component/hcrd"]: horizontalCard, 1132 + ["at://did:plc:test/at.inlay.pack/vertical"]: { 1133 + $type: "at.inlay.pack", 1134 + name: "vertical", 1135 + exports: [ 1136 + { 1137 + type: Card, 1138 + component: "at://did:plc:test/at.inlay.component/vcrd", 1139 + }, 1140 + ], 1141 + }, 1142 + ["at://did:plc:test/at.inlay.pack/horizontal"]: { 1143 + $type: "at.inlay.pack", 1144 + name: "horizontal", 1145 + exports: [ 1146 + { 1147 + type: Card, 1148 + component: "at://did:plc:test/at.inlay.component/hcrd", 1149 + }, 1150 + ], 1151 + }, 1152 + }; 1153 + 1154 + it("component resolves types through its own imports", async () => { 1155 + // Page imports vertical. Its <Card> resolves to Stack layout. 1156 + const pageComponent: ComponentRecord = { 1157 + $type: "at.inlay.component", 1158 + type: Page, 1159 + body: { 1160 + $type: "at.inlay.component#bodyTemplate", 1161 + node: serializeTree($(Card, { title: "hello" })), 1162 + }, 1163 + imports: ["at://did:plc:test/at.inlay.pack/vertical"] as AtUriString[], 1164 + }; 1165 + 1166 + const { options } = testResolver(ISOLATION_RECORDS); 1167 + 1168 + const output = await renderToCompletion( 1169 + $(Page, {}), 1170 + options, 1171 + createContext(pageComponent) 1172 + ); 1173 + 1174 + // vertical Card → Stack → <div> 1175 + assert.deepEqual( 1176 + output, 1177 + h("div", { children: h("span", { value: "hello" }) }) 1178 + ); 1179 + }); 1180 + 1181 + it("parent and child resolve same type independently", async () => { 1182 + // Page imports vertical, Greeting imports horizontal. 1183 + // Both render <Card> — each gets their own version. 1184 + const greetingComponent: ComponentRecord = { 1185 + $type: "at.inlay.component", 1186 + type: Greeting, 1187 + body: { 1188 + $type: "at.inlay.component#bodyTemplate", 1189 + node: serializeTree($(Card, { title: "inner" })), 1190 + }, 1191 + imports: ["at://did:plc:test/at.inlay.pack/horizontal"] as AtUriString[], 1192 + }; 1193 + 1194 + const pageComponent: ComponentRecord = { 1195 + $type: "at.inlay.component", 1196 + type: Page, 1197 + body: { 1198 + $type: "at.inlay.component#bodyTemplate", 1199 + node: serializeTree( 1200 + $(Stack, { 1201 + children: [$(Card, { title: "outer" }), $(Greeting, {})], 1202 + }) 1203 + ), 1204 + }, 1205 + imports: [ 1206 + HOST_PACK_URI, 1207 + "at://did:plc:test/at.inlay.pack/vertical", 1208 + "at://did:plc:test/at.inlay.pack/app", 1209 + ] as AtUriString[], 1210 + }; 1211 + 1212 + const { options } = testResolver({ 1213 + ...ISOLATION_RECORDS, 1214 + ["at://did:plc:test/at.inlay.component/grtng"]: greetingComponent, 1215 + ["at://did:plc:test/at.inlay.pack/app"]: { 1216 + $type: "at.inlay.pack", 1217 + name: "app", 1218 + exports: [ 1219 + { 1220 + type: Greeting, 1221 + component: "at://did:plc:test/at.inlay.component/grtng", 1222 + }, 1223 + ], 1224 + }, 1225 + }); 1226 + 1227 + const output = await renderToCompletion( 1228 + $(Page, {}), 1229 + options, 1230 + createContext(pageComponent) 1231 + ); 1232 + 1233 + assert.deepEqual( 1234 + output, 1235 + h("div", { 1236 + children: [ 1237 + // Page's Card → vertical → Stack → <div> 1238 + h("div", { children: h("span", { value: "outer" }) }), 1239 + // Greeting's Card → horizontal → Row → <section> 1240 + h("section", { children: h("span", { value: "inner" }) }), 1241 + ], 1242 + }) 1243 + ); 1244 + }); 1245 + 1246 + it("missing import fails even if parent has the type", async () => { 1247 + // Page imports vertical (which has Card). Greeting has no imports. 1248 + // Greeting's <Card> fails — it can't see Page's imports. 1249 + const greetingComponent: ComponentRecord = { 1250 + $type: "at.inlay.component", 1251 + type: Greeting, 1252 + body: { 1253 + $type: "at.inlay.component#bodyTemplate", 1254 + node: serializeTree($(Card, { title: "unreachable" })), 1255 + }, 1256 + imports: [], 1257 + }; 1258 + 1259 + const pageComponent: ComponentRecord = { 1260 + $type: "at.inlay.component", 1261 + type: Page, 1262 + body: { 1263 + $type: "at.inlay.component#bodyTemplate", 1264 + node: serializeTree($(Greeting, {})), 1265 + }, 1266 + imports: [ 1267 + "at://did:plc:test/at.inlay.pack/vertical", 1268 + "at://did:plc:test/at.inlay.pack/app", 1269 + ] as AtUriString[], 1270 + }; 1271 + 1272 + const { options } = testResolver({ 1273 + ...ISOLATION_RECORDS, 1274 + ["at://did:plc:test/at.inlay.component/grtng"]: greetingComponent, 1275 + ["at://did:plc:test/at.inlay.pack/app"]: { 1276 + $type: "at.inlay.pack", 1277 + name: "app", 1278 + exports: [ 1279 + { 1280 + type: Greeting, 1281 + component: "at://did:plc:test/at.inlay.component/grtng", 1282 + }, 1283 + ], 1284 + }, 1285 + }); 1286 + 1287 + const output = await renderToCompletion( 1288 + $(Page, {}), 1289 + options, 1290 + createContext(pageComponent) 1291 + ); 1292 + 1293 + assertError(output, `No pack exports type: ${Card}`); 1294 + }); 1295 + 1296 + it("composed children resolve through caller, not callee", async () => { 1297 + // Page passes <Card> as a child to Greeting. The passed-in Card 1298 + // resolves through Page's imports (vertical), not Greeting's. 1299 + const greetingComponent: ComponentRecord = { 1300 + $type: "at.inlay.component", 1301 + type: Greeting, 1302 + body: { 1303 + $type: "at.inlay.component#bodyTemplate", 1304 + node: serializeTree( 1305 + $(Stack, { 1306 + children: [ 1307 + $(Card, { title: "greeting's own" }), 1308 + $(Binding, { path: ["children"] }), 1309 + ], 1310 + }) 1311 + ), 1312 + }, 1313 + imports: [ 1314 + HOST_PACK_URI, 1315 + "at://did:plc:test/at.inlay.pack/horizontal", 1316 + ] as AtUriString[], 1317 + }; 1318 + 1319 + const pageComponent: ComponentRecord = { 1320 + $type: "at.inlay.component", 1321 + type: Page, 1322 + body: { 1323 + $type: "at.inlay.component#bodyTemplate", 1324 + node: serializeTree( 1325 + $(Greeting, { 1326 + children: $(Card, { title: "from caller" }), 1327 + }) 1328 + ), 1329 + }, 1330 + imports: [ 1331 + HOST_PACK_URI, 1332 + "at://did:plc:test/at.inlay.pack/vertical", 1333 + "at://did:plc:test/at.inlay.pack/app", 1334 + ] as AtUriString[], 1335 + }; 1336 + 1337 + const { options } = testResolver({ 1338 + ...ISOLATION_RECORDS, 1339 + ["at://did:plc:test/at.inlay.component/grtng"]: greetingComponent, 1340 + ["at://did:plc:test/at.inlay.pack/app"]: { 1341 + $type: "at.inlay.pack", 1342 + name: "app", 1343 + exports: [ 1344 + { 1345 + type: Greeting, 1346 + component: "at://did:plc:test/at.inlay.component/grtng", 1347 + }, 1348 + ], 1349 + }, 1350 + }); 1351 + 1352 + const output = await renderToCompletion( 1353 + $(Page, {}), 1354 + options, 1355 + createContext(pageComponent) 1356 + ); 1357 + 1358 + // Greeting's own Card → horizontal → Row → <section> 1359 + // Page's passed Card → vertical → Stack → <div> 1360 + assert.deepEqual( 1361 + output, 1362 + h("div", { 1363 + children: [ 1364 + h("section", { children: h("span", { value: "greeting's own" }) }), 1365 + h("div", { children: h("span", { value: "from caller" }) }), 1366 + ], 1367 + }) 1368 + ); 1369 + }); 1370 + 1371 + it("passed child resolves even when callee can't", async () => { 1372 + // Greeting has no Card in its imports. Page passes <Card> as a child. 1373 + // Card resolves through Page's imports — Greeting doesn't need it. 1374 + const greetingComponent: ComponentRecord = { 1375 + $type: "at.inlay.component", 1376 + type: Greeting, 1377 + body: { 1378 + $type: "at.inlay.component#bodyTemplate", 1379 + node: serializeTree( 1380 + $(Stack, { children: $(Binding, { path: ["children"] }) }) 1381 + ), 1382 + }, 1383 + imports: [HOST_PACK_URI] as AtUriString[], 1384 + }; 1385 + 1386 + const pageComponent: ComponentRecord = { 1387 + $type: "at.inlay.component", 1388 + type: Page, 1389 + body: { 1390 + $type: "at.inlay.component#bodyTemplate", 1391 + node: serializeTree( 1392 + $(Greeting, { 1393 + children: $(Card, { title: "from caller" }), 1394 + }) 1395 + ), 1396 + }, 1397 + imports: [ 1398 + HOST_PACK_URI, 1399 + "at://did:plc:test/at.inlay.pack/vertical", 1400 + "at://did:plc:test/at.inlay.pack/app", 1401 + ] as AtUriString[], 1402 + }; 1403 + 1404 + const { options } = testResolver({ 1405 + ...ISOLATION_RECORDS, 1406 + ["at://did:plc:test/at.inlay.component/grtng"]: greetingComponent, 1407 + ["at://did:plc:test/at.inlay.pack/app"]: { 1408 + $type: "at.inlay.pack", 1409 + name: "app", 1410 + exports: [ 1411 + { 1412 + type: Greeting, 1413 + component: "at://did:plc:test/at.inlay.component/grtng", 1414 + }, 1415 + ], 1416 + }, 1417 + }); 1418 + 1419 + const output = await renderToCompletion( 1420 + $(Page, {}), 1421 + options, 1422 + createContext(pageComponent) 1423 + ); 1424 + 1425 + // Card resolves through Page's vertical imports → Stack → <div> 1426 + assert.deepEqual( 1427 + output, 1428 + h("div", { 1429 + children: h("div", { children: h("span", { value: "from caller" }) }), 1430 + }) 1431 + ); 1432 + }); 1433 + 1434 + it("slot isolation works through indirection", async () => { 1435 + // Page passes <Card> through Indirection (which just renders children 1436 + // and has no imports). Card still resolves through Page's imports. 1437 + const indirectionComponent: ComponentRecord = { 1438 + $type: "at.inlay.component", 1439 + type: Indirection, 1440 + body: { 1441 + $type: "at.inlay.component#bodyTemplate", 1442 + node: serializeTree($(Binding, { path: ["children"] })), 1443 + }, 1444 + imports: [], 1445 + }; 1446 + 1447 + const pageComponent: ComponentRecord = { 1448 + $type: "at.inlay.component", 1449 + type: Page, 1450 + body: { 1451 + $type: "at.inlay.component#bodyTemplate", 1452 + node: serializeTree( 1453 + $(Indirection, { 1454 + children: $(Card, { title: "through" }), 1455 + }) 1456 + ), 1457 + }, 1458 + imports: [ 1459 + "at://did:plc:test/at.inlay.pack/vertical", 1460 + "at://did:plc:test/at.inlay.pack/app", 1461 + ] as AtUriString[], 1462 + }; 1463 + 1464 + const { options } = testResolver({ 1465 + ...ISOLATION_RECORDS, 1466 + ["at://did:plc:test/at.inlay.component/indr"]: indirectionComponent, 1467 + ["at://did:plc:test/at.inlay.pack/app"]: { 1468 + $type: "at.inlay.pack", 1469 + name: "app", 1470 + exports: [ 1471 + { 1472 + type: Indirection, 1473 + component: "at://did:plc:test/at.inlay.component/indr", 1474 + }, 1475 + ], 1476 + }, 1477 + }); 1478 + 1479 + const output = await renderToCompletion( 1480 + $(Page, {}), 1481 + options, 1482 + createContext(pageComponent) 1483 + ); 1484 + 1485 + // Card resolves through Page's vertical imports → Stack → <div> 1486 + assert.deepEqual( 1487 + output, 1488 + h("div", { children: h("span", { value: "through" }) }) 1489 + ); 1490 + }); 1491 + }); 1492 + 1493 + // ============================================================================ 1494 + // 5. External components — bodyExternal with XRPC 1495 + // ============================================================================ 1496 + // 1497 + // An external component delegates rendering to an XRPC service addressed 1498 + // by a DID. Where templates are static trees with Binding placeholders, 1499 + // externals run code — the service receives the element's props, does 1500 + // whatever it wants, and returns a tree. 1501 + // 1502 + // Children passed by the caller survive the round-trip: they're stashed 1503 + // before the call and restored afterward, resolving through the caller. 1504 + 1505 + describe("external components", () => { 1506 + it("renders through an XRPC service", async () => { 1507 + // External Greeting: service receives { name } and returns a tree. 1508 + // Same result as the template Greeting, but built with code. 1509 + const greetingComponent: ComponentRecord = { 1510 + $type: "at.inlay.component", 1511 + type: Greeting, 1512 + body: { 1513 + $type: "at.inlay.component#bodyExternal", 1514 + did: SERVICE_DID, 1515 + }, 1516 + imports: [HOST_PACK_URI], 1517 + }; 1518 + 1519 + const { options, log } = world({ 1520 + [`xrpc:${SERVICE_DID}:${Greeting}`]: (params: { 1521 + body?: Record<string, unknown>; 1522 + }) => ({ 1523 + node: $(Row, { 1524 + children: [ 1525 + $(Text, { value: "Hello, " }), 1526 + $(Text, { value: params.body!.name as string }), 1527 + ], 1528 + }), 1529 + }), 1530 + }); 1531 + 1532 + const output = await renderToCompletion( 1533 + $(Greeting, { name: "world" }), 1534 + options, 1535 + createContext(greetingComponent) 1536 + ); 1537 + 1538 + assert.deepEqual( 1539 + output, 1540 + h("section", { 1541 + children: [ 1542 + h("span", { value: "Hello, " }), 1543 + h("span", { value: "world" }), 1544 + ], 1545 + }) 1546 + ); 1547 + assertLog(log, [ 1548 + `lexicon ${Greeting}`, 1549 + `xrpc procedure ${Greeting} -> ${SERVICE_DID}`, 1550 + `fetch ${HOST_PACK_URI}`, 1551 + `fetch ${"at://did:plc:host/at.inlay.component/rw"}`, 1552 + `fetch ${"at://did:plc:host/at.inlay.component/txt"}`, 1553 + ]); 1554 + }); 1555 + 1556 + it("renders caller's children", async () => { 1557 + // Layout receives children, wraps them in a Stack. 1558 + const layoutComponent: ComponentRecord = { 1559 + $type: "at.inlay.component", 1560 + type: Layout, 1561 + body: { 1562 + $type: "at.inlay.component#bodyExternal", 1563 + did: SERVICE_DID, 1564 + }, 1565 + imports: [HOST_PACK_URI], 1566 + }; 1567 + 1568 + const { options } = world({ 1569 + [`xrpc:${SERVICE_DID}:${Layout}`]: (params: { 1570 + body?: Record<string, unknown>; 1571 + }) => ({ 1572 + node: $(Stack, { children: params.body!.children as Element }), 1573 + }), 1574 + }); 1575 + 1576 + const output = await renderToCompletion( 1577 + $(Layout, { children: $(Text, { value: "hello" }) }), 1578 + options, 1579 + createContext(layoutComponent) 1580 + ); 1581 + 1582 + assert.deepEqual( 1583 + output, 1584 + h("div", { 1585 + children: h("frag", { children: h("span", { value: "hello" }) }), 1586 + }) 1587 + ); 1588 + }); 1589 + 1590 + it("service interleaves its own elements with caller's children", async () => { 1591 + // Service adds a divider between each caller child. 1592 + const layoutComponent: ComponentRecord = { 1593 + $type: "at.inlay.component", 1594 + type: Layout, 1595 + body: { 1596 + $type: "at.inlay.component#bodyExternal", 1597 + did: SERVICE_DID, 1598 + }, 1599 + imports: [HOST_PACK_URI], 1600 + }; 1601 + 1602 + const { options } = world({ 1603 + [`xrpc:${SERVICE_DID}:${Layout}`]: (params: { 1604 + body?: Record<string, unknown>; 1605 + }) => { 1606 + const children = params.body!.children as Element[]; 1607 + return { 1608 + node: $(Stack, { 1609 + children: [children[0], $(Text, { value: "---" }), children[1]], 1610 + }), 1611 + }; 1612 + }, 1613 + }); 1614 + 1615 + const output = await renderToCompletion( 1616 + $(Layout, { 1617 + children: [$(Text, { value: "first" }), $(Text, { value: "second" })], 1618 + }), 1619 + options, 1620 + createContext(layoutComponent) 1621 + ); 1622 + 1623 + assert.deepEqual( 1624 + output, 1625 + h("div", { 1626 + children: [ 1627 + h("frag", { children: h("span", { value: "first" }) }), 1628 + h("span", { value: "---" }), 1629 + h("frag", { children: h("span", { value: "second" }) }), 1630 + ], 1631 + }) 1632 + ); 1633 + }); 1634 + 1635 + it("caller's children resolve through caller's imports", async () => { 1636 + // Page passes <Card> to an external Layout. The Card resolves through 1637 + // Page's imports (vertical), not the service's — same as templates. 1638 + const layoutComponent: ComponentRecord = { 1639 + $type: "at.inlay.component", 1640 + type: Layout, 1641 + body: { 1642 + $type: "at.inlay.component#bodyExternal", 1643 + did: SERVICE_DID, 1644 + }, 1645 + imports: [HOST_PACK_URI], 1646 + }; 1647 + 1648 + const pageComponent: ComponentRecord = { 1649 + $type: "at.inlay.component", 1650 + type: Page, 1651 + body: { 1652 + $type: "at.inlay.component#bodyTemplate", 1653 + node: serializeTree( 1654 + $(Layout, { children: $(Card, { title: "from caller" }) }) 1655 + ), 1656 + }, 1657 + imports: [ 1658 + HOST_PACK_URI, 1659 + "at://did:plc:test/at.inlay.pack/vertical", 1660 + "at://did:plc:test/at.inlay.pack/app", 1661 + ] as AtUriString[], 1662 + }; 1663 + 1664 + const verticalCard: ComponentRecord = { 1665 + $type: "at.inlay.component", 1666 + type: Card, 1667 + body: { 1668 + $type: "at.inlay.component#bodyTemplate", 1669 + node: serializeTree( 1670 + $(Stack, { 1671 + children: $(Text, { value: $(Binding, { path: ["title"] }) }), 1672 + }) 1673 + ), 1674 + }, 1675 + imports: [HOST_PACK_URI], 1676 + }; 1677 + 1678 + const { options } = testResolver({ 1679 + ...HOST_RECORDS, 1680 + ["at://did:plc:test/at.inlay.component/vcrd"]: verticalCard, 1681 + ["at://did:plc:test/at.inlay.pack/vertical"]: { 1682 + $type: "at.inlay.pack", 1683 + name: "vertical", 1684 + exports: [ 1685 + { 1686 + type: Card, 1687 + component: "at://did:plc:test/at.inlay.component/vcrd", 1688 + }, 1689 + ], 1690 + }, 1691 + ["at://did:plc:test/at.inlay.component/lyout"]: layoutComponent, 1692 + ["at://did:plc:test/at.inlay.pack/app"]: { 1693 + $type: "at.inlay.pack", 1694 + name: "app", 1695 + exports: [ 1696 + { 1697 + type: Layout, 1698 + component: "at://did:plc:test/at.inlay.component/lyout", 1699 + }, 1700 + ], 1701 + }, 1702 + [`xrpc:${SERVICE_DID}:${Layout}`]: (params: { 1703 + body?: Record<string, unknown>; 1704 + }) => ({ 1705 + node: $(Stack, { children: params.body!.children as Element }), 1706 + }), 1707 + }); 1708 + 1709 + const output = await renderToCompletion( 1710 + $(Page, {}), 1711 + options, 1712 + createContext(pageComponent) 1713 + ); 1714 + 1715 + // Card resolves through Page's vertical imports → Stack → <div> 1716 + assert.deepEqual( 1717 + output, 1718 + h("div", { 1719 + children: h("frag", { 1720 + children: h("div", { children: h("span", { value: "from caller" }) }), 1721 + }), 1722 + }) 1723 + ); 1724 + }); 1725 + 1726 + it("server can re-key caller elements", async () => { 1727 + // Both keys are preserved: the caller's key goes on the Fragment 1728 + // wrapper, the server's key goes on the restored element inside. 1729 + const layoutComponent: ComponentRecord = { 1730 + $type: "at.inlay.component", 1731 + type: Layout, 1732 + body: { 1733 + $type: "at.inlay.component#bodyExternal", 1734 + did: SERVICE_DID, 1735 + }, 1736 + imports: [HOST_PACK_URI], 1737 + }; 1738 + 1739 + const { options } = world({ 1740 + [`xrpc:${SERVICE_DID}:${Layout}`]: (params: { 1741 + body?: Record<string, unknown>; 1742 + }) => { 1743 + const children = params.body!.children as Element[]; 1744 + return { 1745 + node: $(Stack, { children: [{ ...children[0], key: "server-key" }] }), 1746 + }; 1747 + }, 1748 + }); 1749 + 1750 + const output = await renderToCompletion( 1751 + $(Layout, { 1752 + children: [$(Text, { key: "caller-key", value: "hello" })], 1753 + }), 1754 + options, 1755 + createContext(layoutComponent) 1756 + ); 1757 + 1758 + assert.deepEqual( 1759 + output, 1760 + h("div", { 1761 + children: [ 1762 + h("frag", { 1763 + key: "caller-key", 1764 + children: h("span", { key: "server-key", value: "hello" }), 1765 + }), 1766 + ], 1767 + }) 1768 + ); 1769 + }); 1770 + }); 1771 + 1772 + // ============================================================================ 1773 + // 6. Error handling — Throw elements 1774 + // ============================================================================ 1775 + // 1776 + // Errors during render are caught and turned into at.inlay.Throw elements. 1777 + // For now, the seam where the error is displayed is controlled by the host. 1778 + // In the future, a Catch boundary may be added to let the user control this. 1779 + 1780 + describe("error handling", () => { 1781 + it("infinite recursion is caught", async () => { 1782 + const loopComponent: ComponentRecord = { 1783 + $type: "at.inlay.component", 1784 + type: Loop, 1785 + body: { 1786 + $type: "at.inlay.component#bodyTemplate", 1787 + node: serializeTree($(Loop, {})), 1788 + }, 1789 + imports: ["at://did:plc:test/at.inlay.pack/loop"] as AtUriString[], 1790 + }; 1791 + 1792 + const { options } = testResolver({ 1793 + ["at://did:plc:test/at.inlay.component/lp"]: loopComponent, 1794 + ["at://did:plc:test/at.inlay.pack/loop"]: { 1795 + $type: "at.inlay.pack", 1796 + name: "loop", 1797 + exports: [ 1798 + { type: Loop, component: "at://did:plc:test/at.inlay.component/lp" }, 1799 + ], 1800 + }, 1801 + }); 1802 + 1803 + const output = await renderToCompletion( 1804 + $(Loop, {}), 1805 + { ...options, maxDepth: 5 }, 1806 + createContext(loopComponent) 1807 + ); 1808 + assertError(output, "Component depth limit exceeded"); 1809 + }); 1810 + 1811 + it("catches resolver errors", async () => { 1812 + const rootComponent: ComponentRecord = { 1813 + $type: "at.inlay.component", 1814 + type: Root, 1815 + body: { 1816 + $type: "at.inlay.component#bodyTemplate", 1817 + node: serializeTree($(Stack, {})), 1818 + }, 1819 + imports: [HOST_PACK_URI], 1820 + }; 1821 + 1822 + const errorOptions: RenderOptions = { 1823 + resolver: { 1824 + fetchRecord: async () => { 1825 + throw new Error("network down"); 1826 + }, 1827 + xrpc: async () => { 1828 + throw new Error("unreachable"); 1829 + }, 1830 + resolveLexicon: async () => null, 1831 + }, 1832 + }; 1833 + 1834 + const output = await renderToCompletion( 1835 + $(Root, {}), 1836 + errorOptions, 1837 + createContext(rootComponent) 1838 + ); 1839 + assertError(output, "network down"); 1840 + }); 1841 + 1842 + it("catches xrpc service errors", async () => { 1843 + const greetingComponent: ComponentRecord = { 1844 + $type: "at.inlay.component", 1845 + type: Greeting, 1846 + body: { 1847 + $type: "at.inlay.component#bodyExternal", 1848 + did: SERVICE_DID, 1849 + }, 1850 + imports: [HOST_PACK_URI], 1851 + }; 1852 + 1853 + const { options } = world({ 1854 + [`xrpc:${SERVICE_DID}:${Greeting}`]: () => { 1855 + throw new Error("service unavailable"); 1856 + }, 1857 + }); 1858 + 1859 + const output = await renderToCompletion( 1860 + $(Greeting, { name: "world" }), 1861 + options, 1862 + createContext(greetingComponent) 1863 + ); 1864 + assertError(output, "service unavailable"); 1865 + }); 1866 + 1867 + it("unused children don't trigger errors", async () => { 1868 + // Ignore is a template that doesn't reference its children. 1869 + // Even though the child is an external that throws, no error 1870 + // occurs because the child is never rendered. 1871 + const ignoreComponent: ComponentRecord = { 1872 + $type: "at.inlay.component", 1873 + type: Layout, 1874 + body: { 1875 + $type: "at.inlay.component#bodyTemplate", 1876 + node: serializeTree($(Text, { value: "ok" })), 1877 + }, 1878 + imports: [HOST_PACK_URI], 1879 + }; 1880 + 1881 + const crashComponent: ComponentRecord = { 1882 + $type: "at.inlay.component", 1883 + type: Greeting, 1884 + body: { 1885 + $type: "at.inlay.component#bodyExternal", 1886 + did: SERVICE_DID, 1887 + }, 1888 + imports: [HOST_PACK_URI], 1889 + }; 1890 + 1891 + const { options } = world({ 1892 + [`xrpc:${SERVICE_DID}:${Greeting}`]: () => { 1893 + throw new Error("boom"); 1894 + }, 1895 + ["at://did:plc:test/at.inlay.component/ignr"]: ignoreComponent, 1896 + ["at://did:plc:test/at.inlay.component/crsh"]: crashComponent, 1897 + ["at://did:plc:test/at.inlay.pack/app"]: { 1898 + $type: "at.inlay.pack", 1899 + name: "app", 1900 + exports: [ 1901 + { 1902 + type: Layout, 1903 + component: "at://did:plc:test/at.inlay.component/ignr", 1904 + }, 1905 + { 1906 + type: Greeting, 1907 + component: "at://did:plc:test/at.inlay.component/crsh", 1908 + }, 1909 + ], 1910 + }, 1911 + }); 1912 + 1913 + const output = await renderToCompletion( 1914 + $(Layout, { children: $(Greeting, {}) }), 1915 + options, 1916 + createContext(ignoreComponent) 1917 + ); 1918 + assert.deepEqual(output, h("span", { value: "ok" })); 1919 + }); 1920 + }); 1921 + 1922 + // ============================================================================ 1923 + // 7. Fetch behavior — concurrency and prefetching 1924 + // ============================================================================ 1925 + // 1926 + // The renderer kicks off fetches eagerly so the resolver can overlap them. 1927 + // Sibling children can be resolved concurrently, sibling xrpc calls can run 1928 + // in parallel, and child packs are prefetched during record fetches. 1929 + 1930 + describe("fetch behavior", () => { 1931 + it("resolves sibling components concurrently", async () => { 1932 + // Template expands to two siblings. Both component fetches start 1933 + // before either finishes — the host can walk siblings in parallel. 1934 + const layoutComponent: ComponentRecord = { 1935 + $type: "at.inlay.component", 1936 + type: Layout, 1937 + body: { 1938 + $type: "at.inlay.component#bodyTemplate", 1939 + node: serializeTree({ 1940 + first: $(Stack, {}), 1941 + second: $(Text, {}), 1942 + }), 1943 + }, 1944 + imports: [HOST_PACK_URI], 1945 + }; 1946 + 1947 + const { options, log } = world(); 1948 + 1949 + await renderToCompletion( 1950 + $(Layout, {}), 1951 + options, 1952 + createContext(layoutComponent) 1953 + ); 1954 + 1955 + assertLog(log, [ 1956 + `lexicon ${Layout}`, 1957 + `fetch ${HOST_PACK_URI}`, 1958 + // Both component fetches start concurrently (siblings) 1959 + `fetch:start ${"at://did:plc:host/at.inlay.component/stck"}`, 1960 + `fetch:start ${"at://did:plc:host/at.inlay.component/txt"}`, 1961 + `fetch:end ${"at://did:plc:host/at.inlay.component/stck"}`, 1962 + `fetch:end ${"at://did:plc:host/at.inlay.component/txt"}`, 1963 + ]); 1964 + }); 1965 + 1966 + it("prefetches child packs during record fetch", async () => { 1967 + const layoutComponent: ComponentRecord = { 1968 + $type: "at.inlay.component", 1969 + type: Layout, 1970 + body: { 1971 + $type: "at.inlay.component#bodyTemplate", 1972 + node: serializeTree($(Text, { value: $(Binding, { path: ["text"] }) })), 1973 + }, 1974 + imports: ["at://did:plc:test/at.inlay.pack/child"] as AtUriString[], 1975 + view: [ 1976 + { 1977 + $type: "at.inlay.component#viewRecord", 1978 + collection: "app.bsky.feed.post", 1979 + }, 1980 + ], 1981 + }; 1982 + 1983 + const { options, log } = testResolver({ 1984 + ["at://did:plc:alice/app.bsky.feed.post/123"]: { 1985 + text: "Hello world", 1986 + }, 1987 + ["at://did:plc:test/at.inlay.component/grtng"]: layoutComponent, 1988 + ["at://did:plc:host/at.inlay.component/txt"]: { 1989 + $type: "at.inlay.component", 1990 + type: Text, 1991 + }, 1992 + ["at://did:plc:test/at.inlay.pack/parent"]: { 1993 + $type: "at.inlay.pack", 1994 + name: "test", 1995 + exports: [ 1996 + { 1997 + type: Layout, 1998 + component: "at://did:plc:test/at.inlay.component/grtng", 1999 + }, 2000 + ], 2001 + }, 2002 + ["at://did:plc:test/at.inlay.pack/child"]: { 2003 + $type: "at.inlay.pack", 2004 + name: "child", 2005 + exports: [ 2006 + { type: Text, component: "at://did:plc:host/at.inlay.component/txt" }, 2007 + ], 2008 + }, 2009 + }); 2010 + 2011 + await renderToCompletion( 2012 + $(Layout, { uri: "at://did:plc:alice/app.bsky.feed.post/123" }), 2013 + options, 2014 + createContext(layoutComponent) 2015 + ); 2016 + 2017 + // Child pack prefetch overlaps with record fetch 2018 + assertLog(log, [ 2019 + `lexicon ${Layout}`, 2020 + `fetch:start ${"at://did:plc:test/at.inlay.pack/child"}`, 2021 + `fetch:start ${"at://did:plc:alice/app.bsky.feed.post/123"}`, 2022 + `fetch:end ${"at://did:plc:test/at.inlay.pack/child"}`, 2023 + `fetch:end ${"at://did:plc:alice/app.bsky.feed.post/123"}`, 2024 + `fetch ${"at://did:plc:host/at.inlay.component/txt"}`, 2025 + ]); 2026 + }); 2027 + 2028 + it("sibling xrpc calls run concurrently", async () => { 2029 + // A template expands to two external siblings. Both xrpc calls 2030 + // should start before either finishes. 2031 + const wrapperComponent: ComponentRecord = { 2032 + $type: "at.inlay.component", 2033 + type: Root, 2034 + body: { 2035 + $type: "at.inlay.component#bodyTemplate", 2036 + node: serializeTree( 2037 + $(Fragment, { 2038 + children: [$(Greeting, { name: "a" }), $(Card, { title: "b" })], 2039 + }) 2040 + ), 2041 + }, 2042 + imports: ["at://did:plc:test/at.inlay.pack/app"] as AtUriString[], 2043 + }; 2044 + 2045 + const greetingComponent: ComponentRecord = { 2046 + $type: "at.inlay.component", 2047 + type: Greeting, 2048 + body: { $type: "at.inlay.component#bodyExternal", did: SERVICE_DID }, 2049 + imports: [HOST_PACK_URI], 2050 + }; 2051 + 2052 + const cardComponent: ComponentRecord = { 2053 + $type: "at.inlay.component", 2054 + type: Card, 2055 + body: { $type: "at.inlay.component#bodyExternal", did: SERVICE_DID }, 2056 + imports: [HOST_PACK_URI], 2057 + }; 2058 + 2059 + const { options, log } = testResolver({ 2060 + ...HOST_RECORDS, 2061 + ["at://did:plc:test/at.inlay.component/grtng"]: greetingComponent, 2062 + ["at://did:plc:test/at.inlay.component/crd"]: cardComponent, 2063 + ["at://did:plc:test/at.inlay.pack/app"]: { 2064 + $type: "at.inlay.pack", 2065 + name: "app", 2066 + exports: [ 2067 + { 2068 + type: Greeting, 2069 + component: "at://did:plc:test/at.inlay.component/grtng", 2070 + }, 2071 + { type: Card, component: "at://did:plc:test/at.inlay.component/crd" }, 2072 + ], 2073 + }, 2074 + [`xrpc:${SERVICE_DID}:${Greeting}`]: () => ({ 2075 + node: $(Text, { value: "hi" }), 2076 + }), 2077 + [`xrpc:${SERVICE_DID}:${Card}`]: () => ({ 2078 + node: $(Text, { value: "card" }), 2079 + }), 2080 + }); 2081 + 2082 + await renderToCompletion( 2083 + $(Root, {}), 2084 + options, 2085 + createContext(wrapperComponent) 2086 + ); 2087 + 2088 + // Both xrpc starts appear before either ends 2089 + assertLog(log, [ 2090 + `lexicon ${Root}`, 2091 + `fetch ${"at://did:plc:test/at.inlay.pack/app"}`, 2092 + `fetch:start ${"at://did:plc:test/at.inlay.component/grtng"}`, 2093 + `fetch:start ${"at://did:plc:test/at.inlay.component/crd"}`, 2094 + `fetch:end ${"at://did:plc:test/at.inlay.component/grtng"}`, 2095 + `fetch:end ${"at://did:plc:test/at.inlay.component/crd"}`, 2096 + `lexicon ${Greeting}`, 2097 + `lexicon ${Card}`, 2098 + `xrpc:start procedure ${Greeting} -> ${SERVICE_DID}`, 2099 + `xrpc:start procedure ${Card} -> ${SERVICE_DID}`, 2100 + `xrpc:end procedure ${Greeting} -> ${SERVICE_DID}`, 2101 + `xrpc:end procedure ${Card} -> ${SERVICE_DID}`, 2102 + `fetch ${HOST_PACK_URI}`, 2103 + `fetch ${"at://did:plc:host/at.inlay.component/txt"}`, 2104 + ]); 2105 + }); 2106 + }); 2107 + 2108 + // ============================================================================ 2109 + // 8. Caching 2110 + // ============================================================================ 2111 + // 2112 + // render() returns cache tags on each result — record URIs from template 2113 + // fetches, explicit policies from XRPC responses. The resolver the host 2114 + // passes in can use these to cache results and invalidate when records 2115 + // or backlinks to them change (e.g. via firehose) or apply specified TTL. 2116 + // The host may cache non-personalized data globally between all users. 2117 + 2118 + type CacheEntry = { value: unknown; policy: CachePolicy }; 2119 + 2120 + function withGlobalCache(records: Record<string, TestMock>) { 2121 + const persistentStore = new Map<string, CacheEntry>(); 2122 + 2123 + function request() { 2124 + const { resolver, options, log } = world(records); 2125 + const baseFetch = resolver.fetchRecord.bind(resolver); 2126 + const baseXrpc = resolver.xrpc.bind(resolver); 2127 + 2128 + resolver.fetchRecord = async (uri) => { 2129 + const key = `record:${uri}`; 2130 + const hit = persistentStore.get(key); 2131 + if (hit) { 2132 + return hit.value; 2133 + } 2134 + const value = await baseFetch(uri); 2135 + persistentStore.set(key, { 2136 + value, 2137 + policy: { tags: [{ $type: "at.inlay.defs#tagRecord", uri }] }, 2138 + }); 2139 + return value; 2140 + }; 2141 + 2142 + resolver.xrpc = async (params) => { 2143 + if (!params.componentUri) { 2144 + return baseXrpc(params); 2145 + } 2146 + const key = `xrpc:${JSON.stringify(params)}`; 2147 + const hit = persistentStore.get(key); 2148 + if (hit) return hit.value; 2149 + const value = (await baseXrpc(params)) as ComponentResponse; 2150 + const policy: CachePolicy = { 2151 + ...value.cache, 2152 + tags: [ 2153 + ...(value.cache?.tags ?? []), 2154 + { 2155 + $type: "at.inlay.defs#tagRecord", 2156 + uri: params.componentUri as AtUriString, 2157 + }, 2158 + ], 2159 + }; 2160 + persistentStore.set(key, { value, policy }); 2161 + return value; 2162 + }; 2163 + 2164 + return { options, log }; 2165 + } 2166 + 2167 + function onFirehoseUpdate(updates: Record<string, unknown>) { 2168 + for (const [uri, value] of Object.entries(updates)) { 2169 + records[uri] = value as TestMock; 2170 + for (const [key, entry] of persistentStore) { 2171 + const match = (entry.policy.tags ?? []).some((tag) => { 2172 + if (tagRecord.$isTypeOf(tag)) return tag.uri === uri; 2173 + if (tagLink.$isTypeOf(tag)) return tag.subject === uri; 2174 + return false; 2175 + }); 2176 + if (match) persistentStore.delete(key); 2177 + } 2178 + } 2179 + } 2180 + 2181 + return { request, onFirehoseUpdate }; 2182 + } 2183 + 2184 + describe("caching", () => { 2185 + it("template record fetch is cached across requests", async () => { 2186 + // PostCard displays the post's text field from a fetched record. 2187 + const postCardComponent: ComponentRecord = { 2188 + $type: "at.inlay.component", 2189 + type: PostCard, 2190 + body: { 2191 + $type: "at.inlay.component#bodyTemplate", 2192 + node: serializeTree($(Text, { value: $(Binding, { path: ["text"] }) })), 2193 + }, 2194 + imports: [HOST_PACK_URI], 2195 + view: [ 2196 + { 2197 + $type: "at.inlay.component#viewRecord", 2198 + collection: "app.bsky.feed.post", 2199 + }, 2200 + ], 2201 + }; 2202 + 2203 + const postUri = "at://did:plc:alice/app.bsky.feed.post/123"; 2204 + const cache = withGlobalCache({ [postUri]: { text: "Hello" } }); 2205 + const ctx = createContext(postCardComponent); 2206 + 2207 + // Request 1 — cold, fetches post record 2208 + const r1 = cache.request(); 2209 + const out1 = await renderToCompletion( 2210 + $(PostCard, { uri: postUri }), 2211 + r1.options, 2212 + ctx 2213 + ); 2214 + assert.deepEqual(out1, h("span", { value: "Hello" })); 2215 + assertLog(r1.log, [ 2216 + `lexicon ${PostCard}`, 2217 + `fetch:start ${HOST_PACK_URI}`, 2218 + `fetch:start ${postUri}`, 2219 + `fetch:end ${HOST_PACK_URI}`, 2220 + `fetch:end ${postUri}`, 2221 + `fetch ${"at://did:plc:host/at.inlay.component/txt"}`, 2222 + ]); 2223 + 2224 + // Request 2 — warm, no fetches 2225 + const r2 = cache.request(); 2226 + const out2 = await renderToCompletion( 2227 + $(PostCard, { uri: postUri }), 2228 + r2.options, 2229 + ctx 2230 + ); 2231 + assert.deepEqual(out2, h("span", { value: "Hello" })); 2232 + assertLog(r2.log, [`lexicon ${PostCard}`]); 2233 + 2234 + // Firehose: post updated → cache invalidated → re-fetched → output changes 2235 + cache.onFirehoseUpdate({ [postUri]: { text: "Updated" } }); 2236 + const r3 = cache.request(); 2237 + const out3 = await renderToCompletion( 2238 + $(PostCard, { uri: postUri }), 2239 + r3.options, 2240 + ctx 2241 + ); 2242 + assert.deepEqual(out3, h("span", { value: "Updated" })); 2243 + assertLog(r3.log, [`lexicon ${PostCard}`, `fetch ${postUri}`]); 2244 + }); 2245 + 2246 + it("external xrpc call is cached across requests", async () => { 2247 + // The Greeting service fetches a profile internally and renders 2248 + // a greeting from it. Cache tags declare the dependency so the 2249 + // host can invalidate when the profile changes. 2250 + const greetingComponent: ComponentRecord = { 2251 + $type: "at.inlay.component", 2252 + type: Greeting, 2253 + body: { 2254 + $type: "at.inlay.component#bodyExternal", 2255 + did: SERVICE_DID, 2256 + }, 2257 + imports: [HOST_PACK_URI], 2258 + }; 2259 + 2260 + const profileUri = "at://did:plc:alice/app.bsky.actor.profile/self"; 2261 + const records: Record<string, TestMock> = { 2262 + [profileUri]: { displayName: "Alice" }, 2263 + [`xrpc:${SERVICE_DID}:${Greeting}`]: () => { 2264 + // Service reads profile from its own data store 2265 + const profile = records[profileUri] as Record<string, unknown>; 2266 + return { 2267 + node: $(Text, { value: `Hello ${profile.displayName}` }), 2268 + cache: { 2269 + tags: [{ $type: "at.inlay.defs#tagRecord", uri: profileUri }], 2270 + }, 2271 + }; 2272 + }, 2273 + }; 2274 + 2275 + const componentUri = "at://did:plc:test/at.inlay.component/grtng"; 2276 + const cache = withGlobalCache(records); 2277 + const ctx = createContext(greetingComponent, componentUri); 2278 + 2279 + // Request 1 — cold, calls xrpc → "Hello Alice" 2280 + const r1 = cache.request(); 2281 + const out1 = await renderToCompletion($(Greeting, {}), r1.options, ctx); 2282 + assert.deepEqual(out1, h("span", { value: "Hello Alice" })); 2283 + assertLog(r1.log, [ 2284 + `lexicon ${Greeting}`, 2285 + `xrpc procedure ${Greeting} -> ${SERVICE_DID}`, 2286 + `fetch ${HOST_PACK_URI}`, 2287 + `fetch ${"at://did:plc:host/at.inlay.component/txt"}`, 2288 + ]); 2289 + 2290 + // Request 2 — warm, xrpc cached, no calls 2291 + const r2 = cache.request(); 2292 + const out2 = await renderToCompletion($(Greeting, {}), r2.options, ctx); 2293 + assert.deepEqual(out2, out1); 2294 + assertLog(r2.log, [`lexicon ${Greeting}`]); 2295 + 2296 + // Firehose: profile updated → xrpc cache invalidated → re-called → new greeting 2297 + cache.onFirehoseUpdate({ [profileUri]: { displayName: "Bob" } }); 2298 + const r3 = cache.request(); 2299 + const out3 = await renderToCompletion($(Greeting, {}), r3.options, ctx); 2300 + assert.deepEqual(out3, h("span", { value: "Hello Bob" })); 2301 + assertLog(r3.log, [ 2302 + `lexicon ${Greeting}`, 2303 + `xrpc procedure ${Greeting} -> ${SERVICE_DID}`, 2304 + ]); 2305 + 2306 + // Firehose: component record itself updated → xrpc cache invalidated → re-called 2307 + cache.onFirehoseUpdate({ 2308 + [componentUri]: { 2309 + ...greetingComponent, 2310 + updatedAt: new Date().toISOString(), 2311 + }, 2312 + }); 2313 + const r4 = cache.request(); 2314 + const out4 = await renderToCompletion($(Greeting, {}), r4.options, ctx); 2315 + assert.deepEqual(out4, h("span", { value: "Hello Bob" })); 2316 + assertLog(r4.log, [ 2317 + `lexicon ${Greeting}`, 2318 + `xrpc procedure ${Greeting} -> ${SERVICE_DID}`, 2319 + ]); 2320 + 2321 + // Request 5 — warm, xrpc cached, no calls 2322 + const r5 = cache.request(); 2323 + const out5 = await renderToCompletion($(Greeting, {}), r5.options, ctx); 2324 + assert.deepEqual(out5, out4); 2325 + assertLog(r5.log, [`lexicon ${Greeting}`]); 2326 + }); 2327 + 2328 + it("cached xrpc survives while caller children re-render", async () => { 2329 + // Layout is a cached external component: <Stack>{header}{children}</Stack>. 2330 + // PostCard is a template child passed as a slot — it reads from a 2331 + // post record. When the post changes, Layout's xrpc stays cached 2332 + // but the dynamiic child within it re-renders with new data. 2333 + // This is possible because Layout is cached with "holes" for children. 2334 + const layoutComponent: ComponentRecord = { 2335 + $type: "at.inlay.component", 2336 + type: Layout, 2337 + body: { 2338 + $type: "at.inlay.component#bodyExternal", 2339 + did: SERVICE_DID, 2340 + }, 2341 + imports: [HOST_PACK_URI], 2342 + }; 2343 + 2344 + const postCardComponent: ComponentRecord = { 2345 + $type: "at.inlay.component", 2346 + type: PostCard, 2347 + body: { 2348 + $type: "at.inlay.component#bodyTemplate", 2349 + node: serializeTree($(Text, { value: $(Binding, { path: ["text"] }) })), 2350 + }, 2351 + imports: [HOST_PACK_URI], 2352 + view: [ 2353 + { 2354 + $type: "at.inlay.component#viewRecord", 2355 + collection: "app.bsky.feed.post", 2356 + }, 2357 + ], 2358 + }; 2359 + 2360 + const postUri = "at://did:plc:alice/app.bsky.feed.post/123"; 2361 + const layoutUri = "at://did:plc:test/at.inlay.component/lyout"; 2362 + const appPackUri = "at://did:plc:test/at.inlay.pack/app"; 2363 + const cache = withGlobalCache({ 2364 + [postUri]: { text: "Hello" }, 2365 + ["at://did:plc:test/at.inlay.component/pstcrd"]: postCardComponent, 2366 + [appPackUri]: { 2367 + $type: "at.inlay.pack", 2368 + name: "app", 2369 + exports: [ 2370 + { 2371 + type: PostCard, 2372 + component: "at://did:plc:test/at.inlay.component/pstcrd", 2373 + }, 2374 + ], 2375 + }, 2376 + [`xrpc:${SERVICE_DID}:${Layout}`]: (params: { 2377 + body?: Record<string, unknown>; 2378 + }) => ({ 2379 + node: $(Stack, { 2380 + children: [ 2381 + $(Text, { value: "header" }), 2382 + params.body!.children as Element, 2383 + ], 2384 + }), 2385 + cache: { life: "max" }, 2386 + }), 2387 + }); 2388 + 2389 + const ctx: RenderContext = { 2390 + imports: [appPackUri, HOST_PACK_URI], 2391 + component: layoutComponent, 2392 + componentUri: layoutUri, 2393 + }; 2394 + 2395 + const expectedLayout = (text: string) => 2396 + h("div", { 2397 + children: [ 2398 + h("span", { value: "header" }), 2399 + h("frag", { children: h("span", { value: text }) }), 2400 + ], 2401 + }); 2402 + 2403 + // Request 1 — cold: xrpc called, post fetched 2404 + const r1 = cache.request(); 2405 + const out1 = await renderToCompletion( 2406 + $(Layout, { children: $(PostCard, { uri: postUri }) }), 2407 + r1.options, 2408 + ctx 2409 + ); 2410 + assert.deepEqual(out1, expectedLayout("Hello")); 2411 + assertLog(r1.log, [ 2412 + `lexicon ${Layout}`, 2413 + `xrpc procedure ${Layout} -> ${SERVICE_DID}`, 2414 + `fetch:start ${HOST_PACK_URI}`, 2415 + `fetch:end ${HOST_PACK_URI}`, 2416 + `fetch:start ${"at://did:plc:host/at.inlay.component/stck"}`, 2417 + `fetch:end ${"at://did:plc:host/at.inlay.component/stck"}`, 2418 + `fetch:start ${"at://did:plc:host/at.inlay.component/txt"}`, 2419 + `fetch:start ${appPackUri}`, 2420 + `fetch:end ${"at://did:plc:host/at.inlay.component/txt"}`, 2421 + `fetch:end ${appPackUri}`, 2422 + `fetch:start ${"at://did:plc:test/at.inlay.component/pstcrd"}`, 2423 + `fetch:end ${"at://did:plc:test/at.inlay.component/pstcrd"}`, 2424 + `lexicon ${PostCard}`, 2425 + `fetch ${postUri}`, 2426 + ]); 2427 + 2428 + // Request 2 — warm: everything cached 2429 + const r2 = cache.request(); 2430 + const out2 = await renderToCompletion( 2431 + $(Layout, { children: $(PostCard, { uri: postUri }) }), 2432 + r2.options, 2433 + ctx 2434 + ); 2435 + assert.deepEqual(out2, expectedLayout("Hello")); 2436 + assertLog(r2.log, [`lexicon ${Layout}`, `lexicon ${PostCard}`]); 2437 + 2438 + // Firehose: post updated → post cache invalidated, 2439 + // Layout xrpc tagged with layoutUri → stays cached, child re-fetches 2440 + cache.onFirehoseUpdate({ [postUri]: { text: "Updated" } }); 2441 + const r3 = cache.request(); 2442 + const out3 = await renderToCompletion( 2443 + $(Layout, { children: $(PostCard, { uri: postUri }) }), 2444 + r3.options, 2445 + ctx 2446 + ); 2447 + assert.deepEqual(out3, expectedLayout("Updated")); 2448 + assertLog(r3.log, [ 2449 + `lexicon ${Layout}`, 2450 + `lexicon ${PostCard}`, 2451 + `fetch ${postUri}`, 2452 + ]); 2453 + 2454 + // Firehose: component record itself updated → xrpc cache invalidated → re-called 2455 + cache.onFirehoseUpdate({ 2456 + [layoutUri]: { 2457 + ...layoutComponent, 2458 + updatedAt: new Date().toISOString(), 2459 + }, 2460 + }); 2461 + const r4 = cache.request(); 2462 + const out4 = await renderToCompletion( 2463 + $(Layout, { children: $(PostCard, { uri: postUri }) }), 2464 + r4.options, 2465 + ctx 2466 + ); 2467 + assert.deepEqual(out4, expectedLayout("Updated")); 2468 + assertLog(r4.log, [ 2469 + `lexicon ${Layout}`, 2470 + `xrpc procedure ${Layout} -> ${SERVICE_DID}`, 2471 + `lexicon ${PostCard}`, 2472 + ]); 2473 + }); 2474 + }); 2475 + 2476 + // ============================================================================ 2477 + // 9. View identity — bare DID expansion 2478 + // ============================================================================ 2479 + // 2480 + // A component with view declarations (viewIdentity + viewRecord) accepts 2481 + // bare DIDs as the uri prop. The renderer expands the DID to a full 2482 + // AT URI using the collection from viewRecord and the rkey from the 2483 + // collection's lexicon (literal key) or "self" as default. 2484 + // 2485 + // This lets components like a profile card accept `uri="did:plc:alice"` 2486 + // instead of requiring the full `at://did:plc:alice/app.bsky.actor.profile/self`. 2487 + // 2488 + // TODO: I'm not entirely happy about this but it is very convenient in practice. 2489 + 2490 + describe("view identity", () => { 2491 + it("expands bare DID to full AT URI using view declarations", async () => { 2492 + const profile = "test.app.Profile" as const; 2493 + const profileComponent: ComponentRecord = { 2494 + $type: "at.inlay.component", 2495 + type: profile, 2496 + body: { 2497 + $type: "at.inlay.component#bodyTemplate", 2498 + node: serializeTree( 2499 + $(Text, { value: $(Binding, { path: ["displayName"] }) }) 2500 + ), 2501 + }, 2502 + imports: [HOST_PACK_URI], 2503 + view: [ 2504 + { $type: "at.inlay.component#viewIdentity" }, 2505 + { 2506 + $type: "at.inlay.component#viewRecord", 2507 + collection: "app.bsky.actor.profile", 2508 + }, 2509 + ], 2510 + }; 2511 + 2512 + const { options } = world({ 2513 + ["at://did:plc:alice/app.bsky.actor.profile/self"]: { 2514 + displayName: "Alice", 2515 + }, 2516 + }); 2517 + 2518 + // resolveLexicon returns null → rkey defaults to "self" 2519 + const output = await renderToCompletion( 2520 + $(profile, { uri: "did:plc:alice" }), 2521 + options, 2522 + createContext(profileComponent) 2523 + ); 2524 + 2525 + assert.deepEqual(output, h("span", { value: "Alice" })); 2526 + }); 2527 + 2528 + it("uses lexicon literal key when available", async () => { 2529 + const profile = "test.app.Profile" as const; 2530 + const profileComponent: ComponentRecord = { 2531 + $type: "at.inlay.component", 2532 + type: profile, 2533 + body: { 2534 + $type: "at.inlay.component#bodyTemplate", 2535 + node: serializeTree( 2536 + $(Text, { value: $(Binding, { path: ["displayName"] }) }) 2537 + ), 2538 + }, 2539 + imports: [HOST_PACK_URI], 2540 + view: [ 2541 + { $type: "at.inlay.component#viewIdentity" }, 2542 + { 2543 + $type: "at.inlay.component#viewRecord", 2544 + collection: "app.bsky.actor.profile", 2545 + }, 2546 + ], 2547 + }; 2548 + 2549 + const { resolver, options } = world({ 2550 + ["at://did:plc:alice/app.bsky.actor.profile/main"]: { 2551 + displayName: "Alice", 2552 + }, 2553 + }); 2554 + 2555 + // Lexicon declares literal key "main" 2556 + resolver.resolveLexicon = async (nsid: string) => { 2557 + if (nsid === "app.bsky.actor.profile") { 2558 + return { defs: { main: { key: "literal:main" } } }; 2559 + } 2560 + return null; 2561 + }; 2562 + 2563 + const output = await renderToCompletion( 2564 + $(profile, { uri: "did:plc:alice" }), 2565 + options, 2566 + createContext(profileComponent) 2567 + ); 2568 + 2569 + assert.deepEqual(output, h("span", { value: "Alice" })); 2570 + }); 2571 + 2572 + it("leaves full AT URIs unchanged", async () => { 2573 + const profile = "test.app.Profile" as const; 2574 + const profileComponent: ComponentRecord = { 2575 + $type: "at.inlay.component", 2576 + type: profile, 2577 + body: { 2578 + $type: "at.inlay.component#bodyTemplate", 2579 + node: serializeTree( 2580 + $(Text, { value: $(Binding, { path: ["displayName"] }) }) 2581 + ), 2582 + }, 2583 + imports: [HOST_PACK_URI], 2584 + view: [ 2585 + { $type: "at.inlay.component#viewIdentity" }, 2586 + { 2587 + $type: "at.inlay.component#viewRecord", 2588 + collection: "app.bsky.actor.profile", 2589 + }, 2590 + ], 2591 + }; 2592 + 2593 + const { options } = world({ 2594 + ["at://did:plc:alice/app.bsky.actor.profile/self"]: { 2595 + displayName: "Alice", 2596 + }, 2597 + }); 2598 + 2599 + // Full AT URI is not expanded — passed through as-is 2600 + const output = await renderToCompletion( 2601 + $(profile, { 2602 + uri: "at://did:plc:alice/app.bsky.actor.profile/self", 2603 + }), 2604 + options, 2605 + createContext(profileComponent) 2606 + ); 2607 + 2608 + assert.deepEqual(output, h("span", { value: "Alice" })); 2609 + }); 2610 + }); 2611 + 2612 + // ============================================================================ 2613 + // 10. Input validation — lexicon schemas and accepts 2614 + // ============================================================================ 2615 + // 2616 + // Components can declare a published lexicon — the component type is an 2617 + // NSID with a lexicon schema, resolved via resolveLexicon(). Props are 2618 + // checked against the schema before template expansion. Invalid props 2619 + // produce a Throw. 2620 + // 2621 + // (Separately, the `accepts` array on a component record is for editor 2622 + // tooling — "given data of type X, route it to prop Y." It powers 2623 + // palette filtering and autofill, and works even before the component 2624 + // is published or has a lexicon. If specified, it does get validated.) 2625 + 2626 + describe("lexicon validation", () => { 2627 + it("validates props against published lexicon", async () => { 2628 + const viewComponent: ComponentRecord = { 2629 + $type: "at.inlay.component", 2630 + type: View, 2631 + body: { 2632 + $type: "at.inlay.component#bodyTemplate", 2633 + node: { $: "$", type: Text, props: {} }, 2634 + }, 2635 + imports: [HOST_PACK_URI], 2636 + }; 2637 + 2638 + const { resolver } = world(); 2639 + 2640 + resolver.resolveLexicon = async (nsid: string) => { 2641 + if (nsid === View) { 2642 + return { 2643 + lexicon: 1, 2644 + id: View, 2645 + defs: { 2646 + main: { 2647 + type: "procedure", 2648 + input: { 2649 + encoding: "application/json", 2650 + schema: { 2651 + type: "object", 2652 + required: ["uri"], 2653 + properties: { uri: { type: "string", format: "at-uri" } }, 2654 + }, 2655 + }, 2656 + }, 2657 + }, 2658 + }; 2659 + } 2660 + return null; 2661 + }; 2662 + 2663 + const opts: RenderOptions = { resolver }; 2664 + const ctx = createContext(viewComponent); 2665 + 2666 + const ok = await renderToCompletion( 2667 + $(View, { uri: "at://did:plc:alice/app.bsky.feed.post/123" }), 2668 + opts, 2669 + ctx 2670 + ); 2671 + assert.ok(typeof ok !== "string", "valid props should not error"); 2672 + 2673 + const bad = await renderToCompletion($(View, {}), opts, ctx); 2674 + assertError(bad, 'Input must have the property "uri"'); 2675 + }); 2676 + 2677 + it("synthesizes validation from accepts", async () => { 2678 + const cardComponent: ComponentRecord = { 2679 + $type: "at.inlay.component", 2680 + type: Card, 2681 + body: { 2682 + $type: "at.inlay.component#bodyTemplate", 2683 + node: { $: "$", type: Text, props: {} }, 2684 + }, 2685 + imports: [HOST_PACK_URI], 2686 + accepts: [{ prop: "uri", type: "string", format: "at-uri" }], 2687 + }; 2688 + 2689 + const { options } = world(); 2690 + const ctx = createContext(cardComponent); 2691 + 2692 + const ok = await renderToCompletion( 2693 + $(Card, { uri: "at://did:plc:alice/app.bsky.feed.post/123" }), 2694 + options, 2695 + ctx 2696 + ); 2697 + assert.ok(typeof ok !== "string", "valid props should not error"); 2698 + 2699 + const bad = await renderToCompletion( 2700 + $(Card, { uri: 123 as unknown as string }), 2701 + options, 2702 + ctx 2703 + ); 2704 + assert.ok(typeof bad === "string", "wrong type should produce error"); 2705 + }); 2706 + 2707 + it("validates collection constraints from accepts", async () => { 2708 + const cardComponent: ComponentRecord = { 2709 + $type: "at.inlay.component", 2710 + type: Card, 2711 + body: { 2712 + $type: "at.inlay.component#bodyTemplate", 2713 + node: { $: "$", type: Text, props: {} }, 2714 + }, 2715 + imports: [HOST_PACK_URI], 2716 + accepts: [ 2717 + { 2718 + prop: "uri", 2719 + type: "string", 2720 + format: "at-uri", 2721 + collection: "app.bsky.feed.post", 2722 + }, 2723 + ], 2724 + }; 2725 + 2726 + const { options } = world(); 2727 + const ctx = createContext(cardComponent); 2728 + 2729 + const ok = await renderToCompletion( 2730 + $(Card, { uri: "at://did:plc:alice/app.bsky.feed.post/123" }), 2731 + options, 2732 + ctx 2733 + ); 2734 + assert.ok(typeof ok !== "string", "matching collection should not error"); 2735 + 2736 + const bad = await renderToCompletion( 2737 + $(Card, { uri: "at://did:plc:alice/app.bsky.feed.like/456" }), 2738 + options, 2739 + ctx 2740 + ); 2741 + assertError( 2742 + bad, 2743 + `${Card}: uri expects app.bsky.feed.post, got app.bsky.feed.like` 2744 + ); 2745 + }); 2746 + }); 2747 + 2748 + // ============================================================================ 2749 + // 11. Missing data and Maybe — optional record fields 2750 + // ============================================================================ 2751 + // 2752 + // Atproto records have optional fields. When a Binding resolves to null 2753 + // (field absent), the renderer throws MissingError. Unlike other errors, 2754 + // MissingError propagates without being caught by the Throw handler — 2755 + // this lets at.inlay.Maybe elements higher in the tree catch it. 2756 + // 2757 + // Maybe catches MissingError from its children and renders a fallback 2758 + // (or null if no fallback). Siblings outside the Maybe are unaffected. 2759 + // This works across component boundaries, even through external slots. 2760 + // A host may implement this with try/catch or UI framework error boundaries. 2761 + // 2762 + describe("missing bindings and Maybe", () => { 2763 + const missingCard = { 2764 + $type: "at.inlay.component", 2765 + type: MissingCard, 2766 + body: { 2767 + $type: "at.inlay.component#bodyTemplate", 2768 + node: serializeTree($(Text, { value: $(Binding, { path: ["text"] }) })), 2769 + }, 2770 + imports: [HOST_PACK_URI], 2771 + }; 2772 + const externalComponent: ComponentRecord = { 2773 + $type: "at.inlay.component", 2774 + type: External, 2775 + body: { 2776 + $type: "at.inlay.component#bodyExternal", 2777 + did: SERVICE_DID, 2778 + }, 2779 + imports: [HOST_PACK_URI], 2780 + }; 2781 + const APP_PACK: Record<AtUriString, TestMock> = { 2782 + ["at://did:plc:test/at.inlay.component/msng"]: missingCard, 2783 + ["at://did:plc:test/at.inlay.component/extrnl"]: externalComponent, 2784 + ["at://did:plc:test/at.inlay.pack/app"]: { 2785 + $type: "at.inlay.pack", 2786 + name: "app", 2787 + exports: [ 2788 + { 2789 + type: MissingCard, 2790 + component: "at://did:plc:test/at.inlay.component/msng", 2791 + }, 2792 + { 2793 + type: External, 2794 + component: "at://did:plc:test/at.inlay.component/extrnl", 2795 + }, 2796 + ], 2797 + }, 2798 + }; 2799 + 2800 + it("present bindings resolve normally", async () => { 2801 + const cardComponent: ComponentRecord = { 2802 + $type: "at.inlay.component", 2803 + type: Card, 2804 + body: { 2805 + $type: "at.inlay.component#bodyTemplate", 2806 + node: serializeTree( 2807 + $(Text, { value: $(Binding, { path: ["title"] }) }) 2808 + ), 2809 + }, 2810 + imports: [HOST_PACK_URI], 2811 + view: [ 2812 + { 2813 + $type: "at.inlay.component#viewRecord", 2814 + collection: "app.bsky.feed.post", 2815 + }, 2816 + ], 2817 + }; 2818 + 2819 + const { options } = world({ 2820 + ["at://did:plc:alice/app.bsky.feed.post/123"]: { title: "Hello" }, 2821 + }); 2822 + 2823 + const output = await renderToCompletion( 2824 + $(Card, { uri: "at://did:plc:alice/app.bsky.feed.post/123" }), 2825 + options, 2826 + createContext(cardComponent) 2827 + ); 2828 + assert.deepEqual(output, h("span", { value: "Hello" })); 2829 + }); 2830 + 2831 + it("absent binding throws MissingError", async () => { 2832 + // No props match the binding path → MissingError 2833 + const cardComponent: ComponentRecord = { 2834 + $type: "at.inlay.component", 2835 + type: Card, 2836 + body: { 2837 + $type: "at.inlay.component#bodyTemplate", 2838 + node: serializeTree( 2839 + $(Stack, { children: $(Binding, { path: ["text"] }) }) 2840 + ), 2841 + }, 2842 + imports: [HOST_PACK_URI], 2843 + }; 2844 + 2845 + const { options } = world(); 2846 + await assert.rejects( 2847 + renderToCompletion($(Card, {}), options, createContext(cardComponent)), 2848 + (err: unknown) => { 2849 + if (!(err instanceof MissingError)) throw err; 2850 + assert.deepEqual(err.path, ["text"]); 2851 + return true; 2852 + } 2853 + ); 2854 + }); 2855 + 2856 + it("absent nested path throws MissingError", async () => { 2857 + // Binding for ["reply", "parent", "uri"] — a deep path into 2858 + // optional atproto record fields. 2859 + const cardComponent: ComponentRecord = { 2860 + $type: "at.inlay.component", 2861 + type: Card, 2862 + body: { 2863 + $type: "at.inlay.component#bodyTemplate", 2864 + node: serializeTree( 2865 + $(Link, { uri: $(Binding, { path: ["reply", "parent", "uri"] }) }) 2866 + ), 2867 + }, 2868 + imports: [HOST_PACK_URI], 2869 + }; 2870 + 2871 + const { options } = world(); 2872 + await assert.rejects( 2873 + renderToCompletion($(Card, {}), options, createContext(cardComponent)), 2874 + (err: unknown) => { 2875 + if (!(err instanceof MissingError)) throw err; 2876 + assert.deepEqual(err.path, ["reply", "parent", "uri"]); 2877 + return true; 2878 + } 2879 + ); 2880 + }); 2881 + 2882 + it("absent field throws even when record exists", async () => { 2883 + // Record has "title" but not "reply.parent.uri". Having a record 2884 + // doesn't guarantee all bindings resolve. 2885 + const cardComponent: ComponentRecord = { 2886 + $type: "at.inlay.component", 2887 + type: Card, 2888 + body: { 2889 + $type: "at.inlay.component#bodyTemplate", 2890 + node: serializeTree( 2891 + $(Stack, { 2892 + children: [ 2893 + $(Link, { 2894 + uri: $(Binding, { path: ["reply", "parent", "uri"] }), 2895 + }), 2896 + $(Text, { value: $(Binding, { path: ["title"] }) }), 2897 + ], 2898 + }) 2899 + ), 2900 + }, 2901 + imports: [HOST_PACK_URI], 2902 + view: [ 2903 + { 2904 + $type: "at.inlay.component#viewRecord", 2905 + collection: "app.bsky.feed.post", 2906 + }, 2907 + ], 2908 + }; 2909 + 2910 + const { options } = world({ 2911 + ["at://did:plc:alice/app.bsky.feed.post/123"]: { title: "Hello" }, 2912 + }); 2913 + 2914 + await assert.rejects( 2915 + renderToCompletion( 2916 + $(Card, { uri: "at://did:plc:alice/app.bsky.feed.post/123" }), 2917 + options, 2918 + createContext(cardComponent) 2919 + ), 2920 + (err: unknown) => { 2921 + if (!(err instanceof MissingError)) throw err; 2922 + assert.deepEqual(err.path, ["reply", "parent", "uri"]); 2923 + return true; 2924 + } 2925 + ); 2926 + }); 2927 + 2928 + it("Maybe nukes entire subtree when a deep binding is missing", async () => { 2929 + // <Stack> 2930 + // <Maybe> 2931 + // <Row> 2932 + // <Link uri={reply.parent.uri} /> <- missing, deep inside 2933 + // <Text value="visible" /> 2934 + // </Row> 2935 + // <Text value="also visible" /> 2936 + // </Maybe> 2937 + // <Text value="outside" /> 2938 + // </Stack> 2939 + // 2940 + // Everything inside Maybe disappears — Row, both children, the 2941 + // second Text. Only "outside" survives. 2942 + const cardComponent: ComponentRecord = { 2943 + $type: "at.inlay.component", 2944 + type: Card, 2945 + body: { 2946 + $type: "at.inlay.component#bodyTemplate", 2947 + node: serializeTree( 2948 + $(Stack, { 2949 + children: [ 2950 + $(Maybe, { 2951 + children: [ 2952 + $(Row, { 2953 + children: [ 2954 + $(Link, { 2955 + uri: $(Binding, { path: ["reply", "parent", "uri"] }), 2956 + }), 2957 + $(Text, { value: "visible" }), 2958 + ], 2959 + }), 2960 + $(Text, { value: "also visible" }), 2961 + ], 2962 + }), 2963 + $(Text, { value: "outside" }), 2964 + ], 2965 + }) 2966 + ), 2967 + }, 2968 + imports: [HOST_PACK_URI], 2969 + view: [ 2970 + { 2971 + $type: "at.inlay.component#viewRecord", 2972 + collection: "app.bsky.feed.post", 2973 + }, 2974 + ], 2975 + }; 2976 + 2977 + const { options } = world({ 2978 + ["at://did:plc:alice/app.bsky.feed.post/123"]: { text: "Hello" }, 2979 + }); 2980 + 2981 + const output = await renderToCompletion( 2982 + $(Card, { uri: "at://did:plc:alice/app.bsky.feed.post/123" }), 2983 + options, 2984 + createContext(cardComponent) 2985 + ); 2986 + assert.deepEqual( 2987 + output, 2988 + h("div", { children: [null, h("span", { value: "outside" })] }) 2989 + ); 2990 + }); 2991 + 2992 + it("Maybe renders fallback when provided", async () => { 2993 + const cardComponent: ComponentRecord = { 2994 + $type: "at.inlay.component", 2995 + type: Card, 2996 + body: { 2997 + $type: "at.inlay.component#bodyTemplate", 2998 + node: serializeTree( 2999 + $(Maybe, { 3000 + children: $(MissingCard, {}), 3001 + fallback: $(Text, { value: "unavailable" }), 3002 + }) 3003 + ), 3004 + }, 3005 + imports: [ 3006 + HOST_PACK_URI, 3007 + "at://did:plc:test/at.inlay.pack/app", 3008 + ] as AtUriString[], 3009 + }; 3010 + 3011 + const { options } = world(APP_PACK); 3012 + 3013 + const output = await renderToCompletion( 3014 + $(Card, {}), 3015 + options, 3016 + createContext(cardComponent) 3017 + ); 3018 + assert.deepEqual(output, h("span", { value: "unavailable" })); 3019 + }); 3020 + 3021 + it("Maybe catches missing binding in same template", async () => { 3022 + // <Stack> 3023 + // <Maybe fallback={<Text value="nope" />}> 3024 + // <Link uri={reply.parent.uri} /> <- missing 3025 + // </Maybe> 3026 + // <Text value={text} /> <- present 3027 + // </Stack> 3028 + const postCardComponent: ComponentRecord = { 3029 + $type: "at.inlay.component", 3030 + type: PostCard, 3031 + body: { 3032 + $type: "at.inlay.component#bodyTemplate", 3033 + node: serializeTree( 3034 + $(Stack, { 3035 + children: [ 3036 + $(Maybe, { 3037 + fallback: $(Text, { value: "nope" }), 3038 + children: [ 3039 + $(Link, { 3040 + uri: $(Binding, { path: ["reply", "parent", "uri"] }), 3041 + }), 3042 + ], 3043 + }), 3044 + $(Text, { value: $(Binding, { path: ["text"] }) }), 3045 + ], 3046 + }) 3047 + ), 3048 + }, 3049 + imports: [HOST_PACK_URI], 3050 + view: [ 3051 + { 3052 + $type: "at.inlay.component#viewRecord", 3053 + collection: "app.bsky.feed.post", 3054 + }, 3055 + ], 3056 + }; 3057 + 3058 + const { options } = world({ 3059 + ["at://did:plc:alice/app.bsky.feed.post/123"]: { 3060 + text: "Hello world", 3061 + }, 3062 + }); 3063 + 3064 + const output = await renderToCompletion( 3065 + $(PostCard, { uri: "at://did:plc:alice/app.bsky.feed.post/123" }), 3066 + options, 3067 + createContext(postCardComponent) 3068 + ); 3069 + 3070 + assert.deepEqual( 3071 + output, 3072 + h("div", { 3073 + children: [ 3074 + h("span", { value: "nope" }), 3075 + h("span", { value: "Hello world" }), 3076 + ], 3077 + }) 3078 + ); 3079 + }); 3080 + 3081 + it("MissingError propagates through external component slots", async () => { 3082 + const postCardComponent: ComponentRecord = { 3083 + $type: "at.inlay.component", 3084 + type: PostCard, 3085 + body: { 3086 + $type: "at.inlay.component#bodyTemplate", 3087 + node: serializeTree( 3088 + $(External, { 3089 + children: $(Link, { 3090 + uri: $(Binding, { path: ["reply", "parent", "uri"] }), 3091 + }), 3092 + }) 3093 + ), 3094 + }, 3095 + imports: [ 3096 + HOST_PACK_URI, 3097 + "at://did:plc:test/at.inlay.pack/app", 3098 + ] as AtUriString[], 3099 + view: [ 3100 + { 3101 + $type: "at.inlay.component#viewRecord", 3102 + collection: "app.bsky.feed.post", 3103 + }, 3104 + ], 3105 + }; 3106 + 3107 + const { options } = world({ 3108 + ...APP_PACK, 3109 + ["at://did:plc:alice/app.bsky.feed.post/123"]: { 3110 + text: "Hello world", 3111 + }, 3112 + [`xrpc:${SERVICE_DID}:${External}`]: (params: { 3113 + body?: Record<string, unknown>; 3114 + }) => ({ 3115 + node: params.body!.children, 3116 + }), 3117 + }); 3118 + 3119 + await assert.rejects( 3120 + renderToCompletion( 3121 + $(PostCard, { uri: "at://did:plc:alice/app.bsky.feed.post/123" }), 3122 + options, 3123 + createContext(postCardComponent) 3124 + ), 3125 + (err: unknown) => { 3126 + if (!(err instanceof MissingError)) { 3127 + throw err; 3128 + } 3129 + assert.deepEqual(err.path, ["reply", "parent", "uri"]); 3130 + return true; 3131 + } 3132 + ); 3133 + }); 3134 + 3135 + it("Maybe catches missing slot binding through external component", async () => { 3136 + // <Stack> 3137 + // <Maybe fallback={<Text value="nope" />}> 3138 + // <External><Link uri={reply.parent.uri} /></External> <- missing 3139 + // </Maybe> 3140 + // <Text value={text} /> <- present 3141 + // </Stack> 3142 + const postCardComponent: ComponentRecord = { 3143 + $type: "at.inlay.component", 3144 + type: PostCard, 3145 + body: { 3146 + $type: "at.inlay.component#bodyTemplate", 3147 + node: serializeTree( 3148 + $(Stack, { 3149 + children: [ 3150 + $(Maybe, { 3151 + fallback: $(Text, { value: "nope" }), 3152 + children: $(External, { 3153 + children: $(Link, { 3154 + uri: $(Binding, { path: ["reply", "parent", "uri"] }), 3155 + }), 3156 + }), 3157 + }), 3158 + $(Text, { value: $(Binding, { path: ["text"] }) }), 3159 + ], 3160 + }) 3161 + ), 3162 + }, 3163 + imports: [ 3164 + HOST_PACK_URI, 3165 + "at://did:plc:test/at.inlay.pack/app", 3166 + ] as AtUriString[], 3167 + view: [ 3168 + { 3169 + $type: "at.inlay.component#viewRecord", 3170 + collection: "app.bsky.feed.post", 3171 + }, 3172 + ], 3173 + }; 3174 + 3175 + const { options } = world({ 3176 + ...APP_PACK, 3177 + ["at://did:plc:alice/app.bsky.feed.post/123"]: { 3178 + text: "Hello world", 3179 + }, 3180 + [`xrpc:${SERVICE_DID}:${External}`]: (params: { 3181 + body?: Record<string, unknown>; 3182 + }) => ({ 3183 + node: params.body!.children, 3184 + }), 3185 + }); 3186 + 3187 + const output = await renderToCompletion( 3188 + $(PostCard, { uri: "at://did:plc:alice/app.bsky.feed.post/123" }), 3189 + options, 3190 + createContext(postCardComponent) 3191 + ); 3192 + 3193 + assert.deepEqual( 3194 + output, 3195 + h("div", { 3196 + children: [ 3197 + h("span", { value: "nope" }), 3198 + h("span", { value: "Hello world" }), 3199 + ], 3200 + }) 3201 + ); 3202 + }); 3203 + 3204 + it("MissingError propagates through two levels of external components", async () => { 3205 + // <Maybe fallback={<Text value="caught" />}> 3206 + // <External> <- level 1, passes children through 3207 + // <External> <- level 2, passes children through 3208 + // <Link uri={reply.parent.uri} /> <- missing 3209 + // </External> 3210 + // </External> 3211 + // </Maybe> 3212 + const cardComponent: ComponentRecord = { 3213 + $type: "at.inlay.component", 3214 + type: Card, 3215 + body: { 3216 + $type: "at.inlay.component#bodyTemplate", 3217 + node: serializeTree( 3218 + $(Maybe, { 3219 + fallback: $(Text, { value: "caught" }), 3220 + children: $(External, { 3221 + children: $(External, { 3222 + children: $(Link, { 3223 + uri: $(Binding, { path: ["reply", "parent", "uri"] }), 3224 + }), 3225 + }), 3226 + }), 3227 + }) 3228 + ), 3229 + }, 3230 + imports: [ 3231 + HOST_PACK_URI, 3232 + "at://did:plc:test/at.inlay.pack/app", 3233 + ] as AtUriString[], 3234 + view: [ 3235 + { 3236 + $type: "at.inlay.component#viewRecord", 3237 + collection: "app.bsky.feed.post", 3238 + }, 3239 + ], 3240 + }; 3241 + 3242 + const { options } = world({ 3243 + ...APP_PACK, 3244 + ["at://did:plc:alice/app.bsky.feed.post/123"]: { text: "Hello" }, 3245 + [`xrpc:${SERVICE_DID}:${External}`]: (params: { 3246 + body?: Record<string, unknown>; 3247 + }) => ({ 3248 + node: params.body!.children, 3249 + }), 3250 + }); 3251 + 3252 + const output = await renderToCompletion( 3253 + $(Card, { uri: "at://did:plc:alice/app.bsky.feed.post/123" }), 3254 + options, 3255 + createContext(cardComponent) 3256 + ); 3257 + assert.deepEqual(output, h("span", { value: "caught" })); 3258 + }); 3259 + 3260 + it("component's internal Maybe catches before outer Maybe", async () => { 3261 + // SafeWrapper template: wraps children in its own Maybe. 3262 + // 3263 + // <Stack> 3264 + // <Text value="header" /> 3265 + // <Maybe fallback={<Text value="inner" />}> 3266 + // {children} 3267 + // </Maybe> 3268 + // </Stack> 3269 + // 3270 + // Usage: 3271 + // <Maybe fallback={<Text value="outer" />}> 3272 + // <SafeWrapper> 3273 + // <Link uri={reply.parent.uri} /> <- missing 3274 + // </SafeWrapper> 3275 + // </Maybe> 3276 + // <Text value="sibling" /> 3277 + // 3278 + // SafeWrapper's Maybe is closer to the error — it catches first. 3279 + // The outer Maybe never fires. Result: header + "inner" + "sibling". 3280 + const SafeWrapper = "test.app.SafeWrapper" as const; 3281 + const safeWrapperComponent: ComponentRecord = { 3282 + $type: "at.inlay.component", 3283 + type: SafeWrapper, 3284 + body: { 3285 + $type: "at.inlay.component#bodyTemplate", 3286 + node: serializeTree( 3287 + $(Stack, { 3288 + children: [ 3289 + $(Text, { value: "header" }), 3290 + $(Maybe, { 3291 + fallback: $(Text, { value: "inner" }), 3292 + children: $(Binding, { path: ["children"] }), 3293 + }), 3294 + ], 3295 + }) 3296 + ), 3297 + }, 3298 + imports: [HOST_PACK_URI], 3299 + }; 3300 + 3301 + const cardComponent: ComponentRecord = { 3302 + $type: "at.inlay.component", 3303 + type: Card, 3304 + body: { 3305 + $type: "at.inlay.component#bodyTemplate", 3306 + node: serializeTree( 3307 + $(Stack, { 3308 + children: [ 3309 + $(Maybe, { 3310 + fallback: $(Text, { value: "outer" }), 3311 + children: $(SafeWrapper, { 3312 + children: $(Link, { 3313 + uri: $(Binding, { path: ["reply", "parent", "uri"] }), 3314 + }), 3315 + }), 3316 + }), 3317 + $(Text, { value: "sibling" }), 3318 + ], 3319 + }) 3320 + ), 3321 + }, 3322 + imports: [ 3323 + HOST_PACK_URI, 3324 + "at://did:plc:test/at.inlay.pack/safe", 3325 + ] as AtUriString[], 3326 + view: [ 3327 + { 3328 + $type: "at.inlay.component#viewRecord", 3329 + collection: "app.bsky.feed.post", 3330 + }, 3331 + ], 3332 + }; 3333 + 3334 + const { options } = world({ 3335 + ["at://did:plc:test/at.inlay.component/safe"]: safeWrapperComponent, 3336 + ["at://did:plc:test/at.inlay.pack/safe"]: { 3337 + $type: "at.inlay.pack", 3338 + name: "safe", 3339 + exports: [ 3340 + { 3341 + type: SafeWrapper, 3342 + component: "at://did:plc:test/at.inlay.component/safe", 3343 + }, 3344 + ], 3345 + }, 3346 + ["at://did:plc:alice/app.bsky.feed.post/123"]: { text: "Hello" }, 3347 + }); 3348 + 3349 + const output = await renderToCompletion( 3350 + $(Card, { uri: "at://did:plc:alice/app.bsky.feed.post/123" }), 3351 + options, 3352 + createContext(cardComponent) 3353 + ); 3354 + assert.deepEqual( 3355 + output, 3356 + h("div", { 3357 + children: [ 3358 + h("div", { 3359 + children: [ 3360 + h("span", { value: "header" }), 3361 + h("span", { value: "inner" }), 3362 + ], 3363 + }), 3364 + h("span", { value: "sibling" }), 3365 + ], 3366 + }) 3367 + ); 3368 + }); 3369 + }); 3370 + 3371 + // ============================================================================ 3372 + // 12. Pagination — stateless cursors with embedded context 3373 + // ============================================================================ 3374 + // 3375 + // Some host primitives need server-side behavior. The List primitive 3376 + // fetches a page of items and returns a pager with resolved items and 3377 + // an opaque cursor. The cursor embeds the import context (pack URIs) 3378 + // so the host can resume rendering later without any server state. 3379 + 3380 + describe("server primitives", () => { 3381 + type PageData = { 3382 + items: Element[]; 3383 + cursor: string | null; 3384 + }; 3385 + 3386 + const sources: Record<string, Record<string, PageData>> = { 3387 + "did:plc:svc|app.bsky.getFollowers": { 3388 + "": { 3389 + items: [ 3390 + $(Avatar, { did: "did:plc:alice" }), 3391 + $(Avatar, { did: "did:plc:bob" }), 3392 + ], 3393 + cursor: "f2", 3394 + }, 3395 + f2: { 3396 + items: [$(Avatar, { did: "did:plc:charlie" })], 3397 + cursor: null, 3398 + }, 3399 + }, 3400 + "did:plc:svc|app.bsky.getPosts": { 3401 + "": { 3402 + items: [ 3403 + $(Cover, { did: "did:plc:alice" }), 3404 + $(Cover, { did: "did:plc:bob" }), 3405 + ], 3406 + cursor: "p2", 3407 + }, 3408 + p2: { 3409 + items: [$(Cover, { did: "did:plc:charlie" })], 3410 + cursor: null, 3411 + }, 3412 + }, 3413 + }; 3414 + 3415 + function fetchFromSource(did: string, nsid: string, cursor: string) { 3416 + return sources[`${did}|${nsid}`]![cursor]!; 3417 + } 3418 + 3419 + function setup() { 3420 + const rootComponent: ComponentRecord = { 3421 + $type: "at.inlay.component", 3422 + type: Root, 3423 + body: { 3424 + $type: "at.inlay.component#bodyTemplate", 3425 + node: serializeTree( 3426 + $(Fragment, { children: $(Binding, { path: ["children"] }) }) 3427 + ), 3428 + }, 3429 + imports: [HOST_PACK_URI], 3430 + }; 3431 + 3432 + const { options, log } = world(); 3433 + 3434 + const listPrimitive: Primitive = async (el, walk, ctx) => { 3435 + const props = (el.props ?? {}) as Record<string, unknown>; 3436 + const source = { did: props.did as string, nsid: props.nsid as string }; 3437 + const page = fetchFromSource(source.did, source.nsid, ""); 3438 + 3439 + const items = await Promise.all(page.items.map((item) => walk(item))); 3440 + 3441 + return h("pager", { 3442 + cursor: page.cursor 3443 + ? { imports: ctx.imports, source, next: page.cursor } 3444 + : null, 3445 + items, 3446 + }); 3447 + }; 3448 + 3449 + const extra = { [List]: listPrimitive }; 3450 + 3451 + async function continuePage(cursor: Record<string, unknown>) { 3452 + const { imports, source, next } = cursor as { 3453 + imports: AtUriString[]; 3454 + source: { did: string; nsid: string }; 3455 + next: string; 3456 + }; 3457 + const page = fetchFromSource(source.did, source.nsid, next); 3458 + const ctx: RenderContext = { imports }; 3459 + const items = await Promise.all( 3460 + page.items.map((item) => renderToCompletion(item, options, ctx, extra)) 3461 + ); 3462 + return { 3463 + items, 3464 + cursor: page.cursor ? { imports, source, next: page.cursor } : null, 3465 + }; 3466 + } 3467 + 3468 + return { options, log, rootComponent, extra, continuePage }; 3469 + } 3470 + 3471 + it("two independent lists paginate statelessly via opaque cursors", async () => { 3472 + const { options, rootComponent, extra, continuePage } = setup(); 3473 + const ctx = createContext(rootComponent); 3474 + 3475 + // First list: followers 3476 + const fCursor = { 3477 + imports: [HOST_PACK_URI], 3478 + source: { did: "did:plc:svc", nsid: "app.bsky.getFollowers" }, 3479 + next: "f2", 3480 + }; 3481 + assert.deepEqual( 3482 + await renderToCompletion( 3483 + $(Root, { 3484 + children: $(List, { 3485 + did: "did:plc:svc", 3486 + nsid: "app.bsky.getFollowers", 3487 + }), 3488 + }), 3489 + options, 3490 + ctx, 3491 + extra 3492 + ), 3493 + h("frag", { 3494 + children: h("pager", { 3495 + cursor: fCursor, 3496 + items: [ 3497 + h("img", { did: "did:plc:alice" }), 3498 + h("img", { did: "did:plc:bob" }), 3499 + ], 3500 + }), 3501 + }) 3502 + ); 3503 + 3504 + // Second list: posts 3505 + const pCursor = { 3506 + imports: [HOST_PACK_URI], 3507 + source: { did: "did:plc:svc", nsid: "app.bsky.getPosts" }, 3508 + next: "p2", 3509 + }; 3510 + assert.deepEqual( 3511 + await renderToCompletion( 3512 + $(Root, { 3513 + children: $(List, { did: "did:plc:svc", nsid: "app.bsky.getPosts" }), 3514 + }), 3515 + options, 3516 + ctx, 3517 + extra 3518 + ), 3519 + h("frag", { 3520 + children: h("pager", { 3521 + cursor: pCursor, 3522 + items: [ 3523 + h("figure", { did: "did:plc:alice" }), 3524 + h("figure", { did: "did:plc:bob" }), 3525 + ], 3526 + }), 3527 + }) 3528 + ); 3529 + 3530 + // Continue each list — cursor carries all needed context 3531 + assert.deepEqual(await continuePage(fCursor), { 3532 + items: [h("img", { did: "did:plc:charlie" })], 3533 + cursor: null, 3534 + }); 3535 + assert.deepEqual(await continuePage(pCursor), { 3536 + items: [h("figure", { did: "did:plc:charlie" })], 3537 + cursor: null, 3538 + }); 3539 + }); 3540 + });
+13
packages/@inlay/render/tsconfig.json
··· 1 + { 2 + "compilerOptions": { 3 + "target": "ES2022", 4 + "module": "esnext", 5 + "moduleResolution": "bundler", 6 + "allowImportingTsExtensions": true, 7 + "noEmit": true, 8 + "strict": true, 9 + "skipLibCheck": true, 10 + "esModuleInterop": true 11 + }, 12 + "include": ["src"] 13 + }
+22
proto/package.json
··· 1 + { 2 + "name": "proto", 3 + "private": true, 4 + "type": "module", 5 + "scripts": { 6 + "dev": "tsx --conditions source --watch src/index.tsx", 7 + "start": "tsx --conditions source src/index.tsx" 8 + }, 9 + "dependencies": { 10 + "@inlay/core": "*", 11 + "@inlay/render": "*", 12 + "@atproto/syntax": "^0.3.0", 13 + "@hono/node-server": "^1.19.9", 14 + "dotenv": "^16.5.0", 15 + "hono": "^4.12.2", 16 + "ioredis": "^5.6.0" 17 + }, 18 + "devDependencies": { 19 + "tsx": "^4.0.0", 20 + "typescript": "^5.9.0" 21 + } 22 + }
+230
proto/public/host-primitives.css
··· 1 + /* Structural reset for inlay custom elements. 2 + Display modes, flex, sizing. No visual styling. 3 + Everything visual comes from host-theme.css. */ 4 + 5 + at-inlay-root { 6 + display: block; 7 + width: fit-content; 8 + max-width: 100%; 9 + max-height: 100%; 10 + box-sizing: border-box; 11 + overflow-y: auto; 12 + overflow-x: hidden; 13 + overflow-wrap: break-word; 14 + position: relative; 15 + } 16 + at-inlay-root[data-page] { 17 + width: 100%; 18 + max-width: 600px; 19 + } 20 + at-inlay-root[data-full] { 21 + width: 100%; 22 + min-height: 100%; 23 + max-width: none; 24 + max-height: none; 25 + border-radius: 0; 26 + } 27 + at-inlay-root:has(org-atsui-stack) { 28 + width: 100%; 29 + } 30 + 31 + /* Stack — vertical flex, fills parent width */ 32 + 33 + org-atsui-stack { 34 + display: flex; 35 + flex-direction: column; 36 + width: 100%; 37 + box-sizing: border-box; 38 + text-align: inherit; 39 + } 40 + org-atsui-stack[align="start"] { 41 + align-items: flex-start; 42 + } 43 + org-atsui-stack[align="center"] { 44 + align-items: center; 45 + } 46 + org-atsui-stack[align="end"] { 47 + align-items: flex-end; 48 + } 49 + org-atsui-stack[align="stretch"] { 50 + align-items: stretch; 51 + } 52 + org-atsui-stack[sticky] { 53 + position: sticky; 54 + top: 0; 55 + z-index: 1; 56 + } 57 + 58 + /* Row — horizontal flex, hugs content */ 59 + 60 + org-atsui-row { 61 + display: flex; 62 + flex-direction: row; 63 + flex-wrap: nowrap; 64 + align-items: center; 65 + text-align: inherit; 66 + } 67 + org-atsui-row[align="start"] { 68 + align-items: flex-start; 69 + } 70 + org-atsui-row[align="center"] { 71 + align-items: center; 72 + } 73 + org-atsui-row[align="end"] { 74 + align-items: flex-end; 75 + } 76 + org-atsui-row[sticky] { 77 + position: sticky; 78 + top: 0; 79 + z-index: 1; 80 + } 81 + 82 + /* Justify — shared between Stack and Row */ 83 + 84 + :is(org-atsui-stack, org-atsui-row)[justify="between"] { 85 + justify-content: space-between; 86 + } 87 + :is(org-atsui-stack, org-atsui-row)[justify="center"] { 88 + justify-content: center; 89 + } 90 + :is(org-atsui-stack, org-atsui-row)[justify="end"] { 91 + justify-content: flex-end; 92 + } 93 + 94 + /* Fill — greedy child, takes remaining space on parent main axis */ 95 + 96 + org-atsui-fill { 97 + display: flex; 98 + flex-direction: column; 99 + flex: 1; 100 + min-width: 0; 101 + min-height: 0; 102 + } 103 + 104 + /* Text elements */ 105 + 106 + org-atsui-title, 107 + org-atsui-heading, 108 + org-atsui-text, 109 + org-atsui-caption { 110 + display: block; 111 + overflow-wrap: break-word; 112 + } 113 + org-atsui-timestamp { 114 + display: inline; 115 + } 116 + 117 + /* Avatar — fixed dimensions, never stretches */ 118 + 119 + org-atsui-avatar { 120 + display: block; 121 + overflow: hidden; 122 + flex-shrink: 0; 123 + } 124 + org-atsui-avatar[size="xsmall"] { 125 + width: 20px; 126 + height: 20px; 127 + } 128 + org-atsui-avatar[size="small"] { 129 + width: 32px; 130 + height: 32px; 131 + } 132 + org-atsui-avatar[size="medium"] { 133 + width: 48px; 134 + height: 48px; 135 + } 136 + org-atsui-avatar[size="large"] { 137 + width: 80px; 138 + height: 80px; 139 + } 140 + org-atsui-avatar img { 141 + width: 100%; 142 + height: 100%; 143 + object-fit: cover; 144 + display: block; 145 + } 146 + 147 + /* Cover — full-bleed background image */ 148 + 149 + org-atsui-cover { 150 + display: block; 151 + width: 100%; 152 + } 153 + org-atsui-cover::before { 154 + content: ""; 155 + display: block; 156 + } 157 + 158 + /* Clip — constrains child height relative to own width. 159 + cqi custom props are set on the outer element; the inner 160 + div resolves them relative to Clip's inline size. */ 161 + 162 + org-atsui-clip { 163 + display: block; 164 + container-type: inline-size; 165 + } 166 + org-atsui-clip > div { 167 + display: grid; 168 + grid-template-rows: auto; 169 + min-height: var(--clip-min, 0px); 170 + max-height: var(--clip-max, none); 171 + overflow: hidden; 172 + } 173 + 174 + /* Blob — image container */ 175 + 176 + org-atsui-blob { 177 + display: block; 178 + overflow: hidden; 179 + width: 100%; 180 + } 181 + org-atsui-blob[fit] { 182 + height: 100%; 183 + } 184 + org-atsui-blob img { 185 + display: block; 186 + width: 100%; 187 + object-fit: cover; 188 + } 189 + org-atsui-blob[fit] img { 190 + height: 100%; 191 + } 192 + org-atsui-blob[fit="contain"] img { 193 + object-fit: contain; 194 + } 195 + 196 + /* Link — transparent wrapper */ 197 + 198 + org-atsui-link { 199 + display: contents; 200 + } 201 + org-atsui-link a { 202 + overflow-wrap: break-word; 203 + min-width: 0; 204 + } 205 + 206 + /* Tabs */ 207 + 208 + org-atsui-tabs { 209 + display: flex; 210 + flex-direction: column; 211 + } 212 + org-atsui-tabs .tabs-bar { 213 + display: flex; 214 + flex-direction: row; 215 + position: sticky; 216 + top: 0; 217 + z-index: 1; 218 + } 219 + org-atsui-tabs .tabs-bar button { 220 + all: unset; 221 + cursor: pointer; 222 + } 223 + 224 + /* Grid — equal columns */ 225 + 226 + org-atsui-grid { 227 + display: grid; 228 + grid-template-columns: repeat(var(--grid-cols, 3), 1fr); 229 + width: 100%; 230 + }
+169
proto/public/host-theme.css
··· 1 + /* host-theme.css */ 2 + 3 + at-inlay-root { 4 + --text-primary: #111; 5 + --text-body: #444; 6 + --text-secondary: #999; 7 + 8 + font-family: system-ui, sans-serif; 9 + font-size: 15px; 10 + line-height: 1.5; 11 + color: var(--text-primary); 12 + background: #fff; 13 + border-radius: 12px; 14 + -webkit-font-smoothing: antialiased; 15 + } 16 + 17 + at-inlay-root[data-page], 18 + at-inlay-root[data-full] { 19 + border-radius: 0; 20 + } 21 + 22 + /* Gap */ 23 + 24 + :is(org-atsui-stack, org-atsui-row, org-atsui-grid)[gap="none"] { 25 + gap: 0; 26 + } 27 + :is(org-atsui-stack, org-atsui-row, org-atsui-grid)[gap="small"] { 28 + gap: 6px; 29 + } 30 + :is(org-atsui-stack, org-atsui-row, org-atsui-grid)[gap="medium"] { 31 + gap: 12px; 32 + } 33 + :is(org-atsui-stack, org-atsui-row, org-atsui-grid)[gap="large"] { 34 + gap: 24px; 35 + } 36 + 37 + /* Type */ 38 + 39 + org-atsui-title { 40 + font-size: 1.5rem; 41 + font-weight: 700; 42 + line-height: 1.2; 43 + letter-spacing: -0.02em; 44 + color: var(--text-primary); 45 + } 46 + 47 + org-atsui-heading { 48 + font-weight: 600; 49 + color: var(--text-primary); 50 + } 51 + 52 + org-atsui-text { 53 + color: var(--text-body); 54 + } 55 + 56 + org-atsui-text b, 57 + org-atsui-text strong { 58 + font-weight: 600; 59 + color: var(--text-primary); 60 + } 61 + 62 + org-atsui-caption { 63 + font-size: 0.8125rem; 64 + color: var(--text-secondary); 65 + } 66 + 67 + /* Avatar */ 68 + 69 + org-atsui-avatar { 70 + border-radius: 50%; 71 + } 72 + 73 + /* Cover */ 74 + 75 + org-atsui-cover::before { 76 + height: 200px; 77 + background: 78 + linear-gradient(to top, rgba(0, 0, 0, 0.4) 0%, transparent 50%), 79 + var(--cover-src) center / cover no-repeat; 80 + } 81 + 82 + /* Avatar lift */ 83 + 84 + org-atsui-avatar[lift] { 85 + position: relative; 86 + z-index: 1; 87 + box-shadow: 0 0 0 3px #fff; 88 + } 89 + org-atsui-avatar[lift][size="xsmall"] { 90 + margin-top: calc(-10px - var(--inset, 0px)); 91 + } 92 + org-atsui-avatar[lift][size="small"] { 93 + margin-top: calc(-16px - var(--inset, 0px)); 94 + } 95 + org-atsui-avatar[lift][size="medium"] { 96 + margin-top: calc(-24px - var(--inset, 0px)); 97 + } 98 + org-atsui-avatar[lift][size="large"] { 99 + margin-top: calc(-40px - var(--inset, 0px)); 100 + } 101 + 102 + /* Blob */ 103 + 104 + org-atsui-blob img { 105 + border-radius: 8px; 106 + } 107 + 108 + /* Inset */ 109 + 110 + :is(org-atsui-stack, org-atsui-row)[inset] { 111 + --inset: 12px; 112 + padding: 12px; 113 + } 114 + 115 + /* Link */ 116 + 117 + org-atsui-link a { 118 + text-decoration: none; 119 + color: inherit; 120 + display: block; 121 + } 122 + org-atsui-link[decoration="underline"] a { 123 + text-decoration: underline; 124 + color: #1a6dca; 125 + } 126 + 127 + org-atsui-link a:hover { 128 + opacity: 0.85; 129 + } 130 + 131 + /* List separators */ 132 + 133 + hr { 134 + border: none; 135 + height: 1px; 136 + background: #eee; 137 + margin: 0; 138 + width: 100%; 139 + } 140 + 141 + /* Tabs */ 142 + 143 + org-atsui-tabs .tabs-bar { 144 + gap: 0; 145 + border-bottom: 1px solid #eee; 146 + padding: 0 16px; 147 + background: #fff; 148 + } 149 + 150 + org-atsui-tabs .tabs-bar button { 151 + flex: 1; 152 + text-align: center; 153 + font-size: 0.8125rem; 154 + font-weight: 500; 155 + padding: 10px 0; 156 + color: var(--text-secondary); 157 + border-bottom: 2px solid transparent; 158 + margin-bottom: -1px; 159 + } 160 + 161 + org-atsui-tabs .tabs-bar button[aria-selected="true"] { 162 + color: var(--text-primary); 163 + border-bottom-color: var(--text-primary); 164 + font-weight: 600; 165 + } 166 + 167 + org-atsui-tabs .tabs-bar button:hover:not([aria-selected="true"]) { 168 + color: #666; 169 + }
+114
proto/src/cache.ts
··· 1 + // Redis cache layer for proto host 2 + // Uses the same Redis instance + key scheme as the RSC host's cache-handler, 3 + // so the existing invalidator (Jetstream firehose listener) works out of the box. 4 + 5 + import Redis from "ioredis"; 6 + 7 + const CACHE_PREFIX = "inlay:cache:"; 8 + const INDEX_PREFIX = "inlay:idx:"; 9 + 10 + // Life → TTL seconds 11 + const LIFE_TTL: Record<string, number> = { 12 + seconds: 30, 13 + minutes: 5 * 60, 14 + hours: 3600, 15 + max: 86400, 16 + }; 17 + 18 + let redis: Redis | null = null; 19 + 20 + function getRedis(): Redis { 21 + if (!redis) { 22 + redis = new Redis(process.env.REDIS_URL || "redis://localhost:6379", { 23 + maxRetriesPerRequest: 3, 24 + lazyConnect: true, 25 + }); 26 + redis.on("error", (err) => { 27 + console.error("[redis] connection error:", err.message); 28 + }); 29 + redis.on("connect", () => { 30 + console.log("[redis] connected"); 31 + }); 32 + } 33 + return redis; 34 + } 35 + 36 + type CacheTag = { 37 + $type: string; 38 + uri?: string; 39 + subject?: string; 40 + from?: string; 41 + }; 42 + 43 + type CachePolicy = { 44 + life?: string; 45 + tags?: CacheTag[]; 46 + }; 47 + 48 + // Convert inlay cache tags to the string format used by the reverse index. 49 + // Must match the format in app/sandbox/render.tsx (callComponentCached) 50 + // and invalidator/src/cache-invalidation.ts (getRecordPatterns, getLinkPatterns). 51 + function tagToIndexKeys(tag: CacheTag): string[] { 52 + if (tag.$type === "at.inlay.defs#tagRecord") { 53 + return [`record:${tag.uri}`]; 54 + } 55 + if (tag.$type === "at.inlay.defs#tagLink") { 56 + if (tag.from) { 57 + return [`link:${tag.subject}:at://*/${tag.from}/*`]; 58 + } 59 + return [`link:${tag.subject}:at://*/*`]; 60 + } 61 + return []; 62 + } 63 + 64 + export async function cacheGet(key: string): Promise<unknown | undefined> { 65 + try { 66 + const stored = await getRedis().get(CACHE_PREFIX + key); 67 + if (!stored) return undefined; 68 + const data = JSON.parse(stored); 69 + // Check TTL expiration 70 + if (Date.now() > data.timestamp + data.revalidate * 1000) { 71 + await getRedis().del(CACHE_PREFIX + key); 72 + return undefined; 73 + } 74 + return data.value; 75 + } catch { 76 + return undefined; 77 + } 78 + } 79 + 80 + export async function cacheSet( 81 + key: string, 82 + value: unknown, 83 + policy?: CachePolicy 84 + ): Promise<void> { 85 + try { 86 + const life = policy?.life ?? "hours"; 87 + const ttl = LIFE_TTL[life] ?? 3600; 88 + 89 + await getRedis().set( 90 + CACHE_PREFIX + key, 91 + JSON.stringify({ 92 + value, 93 + tags: (policy?.tags ?? []).flatMap(tagToIndexKeys), 94 + timestamp: Date.now(), 95 + revalidate: ttl, 96 + }), 97 + "EX", 98 + ttl 99 + ); 100 + 101 + // Build reverse index so the invalidator can find these entries 102 + const indexKeys = (policy?.tags ?? []).flatMap(tagToIndexKeys); 103 + if (indexKeys.length > 0) { 104 + const pipeline = getRedis().pipeline(); 105 + for (const indexKey of indexKeys) { 106 + pipeline.sadd(INDEX_PREFIX + indexKey, key); 107 + pipeline.expire(INDEX_PREFIX + indexKey, ttl + 3600); 108 + } 109 + await pipeline.exec(); 110 + } 111 + } catch (err) { 112 + console.error("[cache] set error:", err); 113 + } 114 + }
+5
proto/src/env.ts
··· 1 + import dotenv from "dotenv"; 2 + import { resolve } from "path"; 3 + const root = resolve(process.cwd(), ".."); 4 + dotenv.config({ path: resolve(root, ".env.local") }); 5 + dotenv.config({ path: resolve(root, ".env") });
+243
proto/src/index.tsx
··· 1 + import "./env.ts"; 2 + import { Hono } from "hono"; 3 + import { raw } from "hono/html"; 4 + import { renderToReadableStream } from "hono/jsx/streaming"; 5 + import { ErrorBoundary } from "hono/jsx"; 6 + import { serveStatic } from "@hono/node-server/serve-static"; 7 + import { serve } from "@hono/node-server"; 8 + import { createResolver } from "./resolver.ts"; 9 + import { 10 + renderNode, 11 + createContext, 12 + createRenderOptions, 13 + initRender, 14 + type JSXElement, 15 + } from "./render.tsx"; 16 + import { deserializeTree, $ } from "@inlay/core"; 17 + import { setComponentUri } from "./primitives.tsx"; 18 + import { resolveDidToService } from "./resolve.ts"; 19 + import "./types.ts"; 20 + 21 + import type { RenderContext } from "@inlay/render"; 22 + 23 + const app = new Hono(); 24 + 25 + const resolver = createResolver(); 26 + const renderOptions = createRenderOptions(resolver); 27 + 28 + initRender(renderOptions); 29 + 30 + // --- Static files --- 31 + app.use("/public/*", serveStatic({ root: "./" })); 32 + 33 + app.get("/", (c) => 34 + c.redirect( 35 + "/at/did:plc:ragtjsm2j2vknwkz3zp4oxrd/app.bsky.actor.profile/self?componentUri=at%3A%2F%2Fdid%3Aplc%3Afpruhuo22xkm5o7ttr2ktxdo%2Fat.inlay.component%2F3mf6ahin36s2e&layout=page" 36 + ) 37 + ); 38 + 39 + // --- Page shell --- 40 + function Shell({ children }: { children: JSXElement }) { 41 + return ( 42 + <html lang="en"> 43 + <head> 44 + <meta charset="utf-8" /> 45 + <meta name="viewport" content="width=device-width, initial-scale=1" /> 46 + <title>inlay proto</title> 47 + <link rel="stylesheet" href="/public/host-primitives.css" /> 48 + <link rel="stylesheet" href="/public/host-theme.css" /> 49 + <script src="https://unpkg.com/htmx.org@2.0.4"></script> 50 + <script src="https://unpkg.com/htmx-ext-preload@2.1.0/preload.js"></script> 51 + {raw(`<style> 52 + body { margin: 0; font-family: system-ui, sans-serif; background: var(--surface); color: var(--text); } 53 + .error { color: red; padding: 8px; font-size: 12px; } 54 + .list-sentinel { padding: 20px; text-align: center; color: var(--text-secondary); } 55 + .tabs-bar { display: flex; gap: 0; border-bottom: 1px solid var(--border); } 56 + .tabs-bar button { padding: 12px 20px; border: none; background: none; color: var(--text-secondary); cursor: pointer; border-bottom: 2px solid transparent; font-size: 14px; font-weight: 500; } 57 + .tabs-bar button[aria-selected="true"] { color: var(--text); border-bottom-color: var(--accent); } 58 + hr { border: none; border-top: 1px solid var(--border); margin: 0; display: var(--separator, block); } 59 + </style>`)} 60 + {raw(`<script> 61 + function switchTab(btn, index) { 62 + var tabs = btn.closest('org-atsui-tabs'); 63 + tabs.querySelectorAll('.tabs-bar button').forEach(function(b, i) { 64 + b.setAttribute('aria-selected', i === index ? 'true' : 'false'); 65 + }); 66 + tabs.querySelectorAll('.tab-panel').forEach(function(p, i) { 67 + p.hidden = i !== index; 68 + }); 69 + window.scrollTo(0, 0); 70 + } 71 + </script>`)} 72 + </head> 73 + <body hx-ext="preload"> 74 + {children} 75 + {raw(`<script> 76 + (function() { 77 + var seen = new Set(); 78 + var io = new IntersectionObserver(function(entries) { 79 + entries.forEach(function(e) { 80 + if (!e.isIntersecting) return; 81 + var a = e.target; 82 + if (seen.has(a.href)) return; 83 + seen.add(a.href); 84 + io.unobserve(a); 85 + var link = document.createElement('link'); 86 + link.rel = 'prefetch'; 87 + link.href = a.href; 88 + document.head.appendChild(link); 89 + }); 90 + }); 91 + new MutationObserver(function() { 92 + document.querySelectorAll('a[href^="/at/"]').forEach(function(a) { 93 + if (!seen.has(a.href)) io.observe(a); 94 + }); 95 + }).observe(document.body, { childList: true, subtree: true }); 96 + document.querySelectorAll('a[href^="/at/"]').forEach(function(a) { 97 + io.observe(a); 98 + }); 99 + })(); 100 + </script>`)} 101 + </body> 102 + </html> 103 + ); 104 + } 105 + 106 + // --- Main route --- 107 + app.get("/at/:did/:collection/:rkey", async (c) => { 108 + const { did, collection, rkey } = c.req.param(); 109 + const componentUri = c.req.query("componentUri"); 110 + 111 + if (!componentUri) { 112 + return c.html( 113 + <Shell> 114 + <div>Missing componentUri query parameter</div> 115 + </Shell> 116 + ); 117 + } 118 + 119 + try { 120 + const component = await resolver.fetchRecord(componentUri as any); 121 + if (!component) { 122 + return c.html( 123 + <Shell> 124 + <div>Component not found: {componentUri}</div> 125 + </Shell> 126 + ); 127 + } 128 + 129 + const componentRecord = component as { 130 + type: string; 131 + imports?: string[]; 132 + body?: unknown; 133 + view?: unknown[]; 134 + }; 135 + const uri = `at://${did}/${collection}/${rkey}`; 136 + const element = $(componentRecord.type, { uri }); 137 + const context = createContext(componentRecord as any, componentUri); 138 + setComponentUri(componentUri); 139 + 140 + const body = await renderNode(element, context, renderOptions); 141 + 142 + const stream = renderToReadableStream( 143 + <Shell> 144 + <ErrorBoundary 145 + fallbackRender={(e) => <div class="error">{e.message}</div>} 146 + > 147 + {body} 148 + </ErrorBoundary> 149 + </Shell> 150 + ); 151 + return c.body(stream, { 152 + headers: { 153 + "Content-Type": "text/html; charset=UTF-8", 154 + "Transfer-Encoding": "chunked", 155 + }, 156 + }); 157 + } catch (e) { 158 + const msg = e instanceof Error ? e.message : String(e); 159 + return c.html( 160 + <Shell> 161 + <div class="error">Render error: {msg}</div> 162 + </Shell> 163 + ); 164 + } 165 + }); 166 + 167 + // --- HTMX list pagination endpoint --- 168 + app.get("/htmx/list", async (c) => { 169 + const query = c.req.query("query"); 170 + const did = c.req.query("did"); 171 + const cursor = c.req.query("cursor"); 172 + const inputStr = c.req.query("input"); 173 + const importsStr = c.req.query("imports"); 174 + 175 + if (!query || !did || !cursor) { 176 + return c.html(<div class="error">Missing params</div>); 177 + } 178 + 179 + try { 180 + const input = inputStr ? JSON.parse(decodeURIComponent(inputStr)) : {}; 181 + const imports = importsStr 182 + ? JSON.parse(decodeURIComponent(importsStr)) 183 + : []; 184 + 185 + const ctx: RenderContext = { imports }; 186 + 187 + const serviceUrl = await resolveDidToService(did, "#inlay"); 188 + const params = new URLSearchParams(); 189 + for (const [k, v] of Object.entries({ ...input, cursor })) { 190 + if (v != null) params.set(k, String(v)); 191 + } 192 + const url = `${serviceUrl}/xrpc/${query}?${params.toString()}`; 193 + const res = await fetch(url); 194 + 195 + if (!res.ok) { 196 + return c.html(<div class="error">List query failed: {res.status}</div>); 197 + } 198 + 199 + const page = (await res.json()) as { items: unknown[]; cursor?: string }; 200 + 201 + const resolved = await Promise.all( 202 + page.items.map((item) => 203 + renderNode(deserializeTree(item), ctx, renderOptions) 204 + ) 205 + ); 206 + const renderedItems: JSXElement[] = []; 207 + for (let i = 0; i < resolved.length; i++) { 208 + if (i > 0) renderedItems.push(<hr />); 209 + renderedItems.push(resolved[i]); 210 + } 211 + 212 + const importsParam = encodeURIComponent(JSON.stringify(imports)); 213 + const sentinelUrl = page.cursor 214 + ? `/htmx/list?query=${encodeURIComponent(query)}&did=${encodeURIComponent(did)}&cursor=${encodeURIComponent(page.cursor)}&input=${encodeURIComponent(JSON.stringify(input))}&imports=${importsParam}` 215 + : null; 216 + 217 + c.header("Cache-Control", "private, max-age=120"); 218 + return c.html( 219 + <> 220 + {renderedItems} 221 + {sentinelUrl ? ( 222 + <div 223 + hx-get={sentinelUrl} 224 + hx-trigger="intersect once" 225 + hx-swap="outerHTML" 226 + preload="preload:init" 227 + class="list-sentinel" 228 + > 229 + Loading... 230 + </div> 231 + ) : null} 232 + </> 233 + ); 234 + } catch (e) { 235 + const msg = e instanceof Error ? e.message : String(e); 236 + return c.html(<div class="error">List error: {msg}</div>); 237 + } 238 + }); 239 + 240 + // --- Start server --- 241 + const port = parseInt(process.env.PORT ?? "3001"); 242 + console.log(`Proto server starting on http://localhost:${port}`); 243 + serve({ fetch: app.fetch, port });
+527
proto/src/primitives.tsx
··· 1 + import type { HtmlEscapedString } from "hono/utils/html"; 2 + import { Suspense } from "hono/jsx/streaming"; 3 + import { ErrorBoundary } from "hono/jsx"; 4 + import type { RenderContext } from "@inlay/render"; 5 + import { MissingError } from "@inlay/render"; 6 + import { resolveDidToService } from "./resolve.ts"; 7 + import { isValidElement, deserializeTree } from "@inlay/core"; 8 + import "./types.ts"; 9 + 10 + // Hono's JSX.Element — what all JSX expressions produce 11 + type JSXElement = HtmlEscapedString | Promise<HtmlEscapedString>; 12 + 13 + // renderNode is injected to avoid circular deps 14 + let _renderNode: (node: unknown, ctx: RenderContext) => Promise<JSXElement>; 15 + let _componentUri: string | undefined; 16 + 17 + export function setRenderNode( 18 + fn: (node: unknown, ctx: RenderContext) => Promise<JSXElement> 19 + ) { 20 + _renderNode = fn; 21 + } 22 + 23 + export function setComponentUri(uri: string) { 24 + _componentUri = uri; 25 + } 26 + 27 + function rn(node: unknown, ctx: RenderContext): Promise<JSXElement> { 28 + return _renderNode(node, ctx); 29 + } 30 + 31 + type PrimitiveProps = { ctx: RenderContext; props: unknown }; 32 + type PrimitiveFn = (p: PrimitiveProps) => JSXElement | Promise<JSXElement>; 33 + 34 + // --- Blob resolution --- 35 + 36 + async function resolveSrc(src: unknown, did?: string): Promise<string | null> { 37 + if (src == null) return null; 38 + if (typeof src === "string") return src; 39 + if (typeof src === "object") { 40 + const blob = src as Record<string, unknown>; 41 + const ref = blob.ref as Record<string, unknown> | undefined; 42 + const cid = ref?.$link as string | undefined; 43 + if (cid && did) { 44 + const pds = await resolveDidToService(did); 45 + return `${pds}/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(did)}&cid=${encodeURIComponent(cid)}`; 46 + } 47 + } 48 + return null; 49 + } 50 + 51 + function resolveAvatarSrc(src: unknown, did?: string): string | null { 52 + if (!did || src == null || typeof src !== "object") return null; 53 + const ref = (src as Record<string, unknown>).ref as 54 + | Record<string, unknown> 55 + | undefined; 56 + const cid = ref?.$link as string | undefined; 57 + if (!cid) return null; 58 + return `https://cdn.bsky.app/img/avatar_thumbnail/plain/${did}/${cid}@jpeg`; 59 + } 60 + 61 + // --- Layout primitives --- 62 + 63 + async function Stack({ ctx, props }: PrimitiveProps) { 64 + const p = props as { 65 + gap?: string; 66 + align?: string; 67 + justify?: string; 68 + inset?: boolean; 69 + sticky?: boolean; 70 + opaque?: boolean; 71 + separator?: boolean; 72 + children?: unknown[]; 73 + }; 74 + return ( 75 + <org-atsui-stack 76 + gap={p.gap ?? "medium"} 77 + align={p.align ?? "stretch"} 78 + justify={p.justify || undefined} 79 + inset={p.inset || undefined} 80 + sticky={p.sticky || undefined} 81 + opaque={p.opaque || undefined} 82 + > 83 + <Await ctx={ctx}>{p.children ?? []}</Await> 84 + </org-atsui-stack> 85 + ); 86 + } 87 + 88 + async function Row({ ctx, props }: PrimitiveProps) { 89 + const p = props as { 90 + gap?: string; 91 + align?: string; 92 + justify?: string; 93 + inset?: boolean; 94 + sticky?: boolean; 95 + opaque?: boolean; 96 + children?: unknown[]; 97 + }; 98 + return ( 99 + <org-atsui-row 100 + gap={p.gap ?? "medium"} 101 + align={p.align ?? "center"} 102 + justify={p.justify || undefined} 103 + inset={p.inset || undefined} 104 + sticky={p.sticky || undefined} 105 + opaque={p.opaque || undefined} 106 + > 107 + <Await ctx={ctx}>{p.children ?? []}</Await> 108 + </org-atsui-row> 109 + ); 110 + } 111 + 112 + async function Fill({ ctx, props }: PrimitiveProps) { 113 + const p = props as { children?: unknown[] }; 114 + return ( 115 + <org-atsui-fill> 116 + <Await ctx={ctx}>{p.children ?? []}</Await> 117 + </org-atsui-fill> 118 + ); 119 + } 120 + 121 + async function Card({ ctx, props }: PrimitiveProps) { 122 + const p = props as { gap?: string; children?: unknown[] }; 123 + const rendered = await Promise.all( 124 + (p.children ?? []).map((c) => rn(c as unknown, ctx)) 125 + ); 126 + return <org-atsui-card gap={p.gap ?? "medium"}>{rendered}</org-atsui-card>; 127 + } 128 + 129 + async function Grid({ ctx, props }: PrimitiveProps) { 130 + const p = props as { columns?: number; gap?: string; children?: unknown[] }; 131 + return ( 132 + <org-atsui-grid 133 + gap={p.gap ?? "small"} 134 + style={`--grid-cols:${p.columns ?? 3};--separator:none`} 135 + > 136 + <Await ctx={ctx}>{p.children ?? []}</Await> 137 + </org-atsui-grid> 138 + ); 139 + } 140 + 141 + async function Clip({ ctx, props }: PrimitiveProps) { 142 + const p = props as { 143 + min?: { width: number; height: number }; 144 + max?: { width: number; height: number }; 145 + children?: unknown[]; 146 + }; 147 + const rendered = await Promise.all( 148 + (p.children ?? []).map((c) => rn(c as unknown, ctx)) 149 + ); 150 + 151 + const styleParts: string[] = []; 152 + if (p.min && p.min.width > 0) { 153 + styleParts.push(`--clip-min:${(p.min.height / p.min.width) * 100}cqi`); 154 + } 155 + if (p.max && p.max.width > 0) { 156 + styleParts.push(`--clip-max:${(p.max.height / p.max.width) * 100}cqi`); 157 + } 158 + 159 + return ( 160 + <org-atsui-clip style={styleParts.join(";") || undefined}> 161 + <div>{rendered}</div> 162 + </org-atsui-clip> 163 + ); 164 + } 165 + 166 + // --- Image primitives --- 167 + 168 + async function Cover({ props }: PrimitiveProps) { 169 + const p = props as { src?: unknown; did?: string }; 170 + if (p.src == null) return <org-atsui-cover />; 171 + const src = await resolveSrc(p.src, p.did); 172 + return ( 173 + <org-atsui-cover style={src ? `--cover-src:url(${src})` : undefined} /> 174 + ); 175 + } 176 + 177 + async function Avatar({ props }: PrimitiveProps) { 178 + const p = props as { 179 + src?: unknown; 180 + did?: string; 181 + size?: string; 182 + lift?: boolean; 183 + }; 184 + const src = 185 + resolveAvatarSrc(p.src, p.did) ?? (await resolveSrc(p.src, p.did)); 186 + return ( 187 + <org-atsui-avatar size={p.size ?? "medium"} lift={p.lift || undefined}> 188 + <img src={src ?? undefined} alt="" /> 189 + </org-atsui-avatar> 190 + ); 191 + } 192 + 193 + async function Blob({ props }: PrimitiveProps) { 194 + const p = props as { 195 + src?: unknown; 196 + did?: string; 197 + alt?: string; 198 + ratio?: { width: number; height: number }; 199 + fit?: string; 200 + }; 201 + const src = await resolveSrc(p.src, p.did); 202 + const fit = p.fit === "cover" || p.fit === "contain" ? p.fit : undefined; 203 + let style: string | undefined; 204 + if (p.ratio && p.ratio.width > 0 && p.ratio.height > 0) { 205 + style = `aspect-ratio:${p.ratio.width} / ${p.ratio.height}`; 206 + } 207 + return ( 208 + <org-atsui-blob fit={fit} style={style}> 209 + <img src={src ?? undefined} alt={p.alt ?? ""} /> 210 + </org-atsui-blob> 211 + ); 212 + } 213 + 214 + // --- Text primitives --- 215 + 216 + async function Title({ ctx, props }: PrimitiveProps) { 217 + const p = props as { children?: unknown }; 218 + return <org-atsui-title>{await rn(p.children, ctx)}</org-atsui-title>; 219 + } 220 + 221 + async function Heading({ ctx, props }: PrimitiveProps) { 222 + const p = props as { children?: unknown }; 223 + return <org-atsui-heading>{await rn(p.children, ctx)}</org-atsui-heading>; 224 + } 225 + 226 + async function Text({ ctx, props }: PrimitiveProps) { 227 + const p = props as { children?: unknown }; 228 + return <org-atsui-text>{await rn(p.children, ctx)}</org-atsui-text>; 229 + } 230 + 231 + async function Caption({ ctx, props }: PrimitiveProps) { 232 + const p = props as { children?: unknown }; 233 + return <org-atsui-caption>{await rn(p.children, ctx)}</org-atsui-caption>; 234 + } 235 + 236 + function Timestamp({ props }: PrimitiveProps) { 237 + const p = props as { value?: string }; 238 + if (!p.value) return <org-atsui-timestamp />; 239 + 240 + const date = new Date(p.value); 241 + const now = Date.now(); 242 + const diff = now - date.getTime(); 243 + let relative: string; 244 + 245 + if (diff < 60_000) relative = "now"; 246 + else if (diff < 3_600_000) relative = `${Math.floor(diff / 60_000)}m`; 247 + else if (diff < 86_400_000) relative = `${Math.floor(diff / 3_600_000)}h`; 248 + else if (diff < 2_592_000_000) relative = `${Math.floor(diff / 86_400_000)}d`; 249 + else 250 + relative = date.toLocaleDateString(undefined, { 251 + month: "short", 252 + day: "numeric", 253 + }); 254 + 255 + const title = date.toLocaleString(undefined, { 256 + year: "numeric", 257 + month: "short", 258 + day: "numeric", 259 + hour: "numeric", 260 + minute: "2-digit", 261 + }); 262 + 263 + return <org-atsui-timestamp title={title}>{relative}</org-atsui-timestamp>; 264 + } 265 + 266 + // --- Link --- 267 + 268 + function parseAtUri(uri: string) { 269 + const match = uri.match(/^at:\/\/([^/]+)(?:\/([^/]+)(?:\/([^/]+))?)?$/); 270 + if (!match) return null; 271 + return { did: match[1], collection: match[2], rkey: match[3] }; 272 + } 273 + 274 + async function Link({ ctx, props }: PrimitiveProps) { 275 + const p = props as { uri?: string; decoration?: string; children?: unknown }; 276 + if (!p.uri) return <></>; 277 + 278 + const content = p.children != null ? await rn(p.children, ctx) : p.uri; 279 + const qs = _componentUri 280 + ? `?componentUri=${encodeURIComponent(_componentUri)}` 281 + : ""; 282 + 283 + if (p.uri.startsWith("did:")) { 284 + return ( 285 + <org-atsui-link decoration={p.decoration || undefined}> 286 + <a href={`/at/${p.uri}${qs}`}>{content}</a> 287 + </org-atsui-link> 288 + ); 289 + } 290 + 291 + const parsed = p.uri.startsWith("at://") ? parseAtUri(p.uri) : null; 292 + if (parsed) { 293 + const href = parsed.collection 294 + ? `/at/${parsed.did}/${parsed.collection}/${parsed.rkey}${qs}` 295 + : `/at/${parsed.did}${qs}`; 296 + return ( 297 + <org-atsui-link decoration={p.decoration || undefined}> 298 + <a href={href}>{content}</a> 299 + </org-atsui-link> 300 + ); 301 + } 302 + 303 + return ( 304 + <org-atsui-link decoration={p.decoration || undefined}> 305 + <a href={p.uri} target="_blank" rel="noopener noreferrer"> 306 + {content} 307 + </a> 308 + </org-atsui-link> 309 + ); 310 + } 311 + 312 + // --- Fragment / Maybe / Placeholder / Throw --- 313 + 314 + async function Fragment({ ctx, props }: PrimitiveProps) { 315 + const p = props as { children?: unknown[] }; 316 + return <Await ctx={ctx}>{p.children}</Await>; 317 + } 318 + 319 + function Maybe({ ctx, props }: PrimitiveProps) { 320 + const p = props as { children?: unknown[]; fallback?: unknown }; 321 + const children = (p.children ?? []) as unknown[]; 322 + 323 + return ( 324 + <ErrorBoundary 325 + fallbackRender={(error) => { 326 + if (error instanceof MissingError) { 327 + if (p.fallback && isValidElement(p.fallback)) { 328 + return rn(p.fallback, ctx) as any; 329 + } 330 + return <></>; 331 + } 332 + return <div class="error">{error.message}</div>; 333 + }} 334 + > 335 + <Await ctx={ctx}>{children}</Await> 336 + </ErrorBoundary> 337 + ); 338 + } 339 + 340 + async function Placeholder({ ctx, props }: PrimitiveProps) { 341 + const p = props as { children?: unknown[]; fallback?: unknown }; 342 + const fallback = 343 + p.fallback && isValidElement(p.fallback) ? ( 344 + await rn(p.fallback, ctx) 345 + ) : ( 346 + <org-atsui-caption>Loading...</org-atsui-caption> 347 + ); 348 + return ( 349 + <Suspense fallback={fallback}> 350 + <Await ctx={ctx}>{p.children}</Await> 351 + </Suspense> 352 + ); 353 + } 354 + 355 + async function Await({ 356 + children = [], 357 + ctx, 358 + }: { 359 + children?: unknown | unknown[]; 360 + ctx: RenderContext; 361 + }) { 362 + const items = Array.isArray(children) ? children : [children]; 363 + const rendered = await Promise.all(items.map((c) => rn(c, ctx))); 364 + return <>{rendered}</>; 365 + } 366 + 367 + function Throw({ props }: PrimitiveProps) { 368 + const p = props as { message?: string }; 369 + return <div class="error">{p.message ?? "Unknown error"}</div>; 370 + } 371 + 372 + // --- Tabs --- 373 + 374 + async function Tabs({ ctx, props }: PrimitiveProps) { 375 + const p = props as { 376 + items?: Array<{ key: string; label: string; content: unknown }>; 377 + }; 378 + const items = p.items ?? []; 379 + if (items.length === 0) return <></>; 380 + 381 + const panels: JSXElement[] = []; 382 + for (let i = 0; i < items.length; i++) { 383 + const content = await rn(items[i].content, ctx); 384 + panels.push( 385 + <div 386 + class="tab-panel" 387 + role="tabpanel" 388 + data-tab={String(i)} 389 + hidden={i !== 0 ? true : undefined} 390 + > 391 + {content} 392 + </div> 393 + ); 394 + } 395 + 396 + return ( 397 + <org-atsui-tabs> 398 + <div class="tabs-bar" role="tablist"> 399 + {items.map((item, i) => ( 400 + <button 401 + key={item.key} 402 + role="tab" 403 + aria-selected={i === 0 ? "true" : "false"} 404 + onclick={`switchTab(this,${i})`} 405 + > 406 + {item.label} 407 + </button> 408 + ))} 409 + </div> 410 + {panels} 411 + </org-atsui-tabs> 412 + ); 413 + } 414 + 415 + // --- List (HTMX infinite scroll) --- 416 + 417 + async function List({ ctx, props }: PrimitiveProps) { 418 + const p = props as { 419 + query: string; 420 + did: string; 421 + input?: Record<string, unknown>; 422 + }; 423 + if (!p.did || !p.query) { 424 + return <div class="error">List: missing did or query</div>; 425 + } 426 + 427 + const serviceUrl = await resolveDidToService(p.did, "#inlay"); 428 + const params = new URLSearchParams(); 429 + if (p.input) { 430 + for (const [k, v] of Object.entries(p.input)) { 431 + if (v != null) params.set(k, String(v)); 432 + } 433 + } 434 + const qs = params.toString(); 435 + const url = `${serviceUrl}/xrpc/${p.query}${qs ? `?${qs}` : ""}`; 436 + 437 + const res = await fetch(url); 438 + if (!res.ok) { 439 + const text = await res.text().catch(() => ""); 440 + return ( 441 + <div class="error"> 442 + List query failed: {res.status} {text} 443 + </div> 444 + ); 445 + } 446 + 447 + const page = (await res.json()) as { items: unknown[]; cursor?: string }; 448 + 449 + const resolved = await Promise.all( 450 + page.items.map((item) => rn(deserializeTree(item), ctx)) 451 + ); 452 + const renderedItems: JSXElement[] = []; 453 + for (let i = 0; i < resolved.length; i++) { 454 + if (i > 0) renderedItems.push(<hr />); 455 + renderedItems.push(resolved[i]); 456 + } 457 + 458 + const importsParam = encodeURIComponent(JSON.stringify(ctx.imports)); 459 + const sentinelUrl = `/htmx/list?query=${encodeURIComponent(p.query)}&did=${encodeURIComponent(p.did)}&cursor=${encodeURIComponent(page.cursor ?? "")}&input=${encodeURIComponent(JSON.stringify(p.input ?? {}))}&imports=${importsParam}`; 460 + 461 + return ( 462 + <> 463 + {renderedItems} 464 + {page.cursor ? ( 465 + <div 466 + hx-get={sentinelUrl} 467 + hx-trigger="intersect once" 468 + hx-swap="outerHTML" 469 + preload="preload:init" 470 + class="list-sentinel" 471 + > 472 + Loading... 473 + </div> 474 + ) : null} 475 + </> 476 + ); 477 + } 478 + 479 + // --- Record (simplified) --- 480 + 481 + function Record({ props }: PrimitiveProps) { 482 + const p = props as { uri?: string }; 483 + if (!p.uri) return <></>; 484 + const href = `/at/${p.uri.replace("at://", "")}`; 485 + return ( 486 + <org-atsui-stack gap="medium" inset> 487 + <org-atsui-caption>Record</org-atsui-caption> 488 + <org-atsui-link> 489 + <a href={href}>{p.uri}</a> 490 + </org-atsui-link> 491 + </org-atsui-stack> 492 + ); 493 + } 494 + 495 + // --- Editor (no-op) --- 496 + 497 + function Editor() { 498 + return <div class="editor-fallback">Editor not available in proto host</div>; 499 + } 500 + 501 + // --- Component map --- 502 + 503 + export const componentMap: Record<string, PrimitiveFn> = { 504 + "org.atsui.Stack": Stack, 505 + "org.atsui.Row": Row, 506 + "org.atsui.Fill": Fill, 507 + "org.atsui.Card": Card, 508 + "org.atsui.Grid": Grid, 509 + "org.atsui.Clip": Clip, 510 + "org.atsui.Cover": Cover, 511 + "org.atsui.Avatar": Avatar, 512 + "org.atsui.Blob": Blob, 513 + "org.atsui.Title": Title, 514 + "org.atsui.Heading": Heading, 515 + "org.atsui.Text": Text, 516 + "org.atsui.Caption": Caption, 517 + "org.atsui.Timestamp": Timestamp, 518 + "org.atsui.Link": Link, 519 + "at.inlay.Fragment": Fragment, 520 + "at.inlay.Maybe": Maybe, 521 + "at.inlay.Placeholder": Placeholder, 522 + "at.inlay.Throw": Throw, 523 + "org.atsui.Tabs": Tabs, 524 + "org.atsui.List": List, 525 + "org.atsui.Record": Record, 526 + "org.atsui.Editor": Editor, 527 + };
+77
proto/src/render.tsx
··· 1 + import { createElement } from "hono/jsx"; 2 + import type { HtmlEscapedString } from "hono/utils/html"; 3 + import { 4 + render, 5 + createContext, 6 + MissingError, 7 + type Resolver, 8 + type RenderContext, 9 + type RenderOptions, 10 + } from "@inlay/render"; 11 + import { isValidElement } from "@inlay/core"; 12 + import { componentMap, setRenderNode } from "./primitives.tsx"; 13 + import "./types.ts"; 14 + 15 + // Hono's JSX.Element type — what all JSX expressions return 16 + type JSXElement = HtmlEscapedString | Promise<HtmlEscapedString>; 17 + 18 + type Element = { type: string; key?: string; props?: Record<string, unknown> }; 19 + 20 + export { createContext }; 21 + export type { RenderContext, JSXElement }; 22 + 23 + export function createRenderOptions(resolver: Resolver): RenderOptions { 24 + return { resolver, validate: false }; 25 + } 26 + 27 + export async function renderNode( 28 + node: unknown, 29 + context: RenderContext, 30 + options: RenderOptions 31 + ): Promise<JSXElement> { 32 + if (node == null) return <></>; 33 + if (Array.isArray(node)) { 34 + const parts = await Promise.all( 35 + node.map((child) => renderNode(child, context, options)) 36 + ); 37 + return <>{parts}</>; 38 + } 39 + if (typeof node === "string") return <>{node}</>; 40 + if (typeof node === "number") return <>{String(node)}</>; 41 + if (typeof node === "boolean") return <></>; 42 + if (isValidElement(node)) { 43 + return renderElement(node as Element, context, options); 44 + } 45 + return <>{String(node)}</>; 46 + } 47 + 48 + async function renderElement( 49 + element: Element, 50 + context: RenderContext, 51 + options: RenderOptions 52 + ): Promise<JSXElement> { 53 + let result; 54 + try { 55 + result = await render(element as any, context, options); 56 + } catch (e) { 57 + if (e instanceof MissingError) throw e; 58 + const msg = e instanceof Error ? e.message : String(e); 59 + return <div class="error">{msg}</div>; 60 + } 61 + 62 + if (isValidElement(result.node)) { 63 + const el = result.node as Element; 64 + const Builtin = componentMap[el.type]; 65 + if (Builtin) { 66 + return Builtin({ ctx: result.context, props: el.props ?? {} }); 67 + } 68 + } 69 + 70 + const tag = element.type.toLowerCase().replaceAll(".", "-"); 71 + const inner = await renderNode(result.node, result.context, options); 72 + return <>{createElement(tag, { style: "display:contents" }, inner)}</>; 73 + } 74 + 75 + export function initRender(options: RenderOptions) { 76 + setRenderNode((node, ctx) => renderNode(node, ctx, options)); 77 + }
+30
proto/src/resolve.ts
··· 1 + const SLINGSHOT = "https://slingshot.microcosm.blue"; 2 + 3 + const didCache = new Map<string, { value: string; expires: number }>(); 4 + const CACHE_TTL = 5 * 60 * 1000; // 5 minutes 5 + 6 + export async function resolveDidToService( 7 + did: string, 8 + serviceId = "#atproto_pds" 9 + ): Promise<string> { 10 + const cacheKey = `${did}:${serviceId}`; 11 + const cached = didCache.get(cacheKey); 12 + if (cached && cached.expires > Date.now()) return cached.value; 13 + 14 + const res = await fetch( 15 + `${SLINGSHOT}/xrpc/com.bad-example.identity.resolveService?did=${encodeURIComponent(did)}&id=${encodeURIComponent(serviceId)}` 16 + ); 17 + if (!res.ok) { 18 + const body = await res.json().catch(() => ({ message: res.statusText })); 19 + throw new Error( 20 + `No ${serviceId} service found for DID: ${did} (${body.message ?? res.status})` 21 + ); 22 + } 23 + 24 + const data = (await res.json()) as { endpoint: string }; 25 + didCache.set(cacheKey, { 26 + value: data.endpoint, 27 + expires: Date.now() + CACHE_TTL, 28 + }); 29 + return data.endpoint; 30 + }
+136
proto/src/resolver.ts
··· 1 + import { AtUri } from "@atproto/syntax"; 2 + import { resolveDidToService } from "./resolve.ts"; 3 + import { cacheGet, cacheSet } from "./cache.ts"; 4 + import type { Resolver } from "@inlay/render"; 5 + 6 + type CacheTag = { 7 + $type: string; 8 + uri?: string; 9 + subject?: string; 10 + from?: string; 11 + }; 12 + 13 + type CachePolicy = { 14 + life?: string; 15 + tags?: CacheTag[]; 16 + }; 17 + 18 + type ComponentResponse = { 19 + node: unknown; 20 + cache?: CachePolicy; 21 + }; 22 + 23 + const SLINGSHOT = "https://slingshot.microcosm.blue"; 24 + 25 + async function fetchRecordFromPds(uri: string): Promise<unknown | null> { 26 + const key = `record:${uri}`; 27 + const hit = await cacheGet(key); 28 + if (hit !== undefined) return hit; 29 + 30 + const parsed = new AtUri(uri); 31 + const res = await fetch( 32 + `${SLINGSHOT}/xrpc/com.atproto.repo.getRecord?repo=${encodeURIComponent(parsed.host)}&collection=${encodeURIComponent(parsed.collection)}&rkey=${encodeURIComponent(parsed.rkey)}` 33 + ); 34 + if (!res.ok) return null; 35 + 36 + const data = await res.json(); 37 + const value = data.value as Record<string, unknown>; 38 + await cacheSet(key, value, { 39 + life: "hours", 40 + tags: [{ $type: "at.inlay.defs#tagRecord", uri }], 41 + }); 42 + return value; 43 + } 44 + 45 + // --- XRPC --- 46 + 47 + async function callXrpc( 48 + serviceUrl: string, 49 + params: { 50 + did: string; 51 + nsid: string; 52 + type?: string; 53 + body?: unknown; 54 + params?: Record<string, string>; 55 + componentUri?: string; 56 + } 57 + ): Promise<unknown> { 58 + const input = (params.body ?? {}) as Record<string, unknown>; 59 + 60 + if (params.type === "query") { 61 + const qs = new URLSearchParams(); 62 + if (params.params) { 63 + for (const [k, v] of Object.entries(params.params)) { 64 + if (v != null) qs.set(k, v); 65 + } 66 + } 67 + const qsStr = qs.toString(); 68 + const url = `${serviceUrl}/xrpc/${params.nsid}${qsStr ? `?${qsStr}` : ""}`; 69 + const res = await fetch(url); 70 + if (!res.ok) { 71 + const text = await res.text().catch(() => ""); 72 + throw new Error( 73 + `XRPC query failed (${params.nsid}): ${res.status} ${text}` 74 + ); 75 + } 76 + return res.json(); 77 + } 78 + 79 + // procedure 80 + const url = `${serviceUrl}/xrpc/${params.nsid}`; 81 + const res = await fetch(url, { 82 + method: "POST", 83 + headers: { "Content-Type": "application/json" }, 84 + body: JSON.stringify(input), 85 + }); 86 + if (!res.ok) { 87 + const text = await res.text().catch(() => ""); 88 + throw new Error( 89 + `XRPC procedure failed (${params.nsid}): ${res.status} ${text}` 90 + ); 91 + } 92 + return res.json(); 93 + } 94 + 95 + export function createResolver(): Resolver { 96 + return { 97 + async fetchRecord(uri) { 98 + return fetchRecordFromPds(uri); 99 + }, 100 + 101 + async xrpc(params) { 102 + let serviceUrl: string; 103 + try { 104 + serviceUrl = await resolveDidToService(params.did, "#inlay"); 105 + } catch { 106 + throw new Error( 107 + `XRPC resolve failed for ${params.nsid} (did=${params.did})` 108 + ); 109 + } 110 + 111 + if (!params.componentUri || params.type === "query") { 112 + return callXrpc(serviceUrl, params); 113 + } 114 + 115 + const key = `xrpc:${JSON.stringify(params)}`; 116 + const hit = await cacheGet(key); 117 + if (hit !== undefined) return hit; 118 + 119 + const value = await callXrpc(serviceUrl, params); 120 + const response = value as ComponentResponse; 121 + 122 + const tags: CacheTag[] = [ 123 + ...(response.cache?.tags ?? []), 124 + { $type: "at.inlay.defs#tagRecord", uri: params.componentUri }, 125 + ]; 126 + const life = response.cache?.life ?? "hours"; 127 + 128 + await cacheSet(key, value, { life, tags }); 129 + return value; 130 + }, 131 + 132 + async resolveLexicon() { 133 + return null; 134 + }, 135 + }; 136 + }
+27
proto/src/types.ts
··· 1 + // Custom element IntrinsicElements declarations for Hono JSX 2 + 3 + declare module "hono/jsx" { 4 + namespace JSX { 5 + interface IntrinsicElements { 6 + "org-atsui-stack": Record<string, unknown>; 7 + "org-atsui-row": Record<string, unknown>; 8 + "org-atsui-fill": Record<string, unknown>; 9 + "org-atsui-grid": Record<string, unknown>; 10 + "org-atsui-clip": Record<string, unknown>; 11 + "org-atsui-cover": Record<string, unknown>; 12 + "org-atsui-avatar": Record<string, unknown>; 13 + "org-atsui-blob": Record<string, unknown>; 14 + "org-atsui-title": Record<string, unknown>; 15 + "org-atsui-heading": Record<string, unknown>; 16 + "org-atsui-text": Record<string, unknown>; 17 + "org-atsui-caption": Record<string, unknown>; 18 + "org-atsui-timestamp": Record<string, unknown>; 19 + "org-atsui-link": Record<string, unknown>; 20 + "org-atsui-card": Record<string, unknown>; 21 + "org-atsui-tabs": Record<string, unknown>; 22 + [key: `${string}-${string}`]: Record<string, unknown>; 23 + } 24 + } 25 + } 26 + 27 + export {};
+21
proto/tsconfig.json
··· 1 + { 2 + "compilerOptions": { 3 + "target": "ES2022", 4 + "module": "esnext", 5 + "moduleResolution": "bundler", 6 + "jsx": "react-jsx", 7 + "jsxImportSource": "hono/jsx", 8 + "strict": true, 9 + "esModuleInterop": true, 10 + "skipLibCheck": true, 11 + "noEmit": true, 12 + "allowImportingTsExtensions": true, 13 + "resolveJsonModule": true, 14 + "isolatedModules": true, 15 + "types": ["node"], 16 + "paths": { 17 + "@atui/db": ["../db/src"] 18 + } 19 + }, 20 + "include": ["src/**/*.ts", "src/**/*.tsx"] 21 + }
+24
tsconfig.json
··· 1 + { 2 + "compilerOptions": { 3 + "target": "ES2022", 4 + "lib": ["dom", "dom.iterable", "esnext"], 5 + "allowJs": true, 6 + "skipLibCheck": true, 7 + "strict": true, 8 + "noEmit": true, 9 + "allowImportingTsExtensions": true, 10 + "esModuleInterop": true, 11 + "module": "esnext", 12 + "moduleResolution": "bundler", 13 + "resolveJsonModule": true, 14 + "isolatedModules": true, 15 + "jsx": "react-jsx", 16 + "incremental": true, 17 + "paths": { 18 + "@inlay/render": ["./packages/@inlay/render/src/index.ts"], 19 + "@/inlay/*": ["./packages/@inlay/core/*"] 20 + } 21 + }, 22 + "include": ["**/*.ts", "**/*.tsx"], 23 + "exclude": ["node_modules"] 24 + }