a tool for shared writing and social publishing

add basic site.standard stuff and normalization function

+2395 -92
+93
appview/index.ts
··· 11 11 PubLeafletComment, 12 12 PubLeafletPollVote, 13 13 PubLeafletPollDefinition, 14 + SiteStandardDocument, 15 + SiteStandardPublication, 16 + SiteStandardGraphSubscription, 14 17 } from "lexicons/api"; 15 18 import { 16 19 AppBskyEmbedExternal, ··· 47 50 ids.PubLeafletPollDefinition, 48 51 // ids.AppBskyActorProfile, 49 52 "app.bsky.feed.post", 53 + ids.SiteStandardDocument, 54 + ids.SiteStandardPublication, 55 + ids.SiteStandardGraphSubscription, 50 56 ], 51 57 handleEvent, 52 58 onError: (err) => { ··· 221 227 if (evt.event === "delete") { 222 228 await supabase 223 229 .from("publication_subscriptions") 230 + .delete() 231 + .eq("uri", evt.uri.toString()); 232 + } 233 + } 234 + if (evt.collection === ids.SiteStandardDocument) { 235 + if (evt.event === "create" || evt.event === "update") { 236 + let record = SiteStandardDocument.validateRecord(evt.record); 237 + if (!record.success) { 238 + console.log(record.error); 239 + return; 240 + } 241 + await supabase 242 + .from("identities") 243 + .upsert({ atp_did: evt.did }, { onConflict: "atp_did" }); 244 + let docResult = await supabase.from("site_standard_documents").upsert({ 245 + uri: evt.uri.toString(), 246 + data: record.value as Json, 247 + identity_did: evt.did, 248 + }); 249 + if (docResult.error) console.log(docResult.error); 250 + if (record.value.site) { 251 + let siteURI = new AtUri(record.value.site); 252 + 253 + if (siteURI.host !== evt.uri.host) { 254 + console.log("Unauthorized to create document in site!"); 255 + return; 256 + } 257 + let docInPublicationResult = await supabase 258 + .from("site_standard_documents_in_publications") 259 + .upsert({ 260 + publication: record.value.site, 261 + document: evt.uri.toString(), 262 + }); 263 + await supabase 264 + .from("site_standard_documents_in_publications") 265 + .delete() 266 + .neq("publication", record.value.site) 267 + .eq("document", evt.uri.toString()); 268 + 269 + if (docInPublicationResult.error) 270 + console.log(docInPublicationResult.error); 271 + } 272 + } 273 + if (evt.event === "delete") { 274 + await supabase 275 + .from("site_standard_documents") 276 + .delete() 277 + .eq("uri", evt.uri.toString()); 278 + } 279 + } 280 + if (evt.collection === ids.SiteStandardPublication) { 281 + if (evt.event === "create" || evt.event === "update") { 282 + let record = SiteStandardPublication.validateRecord(evt.record); 283 + if (!record.success) return; 284 + await supabase 285 + .from("identities") 286 + .upsert({ atp_did: evt.did }, { onConflict: "atp_did" }); 287 + await supabase.from("site_standard_publications").upsert({ 288 + uri: evt.uri.toString(), 289 + identity_did: evt.did, 290 + data: record.value as Json, 291 + }); 292 + } 293 + if (evt.event === "delete") { 294 + await supabase 295 + .from("site_standard_publications") 296 + .delete() 297 + .eq("uri", evt.uri.toString()); 298 + } 299 + } 300 + if (evt.collection === ids.SiteStandardGraphSubscription) { 301 + if (evt.event === "create" || evt.event === "update") { 302 + let record = SiteStandardGraphSubscription.validateRecord(evt.record); 303 + if (!record.success) return; 304 + await supabase 305 + .from("identities") 306 + .upsert({ atp_did: evt.did }, { onConflict: "atp_did" }); 307 + await supabase.from("site_standard_subscriptions").upsert({ 308 + uri: evt.uri.toString(), 309 + identity: evt.did, 310 + publication: record.value.publication, 311 + record: record.value as Json, 312 + }); 313 + } 314 + if (evt.event === "delete") { 315 + await supabase 316 + .from("site_standard_subscriptions") 224 317 .delete() 225 318 .eq("uri", evt.uri.toString()); 226 319 }
+42 -29
drizzle/relations.ts
··· 1 1 import { relations } from "drizzle-orm/relations"; 2 - import { identities, notifications, publications, documents, comments_on_documents, bsky_profiles, entity_sets, entities, facts, email_auth_tokens, poll_votes_on_entity, permission_tokens, phone_rsvps_to_entity, custom_domains, custom_domain_routes, email_subscriptions_to_entity, atp_poll_records, atp_poll_votes, bsky_follows, subscribers_to_publications, permission_token_on_homepage, documents_in_publications, document_mentions_in_bsky, bsky_posts, publication_domains, leaflets_in_publications, publication_subscriptions, permission_token_rights } from "./schema"; 2 + import { identities, notifications, publications, documents, comments_on_documents, bsky_profiles, entity_sets, entities, facts, email_auth_tokens, poll_votes_on_entity, permission_tokens, phone_rsvps_to_entity, custom_domains, custom_domain_routes, email_subscriptions_to_entity, atp_poll_records, atp_poll_votes, bsky_follows, subscribers_to_publications, documents_in_publications, document_mentions_in_bsky, bsky_posts, permission_token_on_homepage, publication_domains, publication_subscriptions, leaflets_to_documents, permission_token_rights, leaflets_in_publications } from "./schema"; 3 3 4 4 export const notificationsRelations = relations(notifications, ({one}) => ({ 5 5 identity: one(identities, { ··· 43 43 subscribers_to_publications: many(subscribers_to_publications), 44 44 documents_in_publications: many(documents_in_publications), 45 45 publication_domains: many(publication_domains), 46 - leaflets_in_publications: many(leaflets_in_publications), 47 46 publication_subscriptions: many(publication_subscriptions), 47 + leaflets_in_publications: many(leaflets_in_publications), 48 48 })); 49 49 50 50 export const comments_on_documentsRelations = relations(comments_on_documents, ({one}) => ({ ··· 62 62 comments_on_documents: many(comments_on_documents), 63 63 documents_in_publications: many(documents_in_publications), 64 64 document_mentions_in_bskies: many(document_mentions_in_bsky), 65 + leaflets_to_documents: many(leaflets_to_documents), 65 66 leaflets_in_publications: many(leaflets_in_publications), 66 67 })); 67 68 ··· 136 137 }), 137 138 email_subscriptions_to_entities: many(email_subscriptions_to_entity), 138 139 permission_token_on_homepages: many(permission_token_on_homepage), 140 + leaflets_to_documents: many(leaflets_to_documents), 141 + permission_token_rights: many(permission_token_rights), 139 142 leaflets_in_publications: many(leaflets_in_publications), 140 - permission_token_rights: many(permission_token_rights), 141 143 })); 142 144 143 145 export const phone_rsvps_to_entityRelations = relations(phone_rsvps_to_entity, ({one}) => ({ ··· 225 227 }), 226 228 })); 227 229 228 - export const permission_token_on_homepageRelations = relations(permission_token_on_homepage, ({one}) => ({ 229 - identity: one(identities, { 230 - fields: [permission_token_on_homepage.identity], 231 - references: [identities.id] 232 - }), 233 - permission_token: one(permission_tokens, { 234 - fields: [permission_token_on_homepage.token], 235 - references: [permission_tokens.id] 236 - }), 237 - })); 238 - 239 230 export const documents_in_publicationsRelations = relations(documents_in_publications, ({one}) => ({ 240 231 document: one(documents, { 241 232 fields: [documents_in_publications.document], ··· 262 253 document_mentions_in_bskies: many(document_mentions_in_bsky), 263 254 })); 264 255 256 + export const permission_token_on_homepageRelations = relations(permission_token_on_homepage, ({one}) => ({ 257 + identity: one(identities, { 258 + fields: [permission_token_on_homepage.identity], 259 + references: [identities.id] 260 + }), 261 + permission_token: one(permission_tokens, { 262 + fields: [permission_token_on_homepage.token], 263 + references: [permission_tokens.id] 264 + }), 265 + })); 266 + 265 267 export const publication_domainsRelations = relations(publication_domains, ({one}) => ({ 266 268 custom_domain: one(custom_domains, { 267 269 fields: [publication_domains.domain], ··· 277 279 }), 278 280 })); 279 281 280 - export const leaflets_in_publicationsRelations = relations(leaflets_in_publications, ({one}) => ({ 281 - document: one(documents, { 282 - fields: [leaflets_in_publications.doc], 283 - references: [documents.uri] 284 - }), 285 - permission_token: one(permission_tokens, { 286 - fields: [leaflets_in_publications.leaflet], 287 - references: [permission_tokens.id] 288 - }), 289 - publication: one(publications, { 290 - fields: [leaflets_in_publications.publication], 291 - references: [publications.uri] 292 - }), 293 - })); 294 - 295 282 export const publication_subscriptionsRelations = relations(publication_subscriptions, ({one}) => ({ 296 283 identity: one(identities, { 297 284 fields: [publication_subscriptions.identity], ··· 303 290 }), 304 291 })); 305 292 293 + export const leaflets_to_documentsRelations = relations(leaflets_to_documents, ({one}) => ({ 294 + document: one(documents, { 295 + fields: [leaflets_to_documents.document], 296 + references: [documents.uri] 297 + }), 298 + permission_token: one(permission_tokens, { 299 + fields: [leaflets_to_documents.leaflet], 300 + references: [permission_tokens.id] 301 + }), 302 + })); 303 + 306 304 export const permission_token_rightsRelations = relations(permission_token_rights, ({one}) => ({ 307 305 entity_set: one(entity_sets, { 308 306 fields: [permission_token_rights.entity_set], ··· 311 309 permission_token: one(permission_tokens, { 312 310 fields: [permission_token_rights.token], 313 311 references: [permission_tokens.id] 312 + }), 313 + })); 314 + 315 + export const leaflets_in_publicationsRelations = relations(leaflets_in_publications, ({one}) => ({ 316 + document: one(documents, { 317 + fields: [leaflets_in_publications.doc], 318 + references: [documents.uri] 319 + }), 320 + permission_token: one(permission_tokens, { 321 + fields: [leaflets_in_publications.leaflet], 322 + references: [permission_tokens.id] 323 + }), 324 + publication: one(publications, { 325 + fields: [leaflets_in_publications.publication], 326 + references: [publications.uri] 314 327 }), 315 328 }));
+45 -26
drizzle/schema.ts
··· 136 136 export const identities = pgTable("identities", { 137 137 id: uuid("id").defaultRandom().primaryKey().notNull(), 138 138 created_at: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), 139 - home_page: uuid("home_page").notNull().references(() => permission_tokens.id, { onDelete: "cascade" } ), 139 + home_page: uuid("home_page").default(sql`create_identity_homepage()`).notNull().references(() => permission_tokens.id, { onDelete: "cascade" } ), 140 140 email: text("email"), 141 141 atp_did: text("atp_did"), 142 142 interface_state: jsonb("interface_state"), ··· 260 260 } 261 261 }); 262 262 263 - export const permission_token_on_homepage = pgTable("permission_token_on_homepage", { 264 - token: uuid("token").notNull().references(() => permission_tokens.id, { onDelete: "cascade" } ), 265 - identity: uuid("identity").notNull().references(() => identities.id, { onDelete: "cascade" } ), 266 - created_at: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), 267 - }, 268 - (table) => { 269 - return { 270 - permission_token_creator_pkey: primaryKey({ columns: [table.token, table.identity], name: "permission_token_creator_pkey"}), 271 - } 272 - }); 273 - 274 263 export const documents_in_publications = pgTable("documents_in_publications", { 275 264 publication: text("publication").notNull().references(() => publications.uri, { onDelete: "cascade" } ), 276 265 document: text("document").notNull().references(() => documents.uri, { onDelete: "cascade" } ), ··· 295 284 } 296 285 }); 297 286 298 - export const publication_domains = pgTable("publication_domains", { 299 - publication: text("publication").notNull().references(() => publications.uri, { onDelete: "cascade" } ), 300 - domain: text("domain").notNull().references(() => custom_domains.domain, { onDelete: "cascade" } ), 287 + export const permission_token_on_homepage = pgTable("permission_token_on_homepage", { 288 + token: uuid("token").notNull().references(() => permission_tokens.id, { onDelete: "cascade", onUpdate: "cascade" } ), 289 + identity: uuid("identity").notNull().references(() => identities.id, { onDelete: "cascade" } ), 301 290 created_at: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), 302 - identity: text("identity").notNull().references(() => identities.atp_did, { onDelete: "cascade", onUpdate: "cascade" } ), 291 + archived: boolean("archived"), 303 292 }, 304 293 (table) => { 305 294 return { 306 - publication_idx: index("publication_domains_publication_idx").on(table.publication), 307 - publication_domains_pkey: primaryKey({ columns: [table.publication, table.domain], name: "publication_domains_pkey"}), 295 + permission_token_creator_pkey: primaryKey({ columns: [table.token, table.identity], name: "permission_token_creator_pkey"}), 308 296 } 309 297 }); 310 298 311 - export const leaflets_in_publications = pgTable("leaflets_in_publications", { 299 + export const publication_domains = pgTable("publication_domains", { 312 300 publication: text("publication").notNull().references(() => publications.uri, { onDelete: "cascade" } ), 313 - doc: text("doc").default('').references(() => documents.uri, { onDelete: "set null" } ), 314 - leaflet: uuid("leaflet").notNull().references(() => permission_tokens.id, { onDelete: "cascade" } ), 315 - description: text("description").default('').notNull(), 316 - title: text("title").default('').notNull(), 301 + domain: text("domain").notNull().references(() => custom_domains.domain, { onDelete: "cascade" } ), 302 + created_at: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), 303 + identity: text("identity").notNull().references(() => identities.atp_did, { onDelete: "cascade", onUpdate: "cascade" } ), 317 304 }, 318 305 (table) => { 319 306 return { 320 - leaflet_idx: index("leaflets_in_publications_leaflet_idx").on(table.leaflet), 321 - publication_idx: index("leaflets_in_publications_publication_idx").on(table.publication), 322 - leaflets_in_publications_pkey: primaryKey({ columns: [table.publication, table.leaflet], name: "leaflets_in_publications_pkey"}), 307 + publication_idx: index("publication_domains_publication_idx").on(table.publication), 308 + publication_domains_pkey: primaryKey({ columns: [table.publication, table.domain], name: "publication_domains_pkey"}), 323 309 } 324 310 }); 325 311 ··· 338 324 } 339 325 }); 340 326 327 + export const leaflets_to_documents = pgTable("leaflets_to_documents", { 328 + leaflet: uuid("leaflet").notNull().references(() => permission_tokens.id, { onDelete: "cascade", onUpdate: "cascade" } ), 329 + document: text("document").notNull().references(() => documents.uri, { onDelete: "cascade", onUpdate: "cascade" } ), 330 + created_at: timestamp("created_at", { withTimezone: true, mode: 'string' }).defaultNow().notNull(), 331 + title: text("title").default('').notNull(), 332 + description: text("description").default('').notNull(), 333 + tags: text("tags").default('RRAY[').array(), 334 + cover_image: text("cover_image"), 335 + }, 336 + (table) => { 337 + return { 338 + leaflets_to_documents_pkey: primaryKey({ columns: [table.leaflet, table.document], name: "leaflets_to_documents_pkey"}), 339 + } 340 + }); 341 + 341 342 export const permission_token_rights = pgTable("permission_token_rights", { 342 343 token: uuid("token").notNull().references(() => permission_tokens.id, { onDelete: "cascade", onUpdate: "cascade" } ), 343 344 entity_set: uuid("entity_set").notNull().references(() => entity_sets.id, { onDelete: "cascade", onUpdate: "cascade" } ), ··· 352 353 token_idx: index("permission_token_rights_token_idx").on(table.token), 353 354 entity_set_idx: index("permission_token_rights_entity_set_idx").on(table.entity_set), 354 355 permission_token_rights_pkey: primaryKey({ columns: [table.token, table.entity_set], name: "permission_token_rights_pkey"}), 356 + } 357 + }); 358 + 359 + export const leaflets_in_publications = pgTable("leaflets_in_publications", { 360 + publication: text("publication").notNull().references(() => publications.uri, { onDelete: "cascade" } ), 361 + doc: text("doc").default('').references(() => documents.uri, { onDelete: "set null" } ), 362 + leaflet: uuid("leaflet").notNull().references(() => permission_tokens.id, { onDelete: "cascade", onUpdate: "cascade" } ), 363 + description: text("description").default('').notNull(), 364 + title: text("title").default('').notNull(), 365 + archived: boolean("archived"), 366 + tags: text("tags").default('RRAY[').array(), 367 + cover_image: text("cover_image"), 368 + }, 369 + (table) => { 370 + return { 371 + leaflet_idx: index("leaflets_in_publications_leaflet_idx").on(table.leaflet), 372 + publication_idx: index("leaflets_in_publications_publication_idx").on(table.publication), 373 + leaflets_in_publications_pkey: primaryKey({ columns: [table.publication, table.leaflet], name: "leaflets_in_publications_pkey"}), 355 374 } 356 375 });
+303
lexicons/api/index.ts
··· 38 38 import * as PubLeafletBlocksUnorderedList from './types/pub/leaflet/blocks/unorderedList' 39 39 import * as PubLeafletBlocksWebsite from './types/pub/leaflet/blocks/website' 40 40 import * as PubLeafletComment from './types/pub/leaflet/comment' 41 + import * as PubLeafletContent from './types/pub/leaflet/content' 41 42 import * as PubLeafletDocument from './types/pub/leaflet/document' 42 43 import * as PubLeafletGraphSubscription from './types/pub/leaflet/graph/subscription' 43 44 import * as PubLeafletPagesCanvas from './types/pub/leaflet/pages/canvas' ··· 48 49 import * as PubLeafletRichtextFacet from './types/pub/leaflet/richtext/facet' 49 50 import * as PubLeafletThemeBackgroundImage from './types/pub/leaflet/theme/backgroundImage' 50 51 import * as PubLeafletThemeColor from './types/pub/leaflet/theme/color' 52 + import * as SiteStandardDocument from './types/site/standard/document' 53 + import * as SiteStandardGraphSubscription from './types/site/standard/graph/subscription' 54 + import * as SiteStandardPublication from './types/site/standard/publication' 55 + import * as SiteStandardThemeBasic from './types/site/standard/theme/basic' 56 + import * as SiteStandardThemeColor from './types/site/standard/theme/color' 51 57 52 58 export * as AppBskyActorProfile from './types/app/bsky/actor/profile' 53 59 export * as ComAtprotoLabelDefs from './types/com/atproto/label/defs' ··· 78 84 export * as PubLeafletBlocksUnorderedList from './types/pub/leaflet/blocks/unorderedList' 79 85 export * as PubLeafletBlocksWebsite from './types/pub/leaflet/blocks/website' 80 86 export * as PubLeafletComment from './types/pub/leaflet/comment' 87 + export * as PubLeafletContent from './types/pub/leaflet/content' 81 88 export * as PubLeafletDocument from './types/pub/leaflet/document' 82 89 export * as PubLeafletGraphSubscription from './types/pub/leaflet/graph/subscription' 83 90 export * as PubLeafletPagesCanvas from './types/pub/leaflet/pages/canvas' ··· 88 95 export * as PubLeafletRichtextFacet from './types/pub/leaflet/richtext/facet' 89 96 export * as PubLeafletThemeBackgroundImage from './types/pub/leaflet/theme/backgroundImage' 90 97 export * as PubLeafletThemeColor from './types/pub/leaflet/theme/color' 98 + export * as SiteStandardDocument from './types/site/standard/document' 99 + export * as SiteStandardGraphSubscription from './types/site/standard/graph/subscription' 100 + export * as SiteStandardPublication from './types/site/standard/publication' 101 + export * as SiteStandardThemeBasic from './types/site/standard/theme/basic' 102 + export * as SiteStandardThemeColor from './types/site/standard/theme/color' 91 103 92 104 export const PUB_LEAFLET_PAGES = { 93 105 CanvasTextAlignLeft: 'pub.leaflet.pages.canvas#textAlignLeft', ··· 106 118 app: AppNS 107 119 com: ComNS 108 120 pub: PubNS 121 + site: SiteNS 109 122 110 123 constructor(options: FetchHandler | FetchHandlerOptions) { 111 124 super(options, schemas) 112 125 this.app = new AppNS(this) 113 126 this.com = new ComNS(this) 114 127 this.pub = new PubNS(this) 128 + this.site = new SiteNS(this) 115 129 } 116 130 117 131 /** @deprecated use `this` instead */ ··· 952 966 ) 953 967 } 954 968 } 969 + 970 + export class SiteNS { 971 + _client: XrpcClient 972 + standard: SiteStandardNS 973 + 974 + constructor(client: XrpcClient) { 975 + this._client = client 976 + this.standard = new SiteStandardNS(client) 977 + } 978 + } 979 + 980 + export class SiteStandardNS { 981 + _client: XrpcClient 982 + document: SiteStandardDocumentRecord 983 + publication: SiteStandardPublicationRecord 984 + graph: SiteStandardGraphNS 985 + theme: SiteStandardThemeNS 986 + 987 + constructor(client: XrpcClient) { 988 + this._client = client 989 + this.graph = new SiteStandardGraphNS(client) 990 + this.theme = new SiteStandardThemeNS(client) 991 + this.document = new SiteStandardDocumentRecord(client) 992 + this.publication = new SiteStandardPublicationRecord(client) 993 + } 994 + } 995 + 996 + export class SiteStandardGraphNS { 997 + _client: XrpcClient 998 + subscription: SiteStandardGraphSubscriptionRecord 999 + 1000 + constructor(client: XrpcClient) { 1001 + this._client = client 1002 + this.subscription = new SiteStandardGraphSubscriptionRecord(client) 1003 + } 1004 + } 1005 + 1006 + export class SiteStandardGraphSubscriptionRecord { 1007 + _client: XrpcClient 1008 + 1009 + constructor(client: XrpcClient) { 1010 + this._client = client 1011 + } 1012 + 1013 + async list( 1014 + params: OmitKey<ComAtprotoRepoListRecords.QueryParams, 'collection'>, 1015 + ): Promise<{ 1016 + cursor?: string 1017 + records: { uri: string; value: SiteStandardGraphSubscription.Record }[] 1018 + }> { 1019 + const res = await this._client.call('com.atproto.repo.listRecords', { 1020 + collection: 'site.standard.graph.subscription', 1021 + ...params, 1022 + }) 1023 + return res.data 1024 + } 1025 + 1026 + async get( 1027 + params: OmitKey<ComAtprotoRepoGetRecord.QueryParams, 'collection'>, 1028 + ): Promise<{ 1029 + uri: string 1030 + cid: string 1031 + value: SiteStandardGraphSubscription.Record 1032 + }> { 1033 + const res = await this._client.call('com.atproto.repo.getRecord', { 1034 + collection: 'site.standard.graph.subscription', 1035 + ...params, 1036 + }) 1037 + return res.data 1038 + } 1039 + 1040 + async create( 1041 + params: OmitKey< 1042 + ComAtprotoRepoCreateRecord.InputSchema, 1043 + 'collection' | 'record' 1044 + >, 1045 + record: Un$Typed<SiteStandardGraphSubscription.Record>, 1046 + headers?: Record<string, string>, 1047 + ): Promise<{ uri: string; cid: string }> { 1048 + const collection = 'site.standard.graph.subscription' 1049 + const res = await this._client.call( 1050 + 'com.atproto.repo.createRecord', 1051 + undefined, 1052 + { collection, ...params, record: { ...record, $type: collection } }, 1053 + { encoding: 'application/json', headers }, 1054 + ) 1055 + return res.data 1056 + } 1057 + 1058 + async put( 1059 + params: OmitKey< 1060 + ComAtprotoRepoPutRecord.InputSchema, 1061 + 'collection' | 'record' 1062 + >, 1063 + record: Un$Typed<SiteStandardGraphSubscription.Record>, 1064 + headers?: Record<string, string>, 1065 + ): Promise<{ uri: string; cid: string }> { 1066 + const collection = 'site.standard.graph.subscription' 1067 + const res = await this._client.call( 1068 + 'com.atproto.repo.putRecord', 1069 + undefined, 1070 + { collection, ...params, record: { ...record, $type: collection } }, 1071 + { encoding: 'application/json', headers }, 1072 + ) 1073 + return res.data 1074 + } 1075 + 1076 + async delete( 1077 + params: OmitKey<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, 1078 + headers?: Record<string, string>, 1079 + ): Promise<void> { 1080 + await this._client.call( 1081 + 'com.atproto.repo.deleteRecord', 1082 + undefined, 1083 + { collection: 'site.standard.graph.subscription', ...params }, 1084 + { headers }, 1085 + ) 1086 + } 1087 + } 1088 + 1089 + export class SiteStandardThemeNS { 1090 + _client: XrpcClient 1091 + 1092 + constructor(client: XrpcClient) { 1093 + this._client = client 1094 + } 1095 + } 1096 + 1097 + export class SiteStandardDocumentRecord { 1098 + _client: XrpcClient 1099 + 1100 + constructor(client: XrpcClient) { 1101 + this._client = client 1102 + } 1103 + 1104 + async list( 1105 + params: OmitKey<ComAtprotoRepoListRecords.QueryParams, 'collection'>, 1106 + ): Promise<{ 1107 + cursor?: string 1108 + records: { uri: string; value: SiteStandardDocument.Record }[] 1109 + }> { 1110 + const res = await this._client.call('com.atproto.repo.listRecords', { 1111 + collection: 'site.standard.document', 1112 + ...params, 1113 + }) 1114 + return res.data 1115 + } 1116 + 1117 + async get( 1118 + params: OmitKey<ComAtprotoRepoGetRecord.QueryParams, 'collection'>, 1119 + ): Promise<{ uri: string; cid: string; value: SiteStandardDocument.Record }> { 1120 + const res = await this._client.call('com.atproto.repo.getRecord', { 1121 + collection: 'site.standard.document', 1122 + ...params, 1123 + }) 1124 + return res.data 1125 + } 1126 + 1127 + async create( 1128 + params: OmitKey< 1129 + ComAtprotoRepoCreateRecord.InputSchema, 1130 + 'collection' | 'record' 1131 + >, 1132 + record: Un$Typed<SiteStandardDocument.Record>, 1133 + headers?: Record<string, string>, 1134 + ): Promise<{ uri: string; cid: string }> { 1135 + const collection = 'site.standard.document' 1136 + const res = await this._client.call( 1137 + 'com.atproto.repo.createRecord', 1138 + undefined, 1139 + { collection, ...params, record: { ...record, $type: collection } }, 1140 + { encoding: 'application/json', headers }, 1141 + ) 1142 + return res.data 1143 + } 1144 + 1145 + async put( 1146 + params: OmitKey< 1147 + ComAtprotoRepoPutRecord.InputSchema, 1148 + 'collection' | 'record' 1149 + >, 1150 + record: Un$Typed<SiteStandardDocument.Record>, 1151 + headers?: Record<string, string>, 1152 + ): Promise<{ uri: string; cid: string }> { 1153 + const collection = 'site.standard.document' 1154 + const res = await this._client.call( 1155 + 'com.atproto.repo.putRecord', 1156 + undefined, 1157 + { collection, ...params, record: { ...record, $type: collection } }, 1158 + { encoding: 'application/json', headers }, 1159 + ) 1160 + return res.data 1161 + } 1162 + 1163 + async delete( 1164 + params: OmitKey<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, 1165 + headers?: Record<string, string>, 1166 + ): Promise<void> { 1167 + await this._client.call( 1168 + 'com.atproto.repo.deleteRecord', 1169 + undefined, 1170 + { collection: 'site.standard.document', ...params }, 1171 + { headers }, 1172 + ) 1173 + } 1174 + } 1175 + 1176 + export class SiteStandardPublicationRecord { 1177 + _client: XrpcClient 1178 + 1179 + constructor(client: XrpcClient) { 1180 + this._client = client 1181 + } 1182 + 1183 + async list( 1184 + params: OmitKey<ComAtprotoRepoListRecords.QueryParams, 'collection'>, 1185 + ): Promise<{ 1186 + cursor?: string 1187 + records: { uri: string; value: SiteStandardPublication.Record }[] 1188 + }> { 1189 + const res = await this._client.call('com.atproto.repo.listRecords', { 1190 + collection: 'site.standard.publication', 1191 + ...params, 1192 + }) 1193 + return res.data 1194 + } 1195 + 1196 + async get( 1197 + params: OmitKey<ComAtprotoRepoGetRecord.QueryParams, 'collection'>, 1198 + ): Promise<{ 1199 + uri: string 1200 + cid: string 1201 + value: SiteStandardPublication.Record 1202 + }> { 1203 + const res = await this._client.call('com.atproto.repo.getRecord', { 1204 + collection: 'site.standard.publication', 1205 + ...params, 1206 + }) 1207 + return res.data 1208 + } 1209 + 1210 + async create( 1211 + params: OmitKey< 1212 + ComAtprotoRepoCreateRecord.InputSchema, 1213 + 'collection' | 'record' 1214 + >, 1215 + record: Un$Typed<SiteStandardPublication.Record>, 1216 + headers?: Record<string, string>, 1217 + ): Promise<{ uri: string; cid: string }> { 1218 + const collection = 'site.standard.publication' 1219 + const res = await this._client.call( 1220 + 'com.atproto.repo.createRecord', 1221 + undefined, 1222 + { collection, ...params, record: { ...record, $type: collection } }, 1223 + { encoding: 'application/json', headers }, 1224 + ) 1225 + return res.data 1226 + } 1227 + 1228 + async put( 1229 + params: OmitKey< 1230 + ComAtprotoRepoPutRecord.InputSchema, 1231 + 'collection' | 'record' 1232 + >, 1233 + record: Un$Typed<SiteStandardPublication.Record>, 1234 + headers?: Record<string, string>, 1235 + ): Promise<{ uri: string; cid: string }> { 1236 + const collection = 'site.standard.publication' 1237 + const res = await this._client.call( 1238 + 'com.atproto.repo.putRecord', 1239 + undefined, 1240 + { collection, ...params, record: { ...record, $type: collection } }, 1241 + { encoding: 'application/json', headers }, 1242 + ) 1243 + return res.data 1244 + } 1245 + 1246 + async delete( 1247 + params: OmitKey<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>, 1248 + headers?: Record<string, string>, 1249 + ): Promise<void> { 1250 + await this._client.call( 1251 + 'com.atproto.repo.deleteRecord', 1252 + undefined, 1253 + { collection: 'site.standard.publication', ...params }, 1254 + { headers }, 1255 + ) 1256 + } 1257 + }
+271
lexicons/api/lexicons.ts
··· 1400 1400 }, 1401 1401 }, 1402 1402 }, 1403 + PubLeafletContent: { 1404 + lexicon: 1, 1405 + id: 'pub.leaflet.content', 1406 + revision: 1, 1407 + description: 'A lexicon for long form rich media documents', 1408 + defs: { 1409 + main: { 1410 + type: 'object', 1411 + description: 'Content format for leaflet documents', 1412 + required: ['pages'], 1413 + properties: { 1414 + pages: { 1415 + type: 'array', 1416 + items: { 1417 + type: 'union', 1418 + refs: [ 1419 + 'lex:pub.leaflet.pages.linearDocument', 1420 + 'lex:pub.leaflet.pages.canvas', 1421 + ], 1422 + }, 1423 + }, 1424 + }, 1425 + }, 1426 + }, 1427 + }, 1403 1428 PubLeafletDocument: { 1404 1429 lexicon: 1, 1405 1430 id: 'pub.leaflet.document', ··· 2082 2107 }, 2083 2108 }, 2084 2109 }, 2110 + SiteStandardDocument: { 2111 + defs: { 2112 + main: { 2113 + key: 'tid', 2114 + record: { 2115 + properties: { 2116 + bskyPostRef: { 2117 + ref: 'lex:com.atproto.repo.strongRef', 2118 + type: 'ref', 2119 + }, 2120 + content: { 2121 + closed: false, 2122 + refs: ['lex:pub.leaflet.content'], 2123 + type: 'union', 2124 + }, 2125 + coverImage: { 2126 + accept: ['image/*'], 2127 + maxSize: 1000000, 2128 + type: 'blob', 2129 + }, 2130 + description: { 2131 + maxGraphemes: 300, 2132 + maxLength: 3000, 2133 + type: 'string', 2134 + }, 2135 + path: { 2136 + description: 2137 + 'combine with the publication url or the document site to construct a full url to the document', 2138 + type: 'string', 2139 + }, 2140 + publishedAt: { 2141 + format: 'datetime', 2142 + type: 'string', 2143 + }, 2144 + site: { 2145 + description: 2146 + 'URI to the site or publication this document belongs to (https or at-uri)', 2147 + format: 'uri', 2148 + type: 'string', 2149 + }, 2150 + tags: { 2151 + items: { 2152 + maxGraphemes: 50, 2153 + maxLength: 100, 2154 + type: 'string', 2155 + }, 2156 + type: 'array', 2157 + }, 2158 + textContent: { 2159 + type: 'string', 2160 + }, 2161 + title: { 2162 + maxGraphemes: 128, 2163 + maxLength: 1280, 2164 + type: 'string', 2165 + }, 2166 + updatedAt: { 2167 + format: 'datetime', 2168 + type: 'string', 2169 + }, 2170 + }, 2171 + required: ['site', 'title', 'publishedAt'], 2172 + type: 'object', 2173 + }, 2174 + type: 'record', 2175 + }, 2176 + }, 2177 + id: 'site.standard.document', 2178 + lexicon: 1, 2179 + }, 2180 + SiteStandardGraphSubscription: { 2181 + defs: { 2182 + main: { 2183 + description: 'Record declaring a subscription to a publication', 2184 + key: 'tid', 2185 + record: { 2186 + properties: { 2187 + publication: { 2188 + format: 'at-uri', 2189 + type: 'string', 2190 + }, 2191 + }, 2192 + required: ['publication'], 2193 + type: 'object', 2194 + }, 2195 + type: 'record', 2196 + }, 2197 + }, 2198 + id: 'site.standard.graph.subscription', 2199 + lexicon: 1, 2200 + }, 2201 + SiteStandardPublication: { 2202 + defs: { 2203 + main: { 2204 + key: 'tid', 2205 + record: { 2206 + properties: { 2207 + basicTheme: { 2208 + ref: 'lex:site.standard.theme.basic', 2209 + type: 'ref', 2210 + }, 2211 + theme: { 2212 + type: 'ref', 2213 + ref: 'lex:pub.leaflet.publication#theme', 2214 + }, 2215 + description: { 2216 + maxGraphemes: 300, 2217 + maxLength: 3000, 2218 + type: 'string', 2219 + }, 2220 + icon: { 2221 + accept: ['image/*'], 2222 + maxSize: 1000000, 2223 + type: 'blob', 2224 + }, 2225 + name: { 2226 + maxGraphemes: 128, 2227 + maxLength: 1280, 2228 + type: 'string', 2229 + }, 2230 + preferences: { 2231 + ref: 'lex:site.standard.publication#preferences', 2232 + type: 'ref', 2233 + }, 2234 + url: { 2235 + format: 'uri', 2236 + type: 'string', 2237 + }, 2238 + }, 2239 + required: ['url', 'name'], 2240 + type: 'object', 2241 + }, 2242 + type: 'record', 2243 + }, 2244 + preferences: { 2245 + properties: { 2246 + showInDiscover: { 2247 + default: true, 2248 + type: 'boolean', 2249 + }, 2250 + showComments: { 2251 + default: true, 2252 + type: 'boolean', 2253 + }, 2254 + showMentions: { 2255 + default: true, 2256 + type: 'boolean', 2257 + }, 2258 + showPrevNext: { 2259 + default: false, 2260 + type: 'boolean', 2261 + }, 2262 + }, 2263 + type: 'object', 2264 + }, 2265 + }, 2266 + id: 'site.standard.publication', 2267 + lexicon: 1, 2268 + }, 2269 + SiteStandardThemeBasic: { 2270 + defs: { 2271 + main: { 2272 + properties: { 2273 + accent: { 2274 + refs: ['lex:site.standard.theme.color#rgb'], 2275 + type: 'union', 2276 + }, 2277 + accentForeground: { 2278 + refs: ['lex:site.standard.theme.color#rgb'], 2279 + type: 'union', 2280 + }, 2281 + background: { 2282 + refs: ['lex:site.standard.theme.color#rgb'], 2283 + type: 'union', 2284 + }, 2285 + foreground: { 2286 + refs: ['lex:site.standard.theme.color#rgb'], 2287 + type: 'union', 2288 + }, 2289 + }, 2290 + required: ['background', 'foreground', 'accent', 'accentForeground'], 2291 + type: 'object', 2292 + }, 2293 + }, 2294 + id: 'site.standard.theme.basic', 2295 + lexicon: 1, 2296 + }, 2297 + SiteStandardThemeColor: { 2298 + lexicon: 1, 2299 + id: 'site.standard.theme.color', 2300 + defs: { 2301 + rgb: { 2302 + type: 'object', 2303 + required: ['r', 'g', 'b'], 2304 + properties: { 2305 + r: { 2306 + type: 'integer', 2307 + minimum: 0, 2308 + maximum: 255, 2309 + }, 2310 + g: { 2311 + type: 'integer', 2312 + minimum: 0, 2313 + maximum: 255, 2314 + }, 2315 + b: { 2316 + type: 'integer', 2317 + minimum: 0, 2318 + maximum: 255, 2319 + }, 2320 + }, 2321 + }, 2322 + rgba: { 2323 + type: 'object', 2324 + required: ['r', 'g', 'b', 'a'], 2325 + properties: { 2326 + r: { 2327 + type: 'integer', 2328 + minimum: 0, 2329 + maximum: 255, 2330 + }, 2331 + g: { 2332 + type: 'integer', 2333 + minimum: 0, 2334 + maximum: 255, 2335 + }, 2336 + b: { 2337 + type: 'integer', 2338 + minimum: 0, 2339 + maximum: 255, 2340 + }, 2341 + a: { 2342 + type: 'integer', 2343 + minimum: 0, 2344 + maximum: 100, 2345 + }, 2346 + }, 2347 + }, 2348 + }, 2349 + }, 2085 2350 } as const satisfies Record<string, LexiconDoc> 2086 2351 export const schemas = Object.values(schemaDict) satisfies LexiconDoc[] 2087 2352 export const lexicons: Lexicons = new Lexicons(schemas) ··· 2144 2409 PubLeafletBlocksUnorderedList: 'pub.leaflet.blocks.unorderedList', 2145 2410 PubLeafletBlocksWebsite: 'pub.leaflet.blocks.website', 2146 2411 PubLeafletComment: 'pub.leaflet.comment', 2412 + PubLeafletContent: 'pub.leaflet.content', 2147 2413 PubLeafletDocument: 'pub.leaflet.document', 2148 2414 PubLeafletGraphSubscription: 'pub.leaflet.graph.subscription', 2149 2415 PubLeafletPagesCanvas: 'pub.leaflet.pages.canvas', ··· 2154 2420 PubLeafletRichtextFacet: 'pub.leaflet.richtext.facet', 2155 2421 PubLeafletThemeBackgroundImage: 'pub.leaflet.theme.backgroundImage', 2156 2422 PubLeafletThemeColor: 'pub.leaflet.theme.color', 2423 + SiteStandardDocument: 'site.standard.document', 2424 + SiteStandardGraphSubscription: 'site.standard.graph.subscription', 2425 + SiteStandardPublication: 'site.standard.publication', 2426 + SiteStandardThemeBasic: 'site.standard.theme.basic', 2427 + SiteStandardThemeColor: 'site.standard.theme.color', 2157 2428 } as const
+33
lexicons/api/types/pub/leaflet/content.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + import { validate as _validate } from '../../../lexicons' 7 + import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 + import type * as PubLeafletPagesLinearDocument from './pages/linearDocument' 9 + import type * as PubLeafletPagesCanvas from './pages/canvas' 10 + 11 + const is$typed = _is$typed, 12 + validate = _validate 13 + const id = 'pub.leaflet.content' 14 + 15 + /** Content format for leaflet documents */ 16 + export interface Main { 17 + $type?: 'pub.leaflet.content' 18 + pages: ( 19 + | $Typed<PubLeafletPagesLinearDocument.Main> 20 + | $Typed<PubLeafletPagesCanvas.Main> 21 + | { $type: string } 22 + )[] 23 + } 24 + 25 + const hashMain = 'main' 26 + 27 + export function isMain<V>(v: V) { 28 + return is$typed(v, id, hashMain) 29 + } 30 + 31 + export function validateMain<V>(v: V) { 32 + return validate<Main & V>(v, id, hashMain) 33 + }
+41
lexicons/api/types/site/standard/document.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + import { validate as _validate } from '../../../lexicons' 7 + import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 + import type * as ComAtprotoRepoStrongRef from '../../com/atproto/repo/strongRef' 9 + import type * as PubLeafletContent from '../../pub/leaflet/content' 10 + 11 + const is$typed = _is$typed, 12 + validate = _validate 13 + const id = 'site.standard.document' 14 + 15 + export interface Record { 16 + $type: 'site.standard.document' 17 + bskyPostRef?: ComAtprotoRepoStrongRef.Main 18 + content?: $Typed<PubLeafletContent.Main> | { $type: string } 19 + coverImage?: BlobRef 20 + description?: string 21 + /** combine with the publication url or the document site to construct a full url to the document */ 22 + path?: string 23 + publishedAt: string 24 + /** URI to the site or publication this document belongs to (https or at-uri) */ 25 + site: string 26 + tags?: string[] 27 + textContent?: string 28 + title: string 29 + updatedAt?: string 30 + [k: string]: unknown 31 + } 32 + 33 + const hashRecord = 'main' 34 + 35 + export function isRecord<V>(v: V) { 36 + return is$typed(v, id, hashRecord) 37 + } 38 + 39 + export function validateRecord<V>(v: V) { 40 + return validate<Record & V>(v, id, hashRecord, true) 41 + }
+31
lexicons/api/types/site/standard/graph/subscription.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 + 13 + const is$typed = _is$typed, 14 + validate = _validate 15 + const id = 'site.standard.graph.subscription' 16 + 17 + export interface Record { 18 + $type: 'site.standard.graph.subscription' 19 + publication: string 20 + [k: string]: unknown 21 + } 22 + 23 + const hashRecord = 'main' 24 + 25 + export function isRecord<V>(v: V) { 26 + return is$typed(v, id, hashRecord) 27 + } 28 + 29 + export function validateRecord<V>(v: V) { 30 + return validate<Record & V>(v, id, hashRecord, true) 31 + }
+53
lexicons/api/types/site/standard/publication.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + import { validate as _validate } from '../../../lexicons' 7 + import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' 8 + import type * as SiteStandardThemeBasic from './theme/basic' 9 + import type * as PubLeafletPublication from '../../pub/leaflet/publication' 10 + 11 + const is$typed = _is$typed, 12 + validate = _validate 13 + const id = 'site.standard.publication' 14 + 15 + export interface Record { 16 + $type: 'site.standard.publication' 17 + basicTheme?: SiteStandardThemeBasic.Main 18 + theme?: PubLeafletPublication.Theme 19 + description?: string 20 + icon?: BlobRef 21 + name: string 22 + preferences?: Preferences 23 + url: string 24 + [k: string]: unknown 25 + } 26 + 27 + const hashRecord = 'main' 28 + 29 + export function isRecord<V>(v: V) { 30 + return is$typed(v, id, hashRecord) 31 + } 32 + 33 + export function validateRecord<V>(v: V) { 34 + return validate<Record & V>(v, id, hashRecord, true) 35 + } 36 + 37 + export interface Preferences { 38 + $type?: 'site.standard.publication#preferences' 39 + showInDiscover: boolean 40 + showComments: boolean 41 + showMentions: boolean 42 + showPrevNext: boolean 43 + } 44 + 45 + const hashPreferences = 'preferences' 46 + 47 + export function isPreferences<V>(v: V) { 48 + return is$typed(v, id, hashPreferences) 49 + } 50 + 51 + export function validatePreferences<V>(v: V) { 52 + return validate<Preferences & V>(v, id, hashPreferences) 53 + }
+34
lexicons/api/types/site/standard/theme/basic.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 + import type * as SiteStandardThemeColor from './color' 13 + 14 + const is$typed = _is$typed, 15 + validate = _validate 16 + const id = 'site.standard.theme.basic' 17 + 18 + export interface Main { 19 + $type?: 'site.standard.theme.basic' 20 + accent: $Typed<SiteStandardThemeColor.Rgb> | { $type: string } 21 + accentForeground: $Typed<SiteStandardThemeColor.Rgb> | { $type: string } 22 + background: $Typed<SiteStandardThemeColor.Rgb> | { $type: string } 23 + foreground: $Typed<SiteStandardThemeColor.Rgb> | { $type: string } 24 + } 25 + 26 + const hashMain = 'main' 27 + 28 + export function isMain<V>(v: V) { 29 + return is$typed(v, id, hashMain) 30 + } 31 + 32 + export function validateMain<V>(v: V) { 33 + return validate<Main & V>(v, id, hashMain) 34 + }
+50
lexicons/api/types/site/standard/theme/color.ts
··· 1 + /** 2 + * GENERATED CODE - DO NOT MODIFY 3 + */ 4 + import { type ValidationResult, BlobRef } from '@atproto/lexicon' 5 + import { CID } from 'multiformats/cid' 6 + import { validate as _validate } from '../../../../lexicons' 7 + import { 8 + type $Typed, 9 + is$typed as _is$typed, 10 + type OmitKey, 11 + } from '../../../../util' 12 + 13 + const is$typed = _is$typed, 14 + validate = _validate 15 + const id = 'site.standard.theme.color' 16 + 17 + export interface Rgb { 18 + $type?: 'site.standard.theme.color#rgb' 19 + r: number 20 + g: number 21 + b: number 22 + } 23 + 24 + const hashRgb = 'rgb' 25 + 26 + export function isRgb<V>(v: V) { 27 + return is$typed(v, id, hashRgb) 28 + } 29 + 30 + export function validateRgb<V>(v: V) { 31 + return validate<Rgb & V>(v, id, hashRgb) 32 + } 33 + 34 + export interface Rgba { 35 + $type?: 'site.standard.theme.color#rgba' 36 + r: number 37 + g: number 38 + b: number 39 + a: number 40 + } 41 + 42 + const hashRgba = 'rgba' 43 + 44 + export function isRgba<V>(v: V) { 45 + return is$typed(v, id, hashRgba) 46 + } 47 + 48 + export function validateRgba<V>(v: V) { 49 + return validate<Rgba & V>(v, id, hashRgba) 50 + }
+2
lexicons/build.ts
··· 10 10 import { PubLeafletRichTextFacet } from "./src/facet"; 11 11 import { PubLeafletComment } from "./src/comment"; 12 12 import { PubLeafletAuthFullPermissions } from "./src/authFullPermissions"; 13 + import { PubLeafletContent } from "./src/content"; 13 14 14 15 const outdir = path.join("lexicons", "pub", "leaflet"); 15 16 ··· 20 21 21 22 const lexicons = [ 22 23 PubLeafletDocument, 24 + PubLeafletContent, 23 25 PubLeafletComment, 24 26 PubLeafletRichTextFacet, 25 27 PubLeafletAuthFullPermissions,
+27
lexicons/pub/leaflet/content.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "pub.leaflet.content", 4 + "revision": 1, 5 + "description": "A lexicon for long form rich media documents", 6 + "defs": { 7 + "main": { 8 + "type": "object", 9 + "description": "Content format for leaflet documents", 10 + "required": [ 11 + "pages" 12 + ], 13 + "properties": { 14 + "pages": { 15 + "type": "array", 16 + "items": { 17 + "type": "union", 18 + "refs": [ 19 + "pub.leaflet.pages.linearDocument", 20 + "pub.leaflet.pages.canvas" 21 + ] 22 + } 23 + } 24 + } 25 + } 26 + } 27 + }
+68
lexicons/site/standard/document.json
··· 1 + { 2 + "defs": { 3 + "main": { 4 + "key": "tid", 5 + "record": { 6 + "properties": { 7 + "bskyPostRef": { 8 + "ref": "com.atproto.repo.strongRef", 9 + "type": "ref" 10 + }, 11 + "content": { 12 + "closed": false, 13 + "refs": ["pub.leaflet.content"], 14 + "type": "union" 15 + }, 16 + "coverImage": { 17 + "accept": ["image/*"], 18 + "maxSize": 1000000, 19 + "type": "blob" 20 + }, 21 + "description": { 22 + "maxGraphemes": 300, 23 + "maxLength": 3000, 24 + "type": "string" 25 + }, 26 + "path": { 27 + "description": "combine with the publication url or the document site to construct a full url to the document", 28 + "type": "string" 29 + }, 30 + "publishedAt": { 31 + "format": "datetime", 32 + "type": "string" 33 + }, 34 + "site": { 35 + "description": "URI to the site or publication this document belongs to (https or at-uri)", 36 + "format": "uri", 37 + "type": "string" 38 + }, 39 + "tags": { 40 + "items": { 41 + "maxGraphemes": 50, 42 + "maxLength": 100, 43 + "type": "string" 44 + }, 45 + "type": "array" 46 + }, 47 + "textContent": { 48 + "type": "string" 49 + }, 50 + "title": { 51 + "maxGraphemes": 128, 52 + "maxLength": 1280, 53 + "type": "string" 54 + }, 55 + "updatedAt": { 56 + "format": "datetime", 57 + "type": "string" 58 + } 59 + }, 60 + "required": ["site", "title", "publishedAt"], 61 + "type": "object" 62 + }, 63 + "type": "record" 64 + } 65 + }, 66 + "id": "site.standard.document", 67 + "lexicon": 1 68 + }
+23
lexicons/site/standard/graph/subscription.json
··· 1 + { 2 + "defs": { 3 + "main": { 4 + "description": "Record declaring a subscription to a publication", 5 + "key": "tid", 6 + "record": { 7 + "properties": { 8 + "publication": { 9 + "format": "at-uri", 10 + "type": "string" 11 + } 12 + }, 13 + "required": [ 14 + "publication" 15 + ], 16 + "type": "object" 17 + }, 18 + "type": "record" 19 + } 20 + }, 21 + "id": "site.standard.graph.subscription", 22 + "lexicon": 1 23 + }
+68
lexicons/site/standard/publication.json
··· 1 + { 2 + "defs": { 3 + "main": { 4 + "key": "tid", 5 + "record": { 6 + "properties": { 7 + "basicTheme": { 8 + "ref": "site.standard.theme.basic", 9 + "type": "ref" 10 + }, 11 + "theme": { 12 + "type": "ref", 13 + "ref": "pub.leaflet.publication#theme" 14 + }, 15 + "description": { 16 + "maxGraphemes": 300, 17 + "maxLength": 3000, 18 + "type": "string" 19 + }, 20 + "icon": { 21 + "accept": ["image/*"], 22 + "maxSize": 1000000, 23 + "type": "blob" 24 + }, 25 + "name": { 26 + "maxGraphemes": 128, 27 + "maxLength": 1280, 28 + "type": "string" 29 + }, 30 + "preferences": { 31 + "ref": "#preferences", 32 + "type": "ref" 33 + }, 34 + "url": { 35 + "format": "uri", 36 + "type": "string" 37 + } 38 + }, 39 + "required": ["url", "name"], 40 + "type": "object" 41 + }, 42 + "type": "record" 43 + }, 44 + "preferences": { 45 + "properties": { 46 + "showInDiscover": { 47 + "default": true, 48 + "type": "boolean" 49 + }, 50 + "showComments": { 51 + "default": true, 52 + "type": "boolean" 53 + }, 54 + "showMentions": { 55 + "default": true, 56 + "type": "boolean" 57 + }, 58 + "showPrevNext": { 59 + "default": false, 60 + "type": "boolean" 61 + } 62 + }, 63 + "type": "object" 64 + } 65 + }, 66 + "id": "site.standard.publication", 67 + "lexicon": 1 68 + }
+41
lexicons/site/standard/theme/basic.json
··· 1 + { 2 + "defs": { 3 + "main": { 4 + "properties": { 5 + "accent": { 6 + "refs": [ 7 + "site.standard.theme.color#rgb" 8 + ], 9 + "type": "union" 10 + }, 11 + "accentForeground": { 12 + "refs": [ 13 + "site.standard.theme.color#rgb" 14 + ], 15 + "type": "union" 16 + }, 17 + "background": { 18 + "refs": [ 19 + "site.standard.theme.color#rgb" 20 + ], 21 + "type": "union" 22 + }, 23 + "foreground": { 24 + "refs": [ 25 + "site.standard.theme.color#rgb" 26 + ], 27 + "type": "union" 28 + } 29 + }, 30 + "required": [ 31 + "background", 32 + "foreground", 33 + "accent", 34 + "accentForeground" 35 + ], 36 + "type": "object" 37 + } 38 + }, 39 + "id": "site.standard.theme.basic", 40 + "lexicon": 1 41 + }
+53
lexicons/site/standard/theme/color.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "site.standard.theme.color", 4 + "defs": { 5 + "rgb": { 6 + "type": "object", 7 + "required": ["r", "g", "b"], 8 + "properties": { 9 + "r": { 10 + "type": "integer", 11 + "minimum": 0, 12 + "maximum": 255 13 + }, 14 + "g": { 15 + "type": "integer", 16 + "minimum": 0, 17 + "maximum": 255 18 + }, 19 + "b": { 20 + "type": "integer", 21 + "minimum": 0, 22 + "maximum": 255 23 + } 24 + } 25 + }, 26 + "rgba": { 27 + "type": "object", 28 + "required": ["r", "g", "b", "a"], 29 + "properties": { 30 + "r": { 31 + "type": "integer", 32 + "minimum": 0, 33 + "maximum": 255 34 + }, 35 + "g": { 36 + "type": "integer", 37 + "minimum": 0, 38 + "maximum": 255 39 + }, 40 + "b": { 41 + "type": "integer", 42 + "minimum": 0, 43 + "maximum": 255 44 + }, 45 + "a": { 46 + "type": "integer", 47 + "minimum": 0, 48 + "maximum": 100 49 + } 50 + } 51 + } 52 + } 53 + }
+29
lexicons/src/content.ts
··· 1 + import { LexiconDoc } from "@atproto/lexicon"; 2 + import { PubLeafletPagesLinearDocument } from "./pages/LinearDocument"; 3 + import { PubLeafletPagesCanvasDocument } from "./pages"; 4 + 5 + export const PubLeafletContent: LexiconDoc = { 6 + lexicon: 1, 7 + id: "pub.leaflet.content", 8 + revision: 1, 9 + description: "A lexicon for long form rich media documents", 10 + defs: { 11 + main: { 12 + type: "object", 13 + description: "Content format for leaflet documents", 14 + required: ["pages"], 15 + properties: { 16 + pages: { 17 + type: "array", 18 + items: { 19 + type: "union", 20 + refs: [ 21 + PubLeafletPagesLinearDocument.id, 22 + PubLeafletPagesCanvasDocument.id, 23 + ], 24 + }, 25 + }, 26 + }, 27 + }, 28 + }, 29 + };
+262
lexicons/src/normalize.ts
··· 1 + /** 2 + * Normalization utilities for converting between pub.leaflet and site.standard lexicon formats. 3 + * 4 + * The standard format (site.standard.*) is used as the canonical representation for 5 + * reading data from the database, while both formats are accepted for storage. 6 + */ 7 + 8 + import type * as PubLeafletDocument from "../api/types/pub/leaflet/document"; 9 + import type * as PubLeafletPublication from "../api/types/pub/leaflet/publication"; 10 + import type * as PubLeafletContent from "../api/types/pub/leaflet/content"; 11 + import type * as SiteStandardDocument from "../api/types/site/standard/document"; 12 + import type * as SiteStandardPublication from "../api/types/site/standard/publication"; 13 + import type * as SiteStandardThemeBasic from "../api/types/site/standard/theme/basic"; 14 + import type * as PubLeafletThemeColor from "../api/types/pub/leaflet/theme/color"; 15 + import type { $Typed } from "../api/util"; 16 + 17 + // Normalized document type - uses the generated site.standard.document type 18 + // with an additional optional theme field for backwards compatibility 19 + export type NormalizedDocument = SiteStandardDocument.Record & { 20 + // Keep the original theme for components that need leaflet-specific styling 21 + theme?: PubLeafletPublication.Theme; 22 + }; 23 + 24 + // Normalized publication type - uses the generated site.standard.publication type 25 + export type NormalizedPublication = SiteStandardPublication.Record; 26 + 27 + /** 28 + * Checks if the record is a pub.leaflet.document 29 + */ 30 + export function isLeafletDocument( 31 + record: unknown 32 + ): record is PubLeafletDocument.Record { 33 + if (!record || typeof record !== "object") return false; 34 + const r = record as Record<string, unknown>; 35 + return ( 36 + r.$type === "pub.leaflet.document" || 37 + // Legacy records without $type but with pages array 38 + (Array.isArray(r.pages) && typeof r.author === "string") 39 + ); 40 + } 41 + 42 + /** 43 + * Checks if the record is a site.standard.document 44 + */ 45 + export function isStandardDocument( 46 + record: unknown 47 + ): record is SiteStandardDocument.Record { 48 + if (!record || typeof record !== "object") return false; 49 + const r = record as Record<string, unknown>; 50 + return r.$type === "site.standard.document"; 51 + } 52 + 53 + /** 54 + * Checks if the record is a pub.leaflet.publication 55 + */ 56 + export function isLeafletPublication( 57 + record: unknown 58 + ): record is PubLeafletPublication.Record { 59 + if (!record || typeof record !== "object") return false; 60 + const r = record as Record<string, unknown>; 61 + return ( 62 + r.$type === "pub.leaflet.publication" || 63 + // Legacy records without $type but with name and no url 64 + (typeof r.name === "string" && !("url" in r)) 65 + ); 66 + } 67 + 68 + /** 69 + * Checks if the record is a site.standard.publication 70 + */ 71 + export function isStandardPublication( 72 + record: unknown 73 + ): record is SiteStandardPublication.Record { 74 + if (!record || typeof record !== "object") return false; 75 + const r = record as Record<string, unknown>; 76 + return r.$type === "site.standard.publication"; 77 + } 78 + 79 + /** 80 + * Extracts RGB values from a color union type 81 + */ 82 + function extractRgb( 83 + color: 84 + | $Typed<PubLeafletThemeColor.Rgba> 85 + | $Typed<PubLeafletThemeColor.Rgb> 86 + | { $type: string } 87 + | undefined 88 + ): { r: number; g: number; b: number } | undefined { 89 + if (!color || typeof color !== "object") return undefined; 90 + const c = color as Record<string, unknown>; 91 + if ( 92 + typeof c.r === "number" && 93 + typeof c.g === "number" && 94 + typeof c.b === "number" 95 + ) { 96 + return { r: c.r, g: c.g, b: c.b }; 97 + } 98 + return undefined; 99 + } 100 + 101 + /** 102 + * Converts a pub.leaflet theme to a site.standard.theme.basic format 103 + */ 104 + export function leafletThemeToBasicTheme( 105 + theme: PubLeafletPublication.Theme | undefined 106 + ): SiteStandardThemeBasic.Main | undefined { 107 + if (!theme) return undefined; 108 + 109 + const background = extractRgb(theme.backgroundColor); 110 + const accent = extractRgb(theme.accentBackground) || extractRgb(theme.primary); 111 + const accentForeground = extractRgb(theme.accentText); 112 + 113 + // If we don't have the required colors, return undefined 114 + if (!background || !accent) return undefined; 115 + 116 + // Default foreground to dark if not specified 117 + const foreground = { r: 0, g: 0, b: 0 }; 118 + 119 + // Default accent foreground to white if not specified 120 + const finalAccentForeground = accentForeground || { r: 255, g: 255, b: 255 }; 121 + 122 + return { 123 + $type: "site.standard.theme.basic", 124 + background: { $type: "site.standard.theme.color#rgb", ...background }, 125 + foreground: { $type: "site.standard.theme.color#rgb", ...foreground }, 126 + accent: { $type: "site.standard.theme.color#rgb", ...accent }, 127 + accentForeground: { 128 + $type: "site.standard.theme.color#rgb", 129 + ...finalAccentForeground, 130 + }, 131 + }; 132 + } 133 + 134 + /** 135 + * Normalizes a document record from either format to the standard format. 136 + * 137 + * @param record - The document record from the database (either pub.leaflet or site.standard) 138 + * @returns A normalized document in site.standard format, or null if invalid/unrecognized 139 + */ 140 + export function normalizeDocument(record: unknown): NormalizedDocument | null { 141 + if (!record || typeof record !== "object") return null; 142 + 143 + // Pass through site.standard records directly 144 + if (isStandardDocument(record)) { 145 + return record as NormalizedDocument; 146 + } 147 + 148 + if (isLeafletDocument(record)) { 149 + // Convert from pub.leaflet to site.standard 150 + const site = record.publication; 151 + const publishedAt = record.publishedAt; 152 + 153 + if (!site || !publishedAt) { 154 + return null; 155 + } 156 + 157 + // Wrap pages in pub.leaflet.content structure 158 + const content: $Typed<PubLeafletContent.Main> | undefined = record.pages 159 + ? { 160 + $type: "pub.leaflet.content" as const, 161 + pages: record.pages, 162 + } 163 + : undefined; 164 + 165 + return { 166 + $type: "site.standard.document", 167 + title: record.title, 168 + site, 169 + publishedAt, 170 + description: record.description, 171 + tags: record.tags, 172 + coverImage: record.coverImage, 173 + bskyPostRef: record.postRef, 174 + content, 175 + theme: record.theme, 176 + }; 177 + } 178 + 179 + return null; 180 + } 181 + 182 + /** 183 + * Normalizes a publication record from either format to the standard format. 184 + * 185 + * @param record - The publication record from the database (either pub.leaflet or site.standard) 186 + * @returns A normalized publication in site.standard format, or null if invalid/unrecognized 187 + */ 188 + export function normalizePublication( 189 + record: unknown 190 + ): NormalizedPublication | null { 191 + if (!record || typeof record !== "object") return null; 192 + 193 + // Pass through site.standard records directly 194 + if (isStandardPublication(record)) { 195 + return record; 196 + } 197 + 198 + if (isLeafletPublication(record)) { 199 + // Convert from pub.leaflet to site.standard 200 + const url = record.base_path ? `https://${record.base_path}` : undefined; 201 + 202 + if (!url) { 203 + return null; 204 + } 205 + 206 + const basicTheme = leafletThemeToBasicTheme(record.theme); 207 + 208 + // Convert preferences to site.standard format (strip/replace $type) 209 + const preferences: SiteStandardPublication.Preferences | undefined = 210 + record.preferences 211 + ? { 212 + showInDiscover: record.preferences.showInDiscover, 213 + showComments: record.preferences.showComments, 214 + showMentions: record.preferences.showMentions, 215 + showPrevNext: record.preferences.showPrevNext, 216 + } 217 + : undefined; 218 + 219 + return { 220 + $type: "site.standard.publication", 221 + name: record.name, 222 + url, 223 + description: record.description, 224 + icon: record.icon, 225 + basicTheme, 226 + theme: record.theme, 227 + preferences, 228 + }; 229 + } 230 + 231 + return null; 232 + } 233 + 234 + /** 235 + * Type guard to check if a normalized document has leaflet content 236 + */ 237 + export function hasLeafletContent( 238 + doc: NormalizedDocument 239 + ): doc is NormalizedDocument & { 240 + content: $Typed<PubLeafletContent.Main>; 241 + } { 242 + return ( 243 + doc.content !== undefined && 244 + (doc.content as { $type?: string }).$type === "pub.leaflet.content" 245 + ); 246 + } 247 + 248 + /** 249 + * Gets the pages array from a normalized document, handling both formats 250 + */ 251 + export function getDocumentPages( 252 + doc: NormalizedDocument 253 + ): PubLeafletContent.Main["pages"] | undefined { 254 + if (!doc.content) return undefined; 255 + 256 + if (hasLeafletContent(doc)) { 257 + return doc.content.pages; 258 + } 259 + 260 + // Unknown content type 261 + return undefined; 262 + }
+403 -29
package-lock.json
··· 16 16 "@atproto/oauth-client-node": "^0.3.8", 17 17 "@atproto/sync": "^0.1.34", 18 18 "@atproto/syntax": "^0.3.3", 19 + "@atproto/tap": "^0.1.1", 19 20 "@atproto/xrpc": "^0.7.5", 20 21 "@atproto/xrpc-server": "^0.9.5", 21 22 "@hono/node-server": "^1.14.3", ··· 264 265 } 265 266 }, 266 267 "node_modules/@atproto/common-web": { 267 - "version": "0.4.3", 268 - "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.3.tgz", 269 - "integrity": "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg==", 268 + "version": "0.4.10", 269 + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.10.tgz", 270 + "integrity": "sha512-TLDZSgSKzT8ZgOrBrTGK87J1CXve9TEuY6NVVUBRkOMzRRtQzpFb9/ih5WVS/hnaWVvE30CfuyaetRoma+WKNw==", 270 271 "license": "MIT", 271 272 "dependencies": { 272 - "graphemer": "^1.4.0", 273 - "multiformats": "^9.9.0", 274 - "uint8arrays": "3.0.0", 273 + "@atproto/lex-data": "0.0.6", 274 + "@atproto/lex-json": "0.0.6", 275 275 "zod": "^3.23.8" 276 276 } 277 277 }, 278 - "node_modules/@atproto/common-web/node_modules/multiformats": { 279 - "version": "9.9.0", 280 - "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 281 - "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 282 - "license": "(Apache-2.0 AND MIT)" 283 - }, 284 278 "node_modules/@atproto/common/node_modules/multiformats": { 285 279 "version": "9.9.0", 286 280 "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", ··· 288 282 "license": "(Apache-2.0 AND MIT)" 289 283 }, 290 284 "node_modules/@atproto/crypto": { 291 - "version": "0.4.4", 292 - "resolved": "https://registry.npmjs.org/@atproto/crypto/-/crypto-0.4.4.tgz", 293 - "integrity": "sha512-Yq9+crJ7WQl7sxStVpHgie5Z51R05etaK9DLWYG/7bR5T4bhdcIgF6IfklLShtZwLYdVVj+K15s0BqW9a8PSDA==", 285 + "version": "0.4.5", 286 + "resolved": "https://registry.npmjs.org/@atproto/crypto/-/crypto-0.4.5.tgz", 287 + "integrity": "sha512-n40aKkMoCatP0u9Yvhrdk6fXyOHFDDbkdm4h4HCyWW+KlKl8iXfD5iV+ECq+w5BM+QH25aIpt3/j6EUNerhLxw==", 294 288 "license": "MIT", 295 289 "dependencies": { 296 290 "@noble/curves": "^1.7.0", ··· 360 354 "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 361 355 "license": "(Apache-2.0 AND MIT)" 362 356 }, 357 + "node_modules/@atproto/lex": { 358 + "version": "0.0.9", 359 + "resolved": "https://registry.npmjs.org/@atproto/lex/-/lex-0.0.9.tgz", 360 + "integrity": "sha512-o6gauf1lz0iyzJR0rqSj4VHOrO+Nt8+/iPb0KPojw1ieXk13zOSTSxotAoDzO/dP6y8Ey5jxwuCQGuzab/4XnQ==", 361 + "license": "MIT", 362 + "dependencies": { 363 + "@atproto/lex-builder": "0.0.9", 364 + "@atproto/lex-client": "0.0.7", 365 + "@atproto/lex-data": "0.0.6", 366 + "@atproto/lex-installer": "0.0.9", 367 + "@atproto/lex-json": "0.0.6", 368 + "@atproto/lex-schema": "0.0.7", 369 + "tslib": "^2.8.1", 370 + "yargs": "^17.0.0" 371 + }, 372 + "bin": { 373 + "lex": "bin/lex", 374 + "ts-lex": "bin/lex" 375 + } 376 + }, 377 + "node_modules/@atproto/lex-builder": { 378 + "version": "0.0.9", 379 + "resolved": "https://registry.npmjs.org/@atproto/lex-builder/-/lex-builder-0.0.9.tgz", 380 + "integrity": "sha512-buOFk1JpuW3twI7To7f/67zQQ1NulLHf/oasH/kTOPUAd0dNyeAa13t9eRSVGbwi0BcZYxRxBm0QzPmdLKyuyw==", 381 + "license": "MIT", 382 + "dependencies": { 383 + "@atproto/lex-document": "0.0.8", 384 + "@atproto/lex-schema": "0.0.7", 385 + "prettier": "^3.2.5", 386 + "ts-morph": "^27.0.0", 387 + "tslib": "^2.8.1" 388 + } 389 + }, 390 + "node_modules/@atproto/lex-builder/node_modules/@ts-morph/common": { 391 + "version": "0.28.1", 392 + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.28.1.tgz", 393 + "integrity": "sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==", 394 + "license": "MIT", 395 + "dependencies": { 396 + "minimatch": "^10.0.1", 397 + "path-browserify": "^1.0.1", 398 + "tinyglobby": "^0.2.14" 399 + } 400 + }, 401 + "node_modules/@atproto/lex-builder/node_modules/minimatch": { 402 + "version": "10.1.1", 403 + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", 404 + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", 405 + "license": "BlueOak-1.0.0", 406 + "dependencies": { 407 + "@isaacs/brace-expansion": "^5.0.0" 408 + }, 409 + "engines": { 410 + "node": "20 || >=22" 411 + }, 412 + "funding": { 413 + "url": "https://github.com/sponsors/isaacs" 414 + } 415 + }, 416 + "node_modules/@atproto/lex-builder/node_modules/ts-morph": { 417 + "version": "27.0.2", 418 + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-27.0.2.tgz", 419 + "integrity": "sha512-fhUhgeljcrdZ+9DZND1De1029PrE+cMkIP7ooqkLRTrRLTqcki2AstsyJm0vRNbTbVCNJ0idGlbBrfqc7/nA8w==", 420 + "license": "MIT", 421 + "dependencies": { 422 + "@ts-morph/common": "~0.28.1", 423 + "code-block-writer": "^13.0.3" 424 + } 425 + }, 426 + "node_modules/@atproto/lex-cbor": { 427 + "version": "0.0.6", 428 + "resolved": "https://registry.npmjs.org/@atproto/lex-cbor/-/lex-cbor-0.0.6.tgz", 429 + "integrity": "sha512-lee2T00owDy3I1plRHuURT6f98NIpYZZr2wXa5pJZz5JzefZ+nv8gJ2V70C2f+jmSG+5S9NTIy4uJw94vaHf4A==", 430 + "license": "MIT", 431 + "dependencies": { 432 + "@atproto/lex-data": "0.0.6", 433 + "multiformats": "^9.9.0", 434 + "tslib": "^2.8.1" 435 + } 436 + }, 437 + "node_modules/@atproto/lex-cbor/node_modules/multiformats": { 438 + "version": "9.9.0", 439 + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 440 + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 441 + "license": "(Apache-2.0 AND MIT)" 442 + }, 363 443 "node_modules/@atproto/lex-cli": { 364 444 "version": "0.9.5", 365 445 "resolved": "https://registry.npmjs.org/@atproto/lex-cli/-/lex-cli-0.9.5.tgz", ··· 390 470 "dev": true, 391 471 "license": "MIT" 392 472 }, 473 + "node_modules/@atproto/lex-client": { 474 + "version": "0.0.7", 475 + "resolved": "https://registry.npmjs.org/@atproto/lex-client/-/lex-client-0.0.7.tgz", 476 + "integrity": "sha512-ofUz3yXJ0nN/M9aqqF2ZUL/4D1wWT1P4popCfV3OEDsDrtWofMflYPFz1IWuyPa2e83paaEHRhaw3bZEhgXH1w==", 477 + "license": "MIT", 478 + "dependencies": { 479 + "@atproto/lex-data": "0.0.6", 480 + "@atproto/lex-json": "0.0.6", 481 + "@atproto/lex-schema": "0.0.7", 482 + "tslib": "^2.8.1" 483 + } 484 + }, 485 + "node_modules/@atproto/lex-data": { 486 + "version": "0.0.6", 487 + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.6.tgz", 488 + "integrity": "sha512-MBNB4ghRJQzuXK1zlUPljpPbQcF1LZ5dzxy274KqPt4p3uPuRw0mHjgcCoWzRUNBQC685WMQR4IN9DHtsnG57A==", 489 + "license": "MIT", 490 + "dependencies": { 491 + "@atproto/syntax": "0.4.2", 492 + "multiformats": "^9.9.0", 493 + "tslib": "^2.8.1", 494 + "uint8arrays": "3.0.0", 495 + "unicode-segmenter": "^0.14.0" 496 + } 497 + }, 498 + "node_modules/@atproto/lex-data/node_modules/@atproto/syntax": { 499 + "version": "0.4.2", 500 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.2.tgz", 501 + "integrity": "sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA==", 502 + "license": "MIT" 503 + }, 504 + "node_modules/@atproto/lex-data/node_modules/multiformats": { 505 + "version": "9.9.0", 506 + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 507 + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 508 + "license": "(Apache-2.0 AND MIT)" 509 + }, 510 + "node_modules/@atproto/lex-document": { 511 + "version": "0.0.8", 512 + "resolved": "https://registry.npmjs.org/@atproto/lex-document/-/lex-document-0.0.8.tgz", 513 + "integrity": "sha512-p3l5h96Hx0vxUwbO/eas6x5h2vU0JVN1a/ktX4k3PlK9YLXfWMFsv+RdVwVZom8o0irHwlcyh1D/cY0PyUojDA==", 514 + "license": "MIT", 515 + "dependencies": { 516 + "@atproto/lex-schema": "0.0.7", 517 + "core-js": "^3", 518 + "tslib": "^2.8.1" 519 + } 520 + }, 521 + "node_modules/@atproto/lex-installer": { 522 + "version": "0.0.9", 523 + "resolved": "https://registry.npmjs.org/@atproto/lex-installer/-/lex-installer-0.0.9.tgz", 524 + "integrity": "sha512-zEeIeSaSCb3j+zNsqqMY7+X5FO6fxy/MafaCEj42KsXQHNcobuygZsnG/0fxMj/kMvhjrNUCp/w9PyOMwx4hQg==", 525 + "license": "MIT", 526 + "dependencies": { 527 + "@atproto/lex-builder": "0.0.9", 528 + "@atproto/lex-cbor": "0.0.6", 529 + "@atproto/lex-data": "0.0.6", 530 + "@atproto/lex-document": "0.0.8", 531 + "@atproto/lex-resolver": "0.0.8", 532 + "@atproto/lex-schema": "0.0.7", 533 + "@atproto/syntax": "0.4.2", 534 + "tslib": "^2.8.1" 535 + } 536 + }, 537 + "node_modules/@atproto/lex-installer/node_modules/@atproto/syntax": { 538 + "version": "0.4.2", 539 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.2.tgz", 540 + "integrity": "sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA==", 541 + "license": "MIT" 542 + }, 543 + "node_modules/@atproto/lex-json": { 544 + "version": "0.0.6", 545 + "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.6.tgz", 546 + "integrity": "sha512-EILnN5cditPvf+PCNjXt7reMuzjugxAL1fpSzmzJbEMGMUwxOf5pPWxRsaA/M3Boip4NQZ+6DVrPOGUMlnqceg==", 547 + "license": "MIT", 548 + "dependencies": { 549 + "@atproto/lex-data": "0.0.6", 550 + "tslib": "^2.8.1" 551 + } 552 + }, 553 + "node_modules/@atproto/lex-resolver": { 554 + "version": "0.0.8", 555 + "resolved": "https://registry.npmjs.org/@atproto/lex-resolver/-/lex-resolver-0.0.8.tgz", 556 + "integrity": "sha512-4hXT560+k5BIttouuhXOr+UkhAuFvvkJaVdqYb8vx2Ez7eHPiZ+yWkUK6FKpyGsx2whHkJzgleEA6DNWtdDlWA==", 557 + "license": "MIT", 558 + "dependencies": { 559 + "@atproto-labs/did-resolver": "0.2.5", 560 + "@atproto/crypto": "0.4.5", 561 + "@atproto/lex-client": "0.0.7", 562 + "@atproto/lex-data": "0.0.6", 563 + "@atproto/lex-document": "0.0.8", 564 + "@atproto/lex-schema": "0.0.7", 565 + "@atproto/repo": "0.8.12", 566 + "@atproto/syntax": "0.4.2", 567 + "tslib": "^2.8.1" 568 + } 569 + }, 570 + "node_modules/@atproto/lex-resolver/node_modules/@atproto-labs/did-resolver": { 571 + "version": "0.2.5", 572 + "resolved": "https://registry.npmjs.org/@atproto-labs/did-resolver/-/did-resolver-0.2.5.tgz", 573 + "integrity": "sha512-he7EC6OMSifNs01a4RT9mta/yYitoKDzlK9ty2TFV5Uj/+HpB4vYMRdIDFrRW0Hcsehy90E2t/dw0t7361MEKQ==", 574 + "license": "MIT", 575 + "dependencies": { 576 + "@atproto-labs/fetch": "0.2.3", 577 + "@atproto-labs/pipe": "0.1.1", 578 + "@atproto-labs/simple-store": "0.3.0", 579 + "@atproto-labs/simple-store-memory": "0.1.4", 580 + "@atproto/did": "0.2.4", 581 + "zod": "^3.23.8" 582 + } 583 + }, 584 + "node_modules/@atproto/lex-resolver/node_modules/@atproto/did": { 585 + "version": "0.2.4", 586 + "resolved": "https://registry.npmjs.org/@atproto/did/-/did-0.2.4.tgz", 587 + "integrity": "sha512-nxNiCgXeo7pfjojq9fpfZxCO0X0xUipNVKW+AHNZwQKiUDt6zYL0VXEfm8HBUwQOCmKvj2pRRSM1Cur+tUWk3g==", 588 + "license": "MIT", 589 + "dependencies": { 590 + "zod": "^3.23.8" 591 + } 592 + }, 593 + "node_modules/@atproto/lex-resolver/node_modules/@atproto/syntax": { 594 + "version": "0.4.2", 595 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.2.tgz", 596 + "integrity": "sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA==", 597 + "license": "MIT" 598 + }, 599 + "node_modules/@atproto/lex-schema": { 600 + "version": "0.0.7", 601 + "resolved": "https://registry.npmjs.org/@atproto/lex-schema/-/lex-schema-0.0.7.tgz", 602 + "integrity": "sha512-/7HkTUsnP1rlzmVE6nnY0kl/hydL/W8V29V8BhFwdAvdDKpYcdRgzzsMe38LAt+ZOjHknRCZDIKGsbQMSbJErw==", 603 + "license": "MIT", 604 + "dependencies": { 605 + "@atproto/lex-data": "0.0.6", 606 + "@atproto/syntax": "0.4.2", 607 + "tslib": "^2.8.1" 608 + } 609 + }, 610 + "node_modules/@atproto/lex-schema/node_modules/@atproto/syntax": { 611 + "version": "0.4.2", 612 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.2.tgz", 613 + "integrity": "sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA==", 614 + "license": "MIT" 615 + }, 393 616 "node_modules/@atproto/lexicon": { 394 617 "version": "0.5.1", 395 618 "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.5.1.tgz", ··· 472 695 } 473 696 }, 474 697 "node_modules/@atproto/repo": { 475 - "version": "0.8.9", 476 - "resolved": "https://registry.npmjs.org/@atproto/repo/-/repo-0.8.9.tgz", 477 - "integrity": "sha512-FTePZS2KEv8++pkOB8GGvm46V6uJqd/95bPA1cXTDXyw0cqeVEOItfxkCH1ky/fY71QYr0NkmqMUwuwZ/gwEtQ==", 698 + "version": "0.8.12", 699 + "resolved": "https://registry.npmjs.org/@atproto/repo/-/repo-0.8.12.tgz", 700 + "integrity": "sha512-QpVTVulgfz5PUiCTELlDBiRvnsnwrFWi+6CfY88VwXzrRHd9NE8GItK7sfxQ6U65vD/idH8ddCgFrlrsn1REPQ==", 478 701 "license": "MIT", 479 702 "dependencies": { 480 - "@atproto/common": "^0.4.12", 481 - "@atproto/common-web": "^0.4.3", 482 - "@atproto/crypto": "^0.4.4", 483 - "@atproto/lexicon": "^0.5.1", 703 + "@atproto/common": "^0.5.3", 704 + "@atproto/common-web": "^0.4.7", 705 + "@atproto/crypto": "^0.4.5", 706 + "@atproto/lexicon": "^0.6.0", 484 707 "@ipld/dag-cbor": "^7.0.0", 485 708 "multiformats": "^9.9.0", 486 709 "uint8arrays": "3.0.0", ··· 491 714 "node": ">=18.7.0" 492 715 } 493 716 }, 717 + "node_modules/@atproto/repo/node_modules/@atproto/common": { 718 + "version": "0.5.6", 719 + "resolved": "https://registry.npmjs.org/@atproto/common/-/common-0.5.6.tgz", 720 + "integrity": "sha512-rbWoZwHQNP8jcwjCREVecchw8aaoM5A1NCONyb9PVDWOJLRLCzojYMeIS8IbFqXo6NyIByOGddupADkkLeVBGQ==", 721 + "license": "MIT", 722 + "dependencies": { 723 + "@atproto/common-web": "^0.4.10", 724 + "@atproto/lex-cbor": "0.0.6", 725 + "@atproto/lex-data": "0.0.6", 726 + "iso-datestring-validator": "^2.2.2", 727 + "multiformats": "^9.9.0", 728 + "pino": "^8.21.0" 729 + }, 730 + "engines": { 731 + "node": ">=18.7.0" 732 + } 733 + }, 734 + "node_modules/@atproto/repo/node_modules/@atproto/lexicon": { 735 + "version": "0.6.0", 736 + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.6.0.tgz", 737 + "integrity": "sha512-5veb8aD+J5M0qszLJ+73KSFsFrJBgAY/nM1TSAJvGY7fNc9ZAT+PSUlmIyrdye9YznAZ07yktalls/TwNV7cHQ==", 738 + "license": "MIT", 739 + "dependencies": { 740 + "@atproto/common-web": "^0.4.7", 741 + "@atproto/syntax": "^0.4.2", 742 + "iso-datestring-validator": "^2.2.2", 743 + "multiformats": "^9.9.0", 744 + "zod": "^3.23.8" 745 + } 746 + }, 747 + "node_modules/@atproto/repo/node_modules/@atproto/syntax": { 748 + "version": "0.4.2", 749 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.2.tgz", 750 + "integrity": "sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA==", 751 + "license": "MIT" 752 + }, 494 753 "node_modules/@atproto/repo/node_modules/multiformats": { 495 754 "version": "9.9.0", 496 755 "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", ··· 535 794 "integrity": "sha512-8CNmi5DipOLaVeSMPggMe7FCksVag0aO6XZy9WflbduTKM4dFZVCs4686UeMLfGRXX+X966XgwECHoLYrovMMg==", 536 795 "license": "MIT" 537 796 }, 797 + "node_modules/@atproto/tap": { 798 + "version": "0.1.1", 799 + "resolved": "https://registry.npmjs.org/@atproto/tap/-/tap-0.1.1.tgz", 800 + "integrity": "sha512-gW4NzLOxj74TzaDOVzzzt5kl2PdC0r75XkIpYpI5xobwCfsc/DmVtwpuSw1fW9gr4Vzk2Q90S9UE4ifAFl2gyA==", 801 + "license": "MIT", 802 + "dependencies": { 803 + "@atproto/common": "^0.5.6", 804 + "@atproto/lex": "^0.0.9", 805 + "@atproto/syntax": "^0.4.2", 806 + "@atproto/ws-client": "^0.0.4", 807 + "ws": "^8.12.0", 808 + "zod": "^3.23.8" 809 + }, 810 + "engines": { 811 + "node": ">=18.7.0" 812 + } 813 + }, 814 + "node_modules/@atproto/tap/node_modules/@atproto/common": { 815 + "version": "0.5.6", 816 + "resolved": "https://registry.npmjs.org/@atproto/common/-/common-0.5.6.tgz", 817 + "integrity": "sha512-rbWoZwHQNP8jcwjCREVecchw8aaoM5A1NCONyb9PVDWOJLRLCzojYMeIS8IbFqXo6NyIByOGddupADkkLeVBGQ==", 818 + "license": "MIT", 819 + "dependencies": { 820 + "@atproto/common-web": "^0.4.10", 821 + "@atproto/lex-cbor": "0.0.6", 822 + "@atproto/lex-data": "0.0.6", 823 + "iso-datestring-validator": "^2.2.2", 824 + "multiformats": "^9.9.0", 825 + "pino": "^8.21.0" 826 + }, 827 + "engines": { 828 + "node": ">=18.7.0" 829 + } 830 + }, 831 + "node_modules/@atproto/tap/node_modules/@atproto/syntax": { 832 + "version": "0.4.2", 833 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.2.tgz", 834 + "integrity": "sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA==", 835 + "license": "MIT" 836 + }, 837 + "node_modules/@atproto/tap/node_modules/multiformats": { 838 + "version": "9.9.0", 839 + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 840 + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 841 + "license": "(Apache-2.0 AND MIT)" 842 + }, 843 + "node_modules/@atproto/ws-client": { 844 + "version": "0.0.4", 845 + "resolved": "https://registry.npmjs.org/@atproto/ws-client/-/ws-client-0.0.4.tgz", 846 + "integrity": "sha512-dox1XIymuC7/ZRhUqKezIGgooZS45C6vHCfu0PnWjfvsLCK2kAlnvX4IBkA/WpcoijDhQ9ejChnFbo/sLmgvAg==", 847 + "license": "MIT", 848 + "dependencies": { 849 + "@atproto/common": "^0.5.3", 850 + "ws": "^8.12.0" 851 + }, 852 + "engines": { 853 + "node": ">=18.7.0" 854 + } 855 + }, 856 + "node_modules/@atproto/ws-client/node_modules/@atproto/common": { 857 + "version": "0.5.6", 858 + "resolved": "https://registry.npmjs.org/@atproto/common/-/common-0.5.6.tgz", 859 + "integrity": "sha512-rbWoZwHQNP8jcwjCREVecchw8aaoM5A1NCONyb9PVDWOJLRLCzojYMeIS8IbFqXo6NyIByOGddupADkkLeVBGQ==", 860 + "license": "MIT", 861 + "dependencies": { 862 + "@atproto/common-web": "^0.4.10", 863 + "@atproto/lex-cbor": "0.0.6", 864 + "@atproto/lex-data": "0.0.6", 865 + "iso-datestring-validator": "^2.2.2", 866 + "multiformats": "^9.9.0", 867 + "pino": "^8.21.0" 868 + }, 869 + "engines": { 870 + "node": ">=18.7.0" 871 + } 872 + }, 873 + "node_modules/@atproto/ws-client/node_modules/multiformats": { 874 + "version": "9.9.0", 875 + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 876 + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 877 + "license": "(Apache-2.0 AND MIT)" 878 + }, 538 879 "node_modules/@atproto/xrpc": { 539 880 "version": "0.7.5", 540 881 "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.5.tgz", ··· 2553 2894 "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 2554 2895 "license": "(Apache-2.0 AND MIT)" 2555 2896 }, 2897 + "node_modules/@isaacs/balanced-match": { 2898 + "version": "4.0.1", 2899 + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", 2900 + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", 2901 + "license": "MIT", 2902 + "engines": { 2903 + "node": "20 || >=22" 2904 + } 2905 + }, 2906 + "node_modules/@isaacs/brace-expansion": { 2907 + "version": "5.0.0", 2908 + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", 2909 + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", 2910 + "license": "MIT", 2911 + "dependencies": { 2912 + "@isaacs/balanced-match": "^4.0.1" 2913 + }, 2914 + "engines": { 2915 + "node": "20 || >=22" 2916 + } 2917 + }, 2556 2918 "node_modules/@isaacs/fs-minipass": { 2557 2919 "version": "4.0.1", 2558 2920 "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", ··· 9108 9470 "version": "13.0.3", 9109 9471 "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", 9110 9472 "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", 9111 - "dev": true, 9112 9473 "license": "MIT" 9113 9474 }, 9114 9475 "node_modules/collapse-white-space": { ··· 9216 9577 "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 9217 9578 "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", 9218 9579 "license": "MIT" 9580 + }, 9581 + "node_modules/core-js": { 9582 + "version": "3.47.0", 9583 + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", 9584 + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", 9585 + "hasInstallScript": true, 9586 + "license": "MIT", 9587 + "funding": { 9588 + "type": "opencollective", 9589 + "url": "https://opencollective.com/core-js" 9590 + } 9219 9591 }, 9220 9592 "node_modules/crelt": { 9221 9593 "version": "1.0.6", ··· 11884 12256 "node_modules/graphemer": { 11885 12257 "version": "1.4.0", 11886 12258 "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", 11887 - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" 12259 + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", 12260 + "dev": true 11888 12261 }, 11889 12262 "node_modules/gzip-size": { 11890 12263 "version": "6.0.0", ··· 15638 16011 "version": "1.0.1", 15639 16012 "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", 15640 16013 "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", 15641 - "dev": true, 15642 16014 "license": "MIT" 15643 16015 }, 15644 16016 "node_modules/path-exists": { ··· 15920 16292 "version": "3.2.5", 15921 16293 "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", 15922 16294 "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", 15923 - "dev": true, 15924 16295 "bin": { 15925 16296 "prettier": "bin/prettier.cjs" 15926 16297 }, ··· 18024 18395 "version": "0.2.15", 18025 18396 "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", 18026 18397 "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", 18027 - "dev": true, 18028 18398 "license": "MIT", 18029 18399 "dependencies": { 18030 18400 "fdir": "^6.5.0", ··· 18041 18411 "version": "6.5.0", 18042 18412 "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", 18043 18413 "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", 18044 - "dev": true, 18045 18414 "license": "MIT", 18046 18415 "engines": { 18047 18416 "node": ">=12.0.0" ··· 18059 18428 "version": "4.0.3", 18060 18429 "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 18061 18430 "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 18062 - "dev": true, 18063 18431 "license": "MIT", 18064 18432 "engines": { 18065 18433 "node": ">=12" ··· 18449 18817 "version": "6.21.0", 18450 18818 "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", 18451 18819 "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", 18820 + "license": "MIT" 18821 + }, 18822 + "node_modules/unicode-segmenter": { 18823 + "version": "0.14.5", 18824 + "resolved": "https://registry.npmjs.org/unicode-segmenter/-/unicode-segmenter-0.14.5.tgz", 18825 + "integrity": "sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==", 18452 18826 "license": "MIT" 18453 18827 }, 18454 18828 "node_modules/unified": {
+2 -1
package.json
··· 7 7 "dev": "TZ=UTC next dev --turbo", 8 8 "publish-lexicons": "tsx lexicons/publish.ts", 9 9 "generate-db-types": "supabase gen types --local > supabase/database.types.ts && drizzle-kit introspect && rm -rf ./drizzle/*.sql ./drizzle/meta", 10 - "lexgen": "tsx ./lexicons/build.ts && lex gen-api ./lexicons/api ./lexicons/pub/leaflet/document.json ./lexicons/pub/leaflet/comment.json ./lexicons/pub/leaflet/publication.json ./lexicons/pub/leaflet/*/* ./lexicons/com/atproto/*/* ./lexicons/app/bsky/*/* --yes && tsx ./lexicons/fix-extensions.ts ./lexicons/api", 10 + "lexgen": "tsx ./lexicons/build.ts && lex gen-api ./lexicons/api ./lexicons/pub/leaflet/document.json ./lexicons/pub/leaflet/comment.json ./lexicons/pub/leaflet/publication.json ./lexicons/pub/leaflet/content.json ./lexicons/pub/leaflet/*/* ./lexicons/com/atproto/*/* ./lexicons/app/bsky/*/* ./lexicons/site/*/* ./lexicons/site/*/*/* --yes && tsx ./lexicons/fix-extensions.ts ./lexicons/api", 11 11 "wrangler-dev": "wrangler dev", 12 12 "build-appview": "esbuild appview/index.ts --outfile=appview/dist/index.js --bundle --platform=node", 13 13 "build-feed-service": "esbuild feeds/index.ts --outfile=feeds/dist/index.js --bundle --platform=node", ··· 26 26 "@atproto/oauth-client-node": "^0.3.8", 27 27 "@atproto/sync": "^0.1.34", 28 28 "@atproto/syntax": "^0.3.3", 29 + "@atproto/tap": "^0.1.1", 29 30 "@atproto/xrpc": "^0.7.5", 30 31 "@atproto/xrpc-server": "^0.9.5", 31 32 "@hono/node-server": "^1.14.3",
+284
patterns/lexicons.md
··· 1 + # Lexicon System 2 + 3 + ## Overview 4 + 5 + Lexicons define the schema for AT Protocol records. This project has two namespaces: 6 + - **`pub.leaflet.*`** - Leaflet-specific lexicons (documents, publications, blocks, etc.) 7 + - **`site.standard.*`** - Standard site lexicons for interoperability 8 + 9 + The lexicons are defined as TypeScript in `lexicons/src/`, built to JSON in `lexicons/pub/leaflet/` and `lexicons/site/standard/`, and TypeScript types are generated in `lexicons/api/`. 10 + 11 + ## Key Files 12 + 13 + - **`lexicons/src/*.ts`** - Source definitions for `pub.leaflet.*` lexicons 14 + - **`lexicons/site/standard/**/*.json`** - JSON definitions for `site.standard.*` lexicons (manually maintained) 15 + - **`lexicons/build.ts`** - Builds TypeScript sources to JSON 16 + - **`lexicons/api/`** - Generated TypeScript types and client 17 + - **`package.json`** - Contains `lexgen` script 18 + 19 + ## Running Lexicon Generation 20 + 21 + ```bash 22 + npm run lexgen 23 + ``` 24 + 25 + This runs: 26 + 1. `tsx ./lexicons/build.ts` - Builds `pub.leaflet.*` JSON from TypeScript 27 + 2. `lex gen-api` - Generates TypeScript types from all JSON lexicons 28 + 3. `tsx ./lexicons/fix-extensions.ts` - Fixes import extensions 29 + 30 + ## Adding a New pub.leaflet Lexicon 31 + 32 + ### 1. Create the Source Definition 33 + 34 + Create a file in `lexicons/src/` (e.g., `lexicons/src/myLexicon.ts`): 35 + 36 + ```typescript 37 + import { LexiconDoc } from "@atproto/lexicon"; 38 + 39 + export const PubLeafletMyLexicon: LexiconDoc = { 40 + lexicon: 1, 41 + id: "pub.leaflet.myLexicon", 42 + defs: { 43 + main: { 44 + type: "record", // or "object" for non-record types 45 + key: "tid", 46 + record: { 47 + type: "object", 48 + required: ["field1"], 49 + properties: { 50 + field1: { type: "string", maxLength: 1000 }, 51 + field2: { type: "integer", minimum: 0 }, 52 + optionalRef: { type: "ref", ref: "other.lexicon#def" }, 53 + }, 54 + }, 55 + }, 56 + // Additional defs for sub-objects 57 + subType: { 58 + type: "object", 59 + properties: { 60 + nested: { type: "string" }, 61 + }, 62 + }, 63 + }, 64 + }; 65 + ``` 66 + 67 + ### 2. Add to Build 68 + 69 + Update `lexicons/build.ts`: 70 + 71 + ```typescript 72 + import { PubLeafletMyLexicon } from "./src/myLexicon"; 73 + 74 + const lexicons = [ 75 + // ... existing lexicons 76 + PubLeafletMyLexicon, 77 + ]; 78 + ``` 79 + 80 + ### 3. Update lexgen Command (if needed) 81 + 82 + If your lexicon is at the top level of `pub/leaflet/` (not in a subdirectory), add it to the `lexgen` script in `package.json`: 83 + 84 + ```json 85 + "lexgen": "tsx ./lexicons/build.ts && lex gen-api ./lexicons/api ./lexicons/pub/leaflet/document.json ./lexicons/pub/leaflet/myLexicon.json ./lexicons/pub/leaflet/*/* ..." 86 + ``` 87 + 88 + Note: Files in subdirectories (`pub/leaflet/*/*`) are automatically included. 89 + 90 + ### 4. Regenerate Types 91 + 92 + ```bash 93 + npm run lexgen 94 + ``` 95 + 96 + ### 5. Use the Generated Types 97 + 98 + ```typescript 99 + import { PubLeafletMyLexicon } from "lexicons/api"; 100 + 101 + // Type for the record 102 + type MyRecord = PubLeafletMyLexicon.Record; 103 + 104 + // Validation 105 + const result = PubLeafletMyLexicon.validateRecord(data); 106 + if (result.success) { 107 + // result.value is typed 108 + } 109 + 110 + // Type guard 111 + if (PubLeafletMyLexicon.isRecord(data)) { 112 + // data is typed as Record 113 + } 114 + ``` 115 + 116 + ## Adding a New site.standard Lexicon 117 + 118 + ### 1. Create the JSON Definition 119 + 120 + Create a file in `lexicons/site/standard/` (e.g., `lexicons/site/standard/myType.json`): 121 + 122 + ```json 123 + { 124 + "lexicon": 1, 125 + "id": "site.standard.myType", 126 + "defs": { 127 + "main": { 128 + "type": "record", 129 + "key": "tid", 130 + "record": { 131 + "type": "object", 132 + "required": ["field1"], 133 + "properties": { 134 + "field1": { 135 + "type": "string", 136 + "maxLength": 1000 137 + } 138 + } 139 + } 140 + } 141 + } 142 + } 143 + ``` 144 + 145 + ### 2. Regenerate Types 146 + 147 + ```bash 148 + npm run lexgen 149 + ``` 150 + 151 + The `site/*/* site/*/*/*` globs in the lexgen command automatically pick up new files. 152 + 153 + ## Common Lexicon Patterns 154 + 155 + ### Referencing Other Lexicons 156 + 157 + ```typescript 158 + // Reference another lexicon's main def 159 + { type: "ref", ref: "pub.leaflet.publication" } 160 + 161 + // Reference a specific def within a lexicon 162 + { type: "ref", ref: "pub.leaflet.publication#theme" } 163 + 164 + // Reference within the same lexicon 165 + { type: "ref", ref: "#myDef" } 166 + ``` 167 + 168 + ### Union Types 169 + 170 + ```typescript 171 + { 172 + type: "union", 173 + refs: [ 174 + "pub.leaflet.pages.linearDocument", 175 + "pub.leaflet.pages.canvas", 176 + ], 177 + } 178 + 179 + // Open union (allows unknown types) 180 + { 181 + type: "union", 182 + closed: false, // default is true 183 + refs: ["pub.leaflet.content"], 184 + } 185 + ``` 186 + 187 + ### Blob Types (for images/files) 188 + 189 + ```typescript 190 + { 191 + type: "blob", 192 + accept: ["image/*"], // or specific types like ["image/png", "image/jpeg"] 193 + maxSize: 1000000, // bytes 194 + } 195 + ``` 196 + 197 + ### Color Types 198 + 199 + The project has color types defined: 200 + - `pub.leaflet.theme.color#rgb` / `#rgba` 201 + - `site.standard.theme.color#rgb` / `#rgba` 202 + 203 + ```typescript 204 + // In lexicons/src/theme.ts 205 + export const ColorUnion = { 206 + type: "union", 207 + refs: [ 208 + "pub.leaflet.theme.color#rgba", 209 + "pub.leaflet.theme.color#rgb", 210 + ], 211 + }; 212 + ``` 213 + 214 + ## Normalization Between Formats 215 + 216 + Use `lexicons/src/normalize.ts` to convert between `pub.leaflet` and `site.standard` formats: 217 + 218 + ```typescript 219 + import { 220 + normalizeDocument, 221 + normalizePublication, 222 + isLeafletDocument, 223 + isStandardDocument, 224 + getDocumentPages, 225 + } from "lexicons/src/normalize"; 226 + 227 + // Normalize a document from either format 228 + const normalized = normalizeDocument(record); 229 + if (normalized) { 230 + // normalized is always in site.standard.document format 231 + console.log(normalized.title, normalized.site); 232 + 233 + // Get pages if content is pub.leaflet.content 234 + const pages = getDocumentPages(normalized); 235 + } 236 + 237 + // Normalize a publication 238 + const pub = normalizePublication(record); 239 + if (pub) { 240 + console.log(pub.name, pub.url); 241 + } 242 + ``` 243 + 244 + ## Handling in Appview (Firehose Consumer) 245 + 246 + When processing records from the firehose in `appview/index.ts`: 247 + 248 + ```typescript 249 + import { ids } from "lexicons/api/lexicons"; 250 + import { PubLeafletMyLexicon } from "lexicons/api"; 251 + 252 + // In filterCollections: 253 + filterCollections: [ 254 + ids.PubLeafletMyLexicon, 255 + // ... 256 + ], 257 + 258 + // In handleEvent: 259 + if (evt.collection === ids.PubLeafletMyLexicon) { 260 + if (evt.event === "create" || evt.event === "update") { 261 + let record = PubLeafletMyLexicon.validateRecord(evt.record); 262 + if (!record.success) return; 263 + 264 + // Store in database 265 + await supabase.from("my_table").upsert({ 266 + uri: evt.uri.toString(), 267 + data: record.value as Json, 268 + }); 269 + } 270 + if (evt.event === "delete") { 271 + await supabase.from("my_table").delete().eq("uri", evt.uri.toString()); 272 + } 273 + } 274 + ``` 275 + 276 + ## Publishing Lexicons 277 + 278 + To publish lexicons to an AT Protocol PDS: 279 + 280 + ```bash 281 + npm run publish-lexicons 282 + ``` 283 + 284 + This runs `lexicons/publish.ts` which publishes lexicons to the configured PDS.
+137 -7
supabase/database.types.ts
··· 580 580 } 581 581 leaflets_in_publications: { 582 582 Row: { 583 - archived: boolean | null 584 583 cover_image: string | null 585 584 description: string 586 585 doc: string | null 587 586 leaflet: string 588 587 publication: string 588 + tags: string[] | null 589 589 title: string 590 590 } 591 591 Insert: { 592 - archived?: boolean | null 593 592 cover_image?: string | null 594 593 description?: string 595 594 doc?: string | null 596 595 leaflet: string 597 596 publication: string 597 + tags?: string[] | null 598 598 title?: string 599 599 } 600 600 Update: { 601 - archived?: boolean | null 602 601 cover_image?: string | null 603 602 description?: string 604 603 doc?: string | null 605 604 leaflet?: string 606 605 publication?: string 606 + tags?: string[] | null 607 607 title?: string 608 608 } 609 609 Relationships: [ ··· 637 637 description: string 638 638 document: string 639 639 leaflet: string 640 + tags: string[] | null 640 641 title: string 641 642 } 642 643 Insert: { ··· 645 646 description?: string 646 647 document: string 647 648 leaflet: string 649 + tags?: string[] | null 648 650 title?: string 649 651 } 650 652 Update: { ··· 653 655 description?: string 654 656 document?: string 655 657 leaflet?: string 658 + tags?: string[] | null 656 659 title?: string 657 660 } 658 661 Relationships: [ ··· 736 739 } 737 740 permission_token_on_homepage: { 738 741 Row: { 739 - archived: boolean | null 740 742 created_at: string 741 743 identity: string 742 744 token: string 743 745 } 744 746 Insert: { 745 - archived?: boolean | null 746 747 created_at?: string 747 748 identity: string 748 749 token: string 749 750 } 750 751 Update: { 751 - archived?: boolean | null 752 752 created_at?: string 753 753 identity?: string 754 754 token?: string ··· 762 762 referencedColumns: ["id"] 763 763 }, 764 764 { 765 - foreignKeyName: "permission_token_creator_token_fkey" 765 + foreignKeyName: "permission_token_on_homepage_token_fkey" 766 766 columns: ["token"] 767 767 isOneToOne: false 768 768 referencedRelation: "permission_tokens" ··· 1079 1079 last_mutation?: number 1080 1080 } 1081 1081 Relationships: [] 1082 + } 1083 + site_standard_documents: { 1084 + Row: { 1085 + data: Json 1086 + identity_did: string 1087 + indexed_at: string 1088 + uri: string 1089 + } 1090 + Insert: { 1091 + data: Json 1092 + identity_did: string 1093 + indexed_at?: string 1094 + uri: string 1095 + } 1096 + Update: { 1097 + data?: Json 1098 + identity_did?: string 1099 + indexed_at?: string 1100 + uri?: string 1101 + } 1102 + Relationships: [ 1103 + { 1104 + foreignKeyName: "site_standard_documents_identity_did_fkey" 1105 + columns: ["identity_did"] 1106 + isOneToOne: false 1107 + referencedRelation: "identities" 1108 + referencedColumns: ["atp_did"] 1109 + }, 1110 + ] 1111 + } 1112 + site_standard_documents_in_publications: { 1113 + Row: { 1114 + document: string 1115 + indexed_at: string 1116 + publication: string 1117 + } 1118 + Insert: { 1119 + document: string 1120 + indexed_at?: string 1121 + publication: string 1122 + } 1123 + Update: { 1124 + document?: string 1125 + indexed_at?: string 1126 + publication?: string 1127 + } 1128 + Relationships: [ 1129 + { 1130 + foreignKeyName: "site_standard_documents_in_publications_document_fkey" 1131 + columns: ["document"] 1132 + isOneToOne: false 1133 + referencedRelation: "site_standard_documents" 1134 + referencedColumns: ["uri"] 1135 + }, 1136 + { 1137 + foreignKeyName: "site_standard_documents_in_publications_publication_fkey" 1138 + columns: ["publication"] 1139 + isOneToOne: false 1140 + referencedRelation: "site_standard_publications" 1141 + referencedColumns: ["uri"] 1142 + }, 1143 + ] 1144 + } 1145 + site_standard_publications: { 1146 + Row: { 1147 + data: Json 1148 + identity_did: string 1149 + indexed_at: string 1150 + uri: string 1151 + } 1152 + Insert: { 1153 + data: Json 1154 + identity_did: string 1155 + indexed_at?: string 1156 + uri: string 1157 + } 1158 + Update: { 1159 + data?: Json 1160 + identity_did?: string 1161 + indexed_at?: string 1162 + uri?: string 1163 + } 1164 + Relationships: [ 1165 + { 1166 + foreignKeyName: "site_standard_publications_identity_did_fkey" 1167 + columns: ["identity_did"] 1168 + isOneToOne: false 1169 + referencedRelation: "identities" 1170 + referencedColumns: ["atp_did"] 1171 + }, 1172 + ] 1173 + } 1174 + site_standard_subscriptions: { 1175 + Row: { 1176 + created_at: string 1177 + identity: string 1178 + publication: string 1179 + record: Json 1180 + uri: string 1181 + } 1182 + Insert: { 1183 + created_at?: string 1184 + identity: string 1185 + publication: string 1186 + record: Json 1187 + uri: string 1188 + } 1189 + Update: { 1190 + created_at?: string 1191 + identity?: string 1192 + publication?: string 1193 + record?: Json 1194 + uri?: string 1195 + } 1196 + Relationships: [ 1197 + { 1198 + foreignKeyName: "site_standard_subscriptions_identity_fkey" 1199 + columns: ["identity"] 1200 + isOneToOne: false 1201 + referencedRelation: "identities" 1202 + referencedColumns: ["atp_did"] 1203 + }, 1204 + { 1205 + foreignKeyName: "site_standard_subscriptions_publication_fkey" 1206 + columns: ["publication"] 1207 + isOneToOne: false 1208 + referencedRelation: "site_standard_publications" 1209 + referencedColumns: ["uri"] 1210 + }, 1211 + ] 1082 1212 } 1083 1213 subscribers_to_publications: { 1084 1214 Row: {