@getcirrus/pds 0.2.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2,31 +2,95 @@ import { DurableObject, env, waitUntil } from "cloudflare:workers";
2
2
  import { BlockMap, ReadableBlockstore, Repo, WriteOpAction, blocksToCarFile, readCarWithRoot } from "@atproto/repo";
3
3
  import { Secp256k1Keypair, randomStr, verifySignature } from "@atproto/crypto";
4
4
  import { CID, asCid, isBlobRef } from "@atproto/lex-data";
5
- import { TID, check, didDocument, getServiceEndpoint } from "@atproto/common-web";
6
- import { AtUri, ensureValidDid, ensureValidHandle } from "@atproto/syntax";
7
- import { cidForRawBytes, decode, encode } from "@atproto/lex-cbor";
5
+ import { now } from "@atcute/tid";
6
+ import { decode, encode, fromBytes, isBytes, toBytes, toCidLink } from "@atcute/cbor";
7
+ import { CODEC_RAW, create, fromString, toString } from "@atcute/cid";
8
8
  import { Hono } from "hono";
9
9
  import { cors } from "hono/cors";
10
+ import { isDid, isHandle } from "@atcute/lexicons/syntax";
10
11
  import { SignJWT, jwtVerify } from "jose";
11
12
  import { compare, compare as compare$1 } from "bcryptjs";
12
13
  import { ATProtoOAuthProvider } from "@getcirrus/oauth-provider";
13
- import { Lexicons, jsonToLex } from "@atproto/lexicon";
14
+ import { CompositeDidDocumentResolver, PlcDidDocumentResolver, WebDidDocumentResolver } from "@atcute/identity-resolver";
15
+ import { defs, getAtprotoServiceEndpoint } from "@atcute/identity";
16
+ import { ValidationError, parse } from "@atcute/lexicons/validations";
17
+ import { AppBskyActorProfile, AppBskyFeedGenerator, AppBskyFeedLike, AppBskyFeedPost, AppBskyFeedPostgate, AppBskyFeedRepost, AppBskyFeedThreadgate, AppBskyGraphBlock, AppBskyGraphFollow, AppBskyGraphList, AppBskyGraphListblock, AppBskyGraphListitem, AppBskyGraphStarterpack, AppBskyGraphVerification, AppBskyLabelerService } from "@atcute/bluesky";
14
18
 
15
- //#region rolldown:runtime
16
- var __defProp = Object.defineProperty;
17
- var __exportAll = (all, symbols) => {
18
- let target = {};
19
- for (var name in all) {
20
- __defProp(target, name, {
21
- get: all[name],
22
- enumerable: true
23
- });
19
+ //#region src/cbor-compat.ts
20
+ /**
21
+ * CBOR compatibility layer for migrating from @atproto/lex-cbor to @atcute/cbor.
22
+ *
23
+ * @atcute/cbor uses lazy wrappers (BytesWrapper, CidLinkWrapper) that are
24
+ * compatible with atproto's lex-json format. This layer handles conversion
25
+ * of @atproto CID objects to CidLinkWrapper for encoding.
26
+ *
27
+ * Use toCidLink/fromCidLink to convert between lex-json and raw CID types.
28
+ */
29
+ /**
30
+ * Check if a value is an @atproto CID object.
31
+ */
32
+ function isAtprotoCid(value) {
33
+ if (value === null || typeof value !== "object") return false;
34
+ const obj = value;
35
+ return "asCID" in obj && obj[Symbol.toStringTag] === "CID";
36
+ }
37
+ /**
38
+ * Convert @atproto CID to @atcute CidLink.
39
+ */
40
+ function atprotoCidToCidLink(cid) {
41
+ return toCidLink(fromString(cid.toString()));
42
+ }
43
+ /**
44
+ * Recursively convert @atproto CIDs to @atcute CidLinks for encoding.
45
+ */
46
+ function convertCidsForEncode(value) {
47
+ if (value === null || value === void 0) return value;
48
+ if (typeof value !== "object") return value;
49
+ if (ArrayBuffer.isView(value) && value instanceof Uint8Array) return toBytes(value);
50
+ if (isAtprotoCid(value)) return atprotoCidToCidLink(value);
51
+ if (Array.isArray(value)) return value.map(convertCidsForEncode);
52
+ const obj = value;
53
+ if (obj.constructor === Object) {
54
+ const result = {};
55
+ for (const [key, val] of Object.entries(obj)) result[key] = convertCidsForEncode(val);
56
+ return result;
24
57
  }
25
- if (symbols) {
26
- __defProp(target, Symbol.toStringTag, { value: "Module" });
58
+ return value;
59
+ }
60
+ /**
61
+ * Encode a value to CBOR, automatically converting @atproto CIDs to CidLinks.
62
+ *
63
+ * Decoded values will contain CidLinkWrapper objects which have a lazy $link
64
+ * getter returning the CID string - compatible with lex-json format.
65
+ */
66
+ function encode$1(value) {
67
+ return encode(convertCidsForEncode(value));
68
+ }
69
+ /**
70
+ * Recursively convert @atcute wrappers back to raw types for decoding.
71
+ */
72
+ function convertWrappersForDecode(value) {
73
+ if (value === null || value === void 0) return value;
74
+ if (typeof value !== "object") return value;
75
+ if (isBytes(value)) return fromBytes(value);
76
+ if (Array.isArray(value)) return value.map(convertWrappersForDecode);
77
+ const obj = value;
78
+ if (obj.constructor === Object) {
79
+ const result = {};
80
+ for (const [key, val] of Object.entries(obj)) result[key] = convertWrappersForDecode(val);
81
+ return result;
27
82
  }
28
- return target;
29
- };
83
+ return value;
84
+ }
85
+ /**
86
+ * Decode CBOR bytes.
87
+ *
88
+ * Unwraps BytesWrapper to raw Uint8Array for compatibility.
89
+ * CidLinkWrapper is left as-is (access via .$link for lex-json compat).
90
+ */
91
+ function decode$1(bytes) {
92
+ return convertWrappersForDecode(decode(bytes));
93
+ }
30
94
 
31
95
  //#endregion
32
96
  //#region src/storage.ts
@@ -406,11 +470,11 @@ var SqliteOAuthStorage = class {
406
470
  * Clean up expired entries. Should be called periodically.
407
471
  */
408
472
  cleanup() {
409
- const now = Date.now();
410
- this.sql.exec("DELETE FROM oauth_auth_codes WHERE expires_at < ?", now);
411
- this.sql.exec("DELETE FROM oauth_tokens WHERE expires_at < ? AND revoked = 0", now);
412
- this.sql.exec("DELETE FROM oauth_par_requests WHERE expires_at < ?", now);
413
- const nonceExpiry = now - 300 * 1e3;
473
+ const now$1 = Date.now();
474
+ this.sql.exec("DELETE FROM oauth_auth_codes WHERE expires_at < ?", now$1);
475
+ this.sql.exec("DELETE FROM oauth_tokens WHERE expires_at < ? AND revoked = 0", now$1);
476
+ this.sql.exec("DELETE FROM oauth_par_requests WHERE expires_at < ?", now$1);
477
+ const nonceExpiry = now$1 - 300 * 1e3;
414
478
  this.sql.exec("DELETE FROM oauth_nonces WHERE created_at < ?", nonceExpiry);
415
479
  }
416
480
  async saveAuthCode(code, data) {
@@ -586,7 +650,7 @@ var Sequencer = class {
586
650
  blobs: [],
587
651
  time
588
652
  };
589
- const payload = encode(eventPayload);
653
+ const payload = encode$1(eventPayload);
590
654
  const seq = this.sql.exec(`INSERT INTO firehose_events (event_type, payload)
591
655
  VALUES ('commit', ?)
592
656
  RETURNING seq`, payload).one().seq;
@@ -603,24 +667,46 @@ var Sequencer = class {
603
667
  /**
604
668
  * Get events from a cursor position.
605
669
  * Returns up to `limit` events after the cursor.
670
+ * Skips identity events that have empty payloads.
606
671
  */
607
672
  async getEventsSince(cursor, limit = 100) {
608
- return this.sql.exec(`SELECT seq, event_type, payload, created_at
673
+ const rows = this.sql.exec(`SELECT seq, event_type, payload, created_at
609
674
  FROM firehose_events
610
675
  WHERE seq > ?
611
676
  ORDER BY seq ASC
612
- LIMIT ?`, cursor, limit).toArray().map((row) => {
613
- const decoded = decode(new Uint8Array(row.payload));
614
- return {
615
- seq: row.seq,
616
- type: "commit",
617
- event: {
618
- ...decoded,
619
- seq: row.seq
620
- },
621
- time: row.created_at
622
- };
623
- });
677
+ LIMIT ?`, cursor, limit).toArray();
678
+ const events = [];
679
+ for (const row of rows) {
680
+ const eventType = row.event_type;
681
+ const payload = new Uint8Array(row.payload);
682
+ const seq = row.seq;
683
+ const time = row.created_at;
684
+ if (eventType === "identity") {
685
+ if (payload.length === 0) continue;
686
+ const decoded = decode$1(payload);
687
+ events.push({
688
+ seq,
689
+ type: "identity",
690
+ event: {
691
+ ...decoded,
692
+ seq
693
+ },
694
+ time
695
+ });
696
+ } else {
697
+ const decoded = decode$1(payload);
698
+ events.push({
699
+ seq,
700
+ type: "commit",
701
+ event: {
702
+ ...decoded,
703
+ seq
704
+ },
705
+ time
706
+ });
707
+ }
708
+ }
709
+ return events;
624
710
  }
625
711
  /**
626
712
  * Get the latest sequence number.
@@ -654,28 +740,28 @@ var BlobStore = class {
654
740
  * Upload a blob to R2 and return a BlobRef.
655
741
  */
656
742
  async putBlob(bytes, mimeType) {
657
- const cid = await cidForRawBytes(bytes);
658
- const key = `${this.did}/${cid.toString()}`;
743
+ const cidStr = toString(await create(CODEC_RAW, bytes));
744
+ const key = `${this.did}/${cidStr}`;
659
745
  await this.r2.put(key, bytes, { httpMetadata: { contentType: mimeType } });
660
746
  return {
661
747
  $type: "blob",
662
- ref: { $link: cid.toString() },
748
+ ref: { $link: cidStr },
663
749
  mimeType,
664
750
  size: bytes.length
665
751
  };
666
752
  }
667
753
  /**
668
- * Retrieve a blob from R2 by CID.
754
+ * Retrieve a blob from R2 by CID string.
669
755
  */
670
756
  async getBlob(cid) {
671
- const key = `${this.did}/${cid.toString()}`;
757
+ const key = `${this.did}/${cid}`;
672
758
  return this.r2.get(key);
673
759
  }
674
760
  /**
675
761
  * Check if a blob exists in R2.
676
762
  */
677
763
  async hasBlob(cid) {
678
- const key = `${this.did}/${cid.toString()}`;
764
+ const key = `${this.did}/${cid}`;
679
765
  return await this.r2.head(key) !== null;
680
766
  }
681
767
  };
@@ -820,7 +906,7 @@ var AccountDurableObject = class extends DurableObject {
820
906
  continue;
821
907
  }
822
908
  records.push({
823
- uri: AtUri.make(repo.did, record.collection, record.rkey).toString(),
909
+ uri: `at://${repo.did}/${record.collection}/${record.rkey}`,
824
910
  cid: record.cid.toString(),
825
911
  value: serializeRecord(record.record)
826
912
  });
@@ -841,7 +927,7 @@ var AccountDurableObject = class extends DurableObject {
841
927
  await this.ensureActive();
842
928
  const repo = await this.getRepo();
843
929
  const keypair = await this.getKeypair();
844
- const actualRkey = rkey || TID.nextStr();
930
+ const actualRkey = rkey || now();
845
931
  const createOp = {
846
932
  action: WriteOpAction.Create,
847
933
  collection,
@@ -877,7 +963,7 @@ var AccountDurableObject = class extends DurableObject {
877
963
  await this.broadcastCommit(event);
878
964
  }
879
965
  return {
880
- uri: AtUri.make(this.repo.did, collection, actualRkey).toString(),
966
+ uri: `at://${this.repo.did}/${collection}/${actualRkey}`,
881
967
  cid: recordCid.toString(),
882
968
  commit: {
883
969
  cid: this.repo.cid.toString(),
@@ -972,7 +1058,7 @@ var AccountDurableObject = class extends DurableObject {
972
1058
  await this.broadcastCommit(event);
973
1059
  }
974
1060
  return {
975
- uri: AtUri.make(this.repo.did, collection, rkey).toString(),
1061
+ uri: `at://${this.repo.did}/${collection}/${rkey}`,
976
1062
  cid: recordCid.toString(),
977
1063
  commit: {
978
1064
  cid: this.repo.cid.toString(),
@@ -991,7 +1077,7 @@ var AccountDurableObject = class extends DurableObject {
991
1077
  const ops = [];
992
1078
  const results = [];
993
1079
  for (const write of writes) if (write.$type === "com.atproto.repo.applyWrites#create") {
994
- const rkey = write.rkey || TID.nextStr();
1080
+ const rkey = write.rkey || now();
995
1081
  const op = {
996
1082
  action: WriteOpAction.Create,
997
1083
  collection: write.collection,
@@ -1050,7 +1136,7 @@ var AccountDurableObject = class extends DurableObject {
1050
1136
  const recordCid = await this.repo.data.get(dataKey);
1051
1137
  finalResults.push({
1052
1138
  $type: result.$type,
1053
- uri: AtUri.make(this.repo.did, result.collection, result.rkey).toString(),
1139
+ uri: `at://${this.repo.did}/${result.collection}/${result.rkey}`,
1054
1140
  cid: recordCid?.toString(),
1055
1141
  validationStatus: "valid"
1056
1142
  });
@@ -1145,7 +1231,7 @@ var AccountDurableObject = class extends DurableObject {
1145
1231
  this.repoInitialized = false;
1146
1232
  }
1147
1233
  const { root: rootCid, blocks } = await readCarWithRoot(carBytes);
1148
- const importRev = TID.nextStr();
1234
+ const importRev = now();
1149
1235
  await this.storage.putMany(blocks, importRev);
1150
1236
  this.keypair = await Secp256k1Keypair.import(this.env.SIGNING_KEY);
1151
1237
  this.repo = await Repo.load(this.storage, rootCid);
@@ -1158,7 +1244,7 @@ var AccountDurableObject = class extends DurableObject {
1158
1244
  for await (const record of this.repo.walkRecords()) {
1159
1245
  const blobCids = extractBlobCids(record.record);
1160
1246
  if (blobCids.length > 0) {
1161
- const uri = AtUri.make(this.repo.did, record.collection, record.rkey).toString();
1247
+ const uri = `at://${this.repo.did}/${record.collection}/${record.rkey}`;
1162
1248
  this.storage.addRecordBlobs(uri, blobCids);
1163
1249
  }
1164
1250
  }
@@ -1184,15 +1270,14 @@ var AccountDurableObject = class extends DurableObject {
1184
1270
  */
1185
1271
  async rpcGetBlob(cidStr) {
1186
1272
  if (!this.blobStore) throw new Error("Blob storage not configured");
1187
- const cid = CID.parse(cidStr);
1188
- return this.blobStore.getBlob(cid);
1273
+ return this.blobStore.getBlob(cidStr);
1189
1274
  }
1190
1275
  /**
1191
1276
  * Encode a firehose frame (header + body CBOR).
1192
1277
  */
1193
1278
  encodeFrame(header, body) {
1194
- const headerBytes = encode(header);
1195
- const bodyBytes = encode(body);
1279
+ const headerBytes = encode$1(header);
1280
+ const bodyBytes = encode$1(body);
1196
1281
  const frame = new Uint8Array(headerBytes.length + bodyBytes.length);
1197
1282
  frame.set(headerBytes, 0);
1198
1283
  frame.set(bodyBytes, headerBytes.length);
@@ -1208,6 +1293,22 @@ var AccountDurableObject = class extends DurableObject {
1208
1293
  }, event.event);
1209
1294
  }
1210
1295
  /**
1296
+ * Encode an identity event frame.
1297
+ */
1298
+ encodeIdentityFrame(event) {
1299
+ return this.encodeFrame({
1300
+ op: 1,
1301
+ t: "#identity"
1302
+ }, event.event);
1303
+ }
1304
+ /**
1305
+ * Encode any event frame based on its type.
1306
+ */
1307
+ encodeEventFrame(event) {
1308
+ if (event.type === "identity") return this.encodeIdentityFrame(event);
1309
+ return this.encodeCommitFrame(event);
1310
+ }
1311
+ /**
1211
1312
  * Encode an error frame.
1212
1313
  */
1213
1314
  encodeErrorFrame(error, message) {
@@ -1231,7 +1332,7 @@ var AccountDurableObject = class extends DurableObject {
1231
1332
  }
1232
1333
  const events = await this.sequencer.getEventsSince(cursor, 1e3);
1233
1334
  for (const event of events) {
1234
- const frame = this.encodeCommitFrame(event);
1335
+ const frame = this.encodeEventFrame(event);
1235
1336
  ws.send(frame);
1236
1337
  }
1237
1338
  if (events.length > 0) {
@@ -1247,7 +1348,7 @@ var AccountDurableObject = class extends DurableObject {
1247
1348
  * Broadcast a commit event to all connected firehose clients.
1248
1349
  */
1249
1350
  async broadcastCommit(event) {
1250
- const frame = this.encodeCommitFrame(event);
1351
+ const frame = this.encodeEventFrame(event);
1251
1352
  for (const ws of this.ctx.getWebSockets()) try {
1252
1353
  ws.send(frame);
1253
1354
  const attachment = ws.deserializeAttachment();
@@ -1393,8 +1494,8 @@ var AccountDurableObject = class extends DurableObject {
1393
1494
  time,
1394
1495
  handle
1395
1496
  };
1396
- const headerBytes = encode(header);
1397
- const bodyBytes = encode(body);
1497
+ const headerBytes = encode$1(header);
1498
+ const bodyBytes = encode$1(body);
1398
1499
  const frame = new Uint8Array(headerBytes.length + bodyBytes.length);
1399
1500
  frame.set(headerBytes, 0);
1400
1501
  frame.set(bodyBytes, headerBytes.length);
@@ -1405,6 +1506,25 @@ var AccountDurableObject = class extends DurableObject {
1405
1506
  }
1406
1507
  return { seq };
1407
1508
  }
1509
+ /**
1510
+ * RPC method: Health check - verifies storage is accessible
1511
+ */
1512
+ async rpcHealthCheck() {
1513
+ this.ctx.storage.sql.exec("SELECT 1").toArray();
1514
+ return { ok: true };
1515
+ }
1516
+ /**
1517
+ * RPC method: Firehose status - returns subscriber count and latest sequence
1518
+ */
1519
+ async rpcGetFirehoseStatus() {
1520
+ const sockets = this.ctx.getWebSockets();
1521
+ await this.ensureStorageInitialized();
1522
+ const seq = await (await this.getStorage()).getSeq();
1523
+ return {
1524
+ subscribers: sockets.length,
1525
+ latestSeq: seq || null
1526
+ };
1527
+ }
1408
1528
  /** Save an authorization code */
1409
1529
  async rpcSaveAuthCode(code, data) {
1410
1530
  await (await this.getOAuthStorage()).saveAuthCode(code, data);
@@ -1571,8 +1691,8 @@ async function verifyServiceJwt(token, signingKey, expectedAudience, expectedIss
1571
1691
  const header = JSON.parse(Buffer.from(headerB64, "base64url").toString());
1572
1692
  if (header.alg !== "ES256K") throw new Error(`Unsupported algorithm: ${header.alg}`);
1573
1693
  const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString());
1574
- const now = Math.floor(Date.now() / 1e3);
1575
- if (payload.exp && payload.exp < now) throw new Error("Token expired");
1694
+ const now$1 = Math.floor(Date.now() / 1e3);
1695
+ if (payload.exp && payload.exp < now$1) throw new Error("Token expired");
1576
1696
  if (payload.aud !== expectedAudience) throw new Error(`Invalid audience: expected ${expectedAudience}`);
1577
1697
  if (payload.iss !== expectedIssuer) throw new Error(`Invalid issuer: expected ${expectedIssuer}`);
1578
1698
  const keypair = await getSigningKeypair(signingKey);
@@ -1859,20 +1979,31 @@ async function requireAuth(c, next) {
1859
1979
  /**
1860
1980
  * DID resolution for Cloudflare Workers
1861
1981
  *
1862
- * We can't use @atproto/identity directly because it uses `redirect: "error"`
1863
- * which Cloudflare Workers doesn't support. This is a simple implementation
1864
- * that's compatible with Workers.
1982
+ * Uses @atcute/identity-resolver which is already Workers-compatible
1983
+ * (uses redirect: "manual" internally).
1865
1984
  */
1866
1985
  const PLC_DIRECTORY = "https://plc.directory";
1867
1986
  const TIMEOUT_MS = 3e3;
1987
+ /**
1988
+ * Wrapper that always uses globalThis.fetch so it can be mocked in tests.
1989
+ * @atcute resolvers capture the fetch reference at construction time,
1990
+ * so we need this indirection to allow test mocking.
1991
+ */
1992
+ const stubbableFetch = (input, init) => globalThis.fetch(input, init);
1868
1993
  var DidResolver = class {
1869
- plcUrl;
1994
+ resolver;
1870
1995
  timeout;
1871
1996
  cache;
1872
1997
  constructor(opts = {}) {
1873
- this.plcUrl = opts.plcUrl ?? PLC_DIRECTORY;
1874
1998
  this.timeout = opts.timeout ?? TIMEOUT_MS;
1875
1999
  this.cache = opts.didCache;
2000
+ this.resolver = new CompositeDidDocumentResolver({ methods: {
2001
+ plc: new PlcDidDocumentResolver({
2002
+ apiUrl: opts.plcUrl ?? PLC_DIRECTORY,
2003
+ fetch: stubbableFetch
2004
+ }),
2005
+ web: new WebDidDocumentResolver({ fetch: stubbableFetch })
2006
+ } });
1876
2007
  }
1877
2008
  async resolve(did) {
1878
2009
  if (this.cache) {
@@ -1888,61 +2019,25 @@ var DidResolver = class {
1888
2019
  return doc;
1889
2020
  }
1890
2021
  async resolveNoCache(did) {
1891
- if (did.startsWith("did:web:")) return this.resolveDidWeb(did);
1892
- if (did.startsWith("did:plc:")) return this.resolveDidPlc(did);
1893
- throw new Error(`Unsupported DID method: ${did}`);
1894
- }
1895
- async resolveDidWeb(did) {
1896
- const parts = did.split(":").slice(2);
1897
- if (parts.length === 0) throw new Error(`Invalid did:web format: ${did}`);
1898
- if (parts.length > 1) throw new Error(`Unsupported did:web with path: ${did}`);
1899
- const domain = decodeURIComponent(parts[0]);
1900
- const url = new URL(`https://${domain}/.well-known/did.json`);
1901
- if (url.hostname === "localhost") url.protocol = "http:";
1902
- const controller = new AbortController();
1903
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
1904
- try {
1905
- const res = await fetch(url.toString(), {
1906
- signal: controller.signal,
1907
- redirect: "manual",
1908
- headers: { accept: "application/did+ld+json,application/json" }
1909
- });
1910
- if (res.status >= 300 && res.status < 400) return null;
1911
- if (!res.ok) return null;
1912
- const doc = await res.json();
1913
- return this.validateDidDoc(did, doc);
1914
- } finally {
1915
- clearTimeout(timeoutId);
1916
- }
1917
- }
1918
- async resolveDidPlc(did) {
1919
- const url = new URL(`/${encodeURIComponent(did)}`, this.plcUrl);
1920
2022
  const controller = new AbortController();
1921
2023
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
1922
2024
  try {
1923
- const res = await fetch(url.toString(), {
1924
- signal: controller.signal,
1925
- redirect: "manual",
1926
- headers: { accept: "application/did+ld+json,application/json" }
1927
- });
1928
- if (res.status >= 300 && res.status < 400) return null;
1929
- if (res.status === 404) return null;
1930
- if (!res.ok) throw new Error(`PLC directory error: ${res.status} ${res.statusText}`);
1931
- const doc = await res.json();
1932
- return this.validateDidDoc(did, doc);
2025
+ const doc = await this.resolver.resolve(did, { signal: controller.signal });
2026
+ if (doc.id !== did) return null;
2027
+ return doc;
2028
+ } catch {
2029
+ return null;
1933
2030
  } finally {
1934
2031
  clearTimeout(timeoutId);
1935
2032
  }
1936
2033
  }
1937
- validateDidDoc(did, doc) {
1938
- if (!check.is(doc, didDocument)) return null;
1939
- if (doc.id !== did) return null;
1940
- return doc;
1941
- }
1942
2034
  };
1943
2035
 
1944
2036
  //#endregion
1945
2037
  //#region src/did-cache.ts
2038
+ /**
2039
+ * DID cache using Cloudflare Workers Cache API
2040
+ */
1946
2041
  const STALE_TTL = 3600 * 1e3;
1947
2042
  const MAX_TTL = 1440 * 60 * 1e3;
1948
2043
  var WorkersDidCache = class {
@@ -1968,14 +2063,14 @@ var WorkersDidCache = class {
1968
2063
  if (!response) return null;
1969
2064
  const cachedAt = parseInt(response.headers.get("X-Cached-At") || "0", 10);
1970
2065
  const age = Date.now() - cachedAt;
1971
- const doc = await response.json();
1972
- if (!check.is(doc, didDocument) || doc.id !== did) {
2066
+ const parsed = defs.didDocument.try(await response.json());
2067
+ if (!parsed.ok || parsed.value.id !== did) {
1973
2068
  await this.clearEntry(did);
1974
2069
  return null;
1975
2070
  }
1976
2071
  return {
1977
2072
  did,
1978
- doc,
2073
+ doc: parsed.value,
1979
2074
  updatedAt: cachedAt,
1980
2075
  stale: age > STALE_TTL,
1981
2076
  expired: age > MAX_TTL
@@ -2036,7 +2131,7 @@ async function handleXrpcProxy(c, didResolver$1, getKeypair$1) {
2036
2131
  error: "InvalidRequest",
2037
2132
  message: `DID not found: ${parsed.did}`
2038
2133
  }, 400);
2039
- const endpoint = getServiceEndpoint(didDoc, { id: parsed.serviceId.startsWith("#") ? parsed.serviceId : `#${parsed.serviceId}` });
2134
+ const endpoint = getAtprotoServiceEndpoint(didDoc, { id: parsed.serviceId.startsWith("#") ? parsed.serviceId : `#${parsed.serviceId}` });
2040
2135
  if (!endpoint) return c.json({
2041
2136
  error: "InvalidRequest",
2042
2137
  message: `Service not found in DID document: ${parsed.serviceId}`
@@ -2117,14 +2212,10 @@ async function getRepo(c, accountDO) {
2117
2212
  error: "InvalidRequest",
2118
2213
  message: "Missing required parameter: did"
2119
2214
  }, 400);
2120
- try {
2121
- ensureValidDid(did);
2122
- } catch (err) {
2123
- return c.json({
2124
- error: "InvalidRequest",
2125
- message: `Invalid DID format: ${err instanceof Error ? err.message : String(err)}`
2126
- }, 400);
2127
- }
2215
+ if (!isDid(did)) return c.json({
2216
+ error: "InvalidRequest",
2217
+ message: "Invalid DID format"
2218
+ }, 400);
2128
2219
  if (did !== c.env.DID) return c.json({
2129
2220
  error: "RepoNotFound",
2130
2221
  message: `Repository not found for DID: ${did}`
@@ -2144,14 +2235,10 @@ async function getRepoStatus(c, accountDO) {
2144
2235
  error: "InvalidRequest",
2145
2236
  message: "Missing required parameter: did"
2146
2237
  }, 400);
2147
- try {
2148
- ensureValidDid(did);
2149
- } catch (err) {
2150
- return c.json({
2151
- error: "InvalidRequest",
2152
- message: `Invalid DID format: ${err instanceof Error ? err.message : String(err)}`
2153
- }, 400);
2154
- }
2238
+ if (!isDid(did)) return c.json({
2239
+ error: "InvalidRequest",
2240
+ message: "Invalid DID format"
2241
+ }, 400);
2155
2242
  if (did !== c.env.DID) return c.json({
2156
2243
  error: "RepoNotFound",
2157
2244
  message: `Repository not found for DID: ${did}`
@@ -2179,14 +2266,10 @@ async function listBlobs(c, _accountDO) {
2179
2266
  error: "InvalidRequest",
2180
2267
  message: "Missing required parameter: did"
2181
2268
  }, 400);
2182
- try {
2183
- ensureValidDid(did);
2184
- } catch (err) {
2185
- return c.json({
2186
- error: "InvalidRequest",
2187
- message: `Invalid DID format: ${err instanceof Error ? err.message : String(err)}`
2188
- }, 400);
2189
- }
2269
+ if (!isDid(did)) return c.json({
2270
+ error: "InvalidRequest",
2271
+ message: "Invalid DID format"
2272
+ }, 400);
2190
2273
  if (did !== c.env.DID) return c.json({
2191
2274
  error: "RepoNotFound",
2192
2275
  message: `Repository not found for DID: ${did}`
@@ -2215,14 +2298,10 @@ async function getBlocks(c, accountDO) {
2215
2298
  error: "InvalidRequest",
2216
2299
  message: "Missing required parameter: cids"
2217
2300
  }, 400);
2218
- try {
2219
- ensureValidDid(did);
2220
- } catch (err) {
2221
- return c.json({
2222
- error: "InvalidRequest",
2223
- message: `Invalid DID format: ${err instanceof Error ? err.message : String(err)}`
2224
- }, 400);
2225
- }
2301
+ if (!isDid(did)) return c.json({
2302
+ error: "InvalidRequest",
2303
+ message: "Invalid DID format"
2304
+ }, 400);
2226
2305
  if (did !== c.env.DID) return c.json({
2227
2306
  error: "RepoNotFound",
2228
2307
  message: `Repository not found for DID: ${did}`
@@ -2243,14 +2322,10 @@ async function getBlob(c, _accountDO) {
2243
2322
  error: "InvalidRequest",
2244
2323
  message: "Missing required parameters: did, cid"
2245
2324
  }, 400);
2246
- try {
2247
- ensureValidDid(did);
2248
- } catch (err) {
2249
- return c.json({
2250
- error: "InvalidRequest",
2251
- message: `Invalid DID format: ${err instanceof Error ? err.message : String(err)}`
2252
- }, 400);
2253
- }
2325
+ if (!isDid(did)) return c.json({
2326
+ error: "InvalidRequest",
2327
+ message: "Invalid DID format"
2328
+ }, 400);
2254
2329
  if (did !== c.env.DID) return c.json({
2255
2330
  error: "RepoNotFound",
2256
2331
  message: `Repository not found for DID: ${did}`
@@ -2275,3292 +2350,80 @@ async function getBlob(c, _accountDO) {
2275
2350
  }
2276
2351
 
2277
2352
  //#endregion
2278
- //#region src/lexicons/app.bsky.actor.defs.json
2279
- var app_bsky_actor_defs_exports = /* @__PURE__ */ __exportAll({
2280
- default: () => app_bsky_actor_defs_default,
2281
- defs: () => defs$21,
2282
- id: () => id$21,
2283
- lexicon: () => lexicon$21
2284
- });
2285
- var lexicon$21 = 1;
2286
- var id$21 = "app.bsky.actor.defs";
2287
- var defs$21 = {
2288
- "profileViewBasic": {
2289
- "type": "object",
2290
- "required": ["did", "handle"],
2291
- "properties": {
2292
- "did": {
2293
- "type": "string",
2294
- "format": "did"
2295
- },
2296
- "handle": {
2297
- "type": "string",
2298
- "format": "handle"
2299
- },
2300
- "displayName": {
2301
- "type": "string",
2302
- "maxGraphemes": 64,
2303
- "maxLength": 640
2304
- },
2305
- "pronouns": { "type": "string" },
2306
- "avatar": {
2307
- "type": "string",
2308
- "format": "uri"
2309
- },
2310
- "associated": {
2311
- "type": "ref",
2312
- "ref": "#profileAssociated"
2313
- },
2314
- "viewer": {
2315
- "type": "ref",
2316
- "ref": "#viewerState"
2317
- },
2318
- "labels": {
2319
- "type": "array",
2320
- "items": {
2321
- "type": "ref",
2322
- "ref": "com.atproto.label.defs#label"
2323
- }
2324
- },
2325
- "createdAt": {
2326
- "type": "string",
2327
- "format": "datetime"
2328
- },
2329
- "verification": {
2330
- "type": "ref",
2331
- "ref": "#verificationState"
2332
- },
2333
- "status": {
2334
- "type": "ref",
2335
- "ref": "#statusView"
2336
- },
2337
- "debug": {
2338
- "type": "unknown",
2339
- "description": "Debug information for internal development"
2340
- }
2341
- }
2342
- },
2343
- "profileView": {
2344
- "type": "object",
2345
- "required": ["did", "handle"],
2346
- "properties": {
2347
- "did": {
2348
- "type": "string",
2349
- "format": "did"
2350
- },
2351
- "handle": {
2352
- "type": "string",
2353
- "format": "handle"
2354
- },
2355
- "displayName": {
2356
- "type": "string",
2357
- "maxGraphemes": 64,
2358
- "maxLength": 640
2359
- },
2360
- "pronouns": { "type": "string" },
2361
- "description": {
2362
- "type": "string",
2363
- "maxGraphemes": 256,
2364
- "maxLength": 2560
2365
- },
2366
- "avatar": {
2367
- "type": "string",
2368
- "format": "uri"
2369
- },
2370
- "associated": {
2371
- "type": "ref",
2372
- "ref": "#profileAssociated"
2373
- },
2374
- "indexedAt": {
2375
- "type": "string",
2376
- "format": "datetime"
2377
- },
2378
- "createdAt": {
2379
- "type": "string",
2380
- "format": "datetime"
2381
- },
2382
- "viewer": {
2383
- "type": "ref",
2384
- "ref": "#viewerState"
2385
- },
2386
- "labels": {
2387
- "type": "array",
2388
- "items": {
2389
- "type": "ref",
2390
- "ref": "com.atproto.label.defs#label"
2391
- }
2392
- },
2393
- "verification": {
2394
- "type": "ref",
2395
- "ref": "#verificationState"
2396
- },
2397
- "status": {
2398
- "type": "ref",
2399
- "ref": "#statusView"
2400
- },
2401
- "debug": {
2402
- "type": "unknown",
2403
- "description": "Debug information for internal development"
2404
- }
2405
- }
2406
- },
2407
- "profileViewDetailed": {
2408
- "type": "object",
2409
- "required": ["did", "handle"],
2410
- "properties": {
2411
- "did": {
2412
- "type": "string",
2413
- "format": "did"
2414
- },
2415
- "handle": {
2416
- "type": "string",
2417
- "format": "handle"
2418
- },
2419
- "displayName": {
2420
- "type": "string",
2421
- "maxGraphemes": 64,
2422
- "maxLength": 640
2423
- },
2424
- "description": {
2425
- "type": "string",
2426
- "maxGraphemes": 256,
2427
- "maxLength": 2560
2428
- },
2429
- "pronouns": { "type": "string" },
2430
- "website": {
2431
- "type": "string",
2432
- "format": "uri"
2433
- },
2434
- "avatar": {
2435
- "type": "string",
2436
- "format": "uri"
2437
- },
2438
- "banner": {
2439
- "type": "string",
2440
- "format": "uri"
2441
- },
2442
- "followersCount": { "type": "integer" },
2443
- "followsCount": { "type": "integer" },
2444
- "postsCount": { "type": "integer" },
2445
- "associated": {
2446
- "type": "ref",
2447
- "ref": "#profileAssociated"
2448
- },
2449
- "joinedViaStarterPack": {
2450
- "type": "ref",
2451
- "ref": "app.bsky.graph.defs#starterPackViewBasic"
2452
- },
2453
- "indexedAt": {
2454
- "type": "string",
2455
- "format": "datetime"
2456
- },
2457
- "createdAt": {
2458
- "type": "string",
2459
- "format": "datetime"
2460
- },
2461
- "viewer": {
2462
- "type": "ref",
2463
- "ref": "#viewerState"
2464
- },
2465
- "labels": {
2466
- "type": "array",
2467
- "items": {
2468
- "type": "ref",
2469
- "ref": "com.atproto.label.defs#label"
2470
- }
2471
- },
2472
- "pinnedPost": {
2473
- "type": "ref",
2474
- "ref": "com.atproto.repo.strongRef"
2475
- },
2476
- "verification": {
2477
- "type": "ref",
2478
- "ref": "#verificationState"
2479
- },
2480
- "status": {
2481
- "type": "ref",
2482
- "ref": "#statusView"
2483
- },
2484
- "debug": {
2485
- "type": "unknown",
2486
- "description": "Debug information for internal development"
2487
- }
2488
- }
2489
- },
2490
- "profileAssociated": {
2491
- "type": "object",
2492
- "properties": {
2493
- "lists": { "type": "integer" },
2494
- "feedgens": { "type": "integer" },
2495
- "starterPacks": { "type": "integer" },
2496
- "labeler": { "type": "boolean" },
2497
- "chat": {
2498
- "type": "ref",
2499
- "ref": "#profileAssociatedChat"
2500
- },
2501
- "activitySubscription": {
2502
- "type": "ref",
2503
- "ref": "#profileAssociatedActivitySubscription"
2504
- }
2505
- }
2506
- },
2507
- "profileAssociatedChat": {
2508
- "type": "object",
2509
- "required": ["allowIncoming"],
2510
- "properties": { "allowIncoming": {
2511
- "type": "string",
2512
- "knownValues": [
2513
- "all",
2514
- "none",
2515
- "following"
2516
- ]
2517
- } }
2518
- },
2519
- "profileAssociatedActivitySubscription": {
2520
- "type": "object",
2521
- "required": ["allowSubscriptions"],
2522
- "properties": { "allowSubscriptions": {
2523
- "type": "string",
2524
- "knownValues": [
2525
- "followers",
2526
- "mutuals",
2527
- "none"
2528
- ]
2529
- } }
2530
- },
2531
- "viewerState": {
2532
- "type": "object",
2533
- "description": "Metadata about the requesting account's relationship with the subject account. Only has meaningful content for authed requests.",
2534
- "properties": {
2535
- "muted": { "type": "boolean" },
2536
- "mutedByList": {
2537
- "type": "ref",
2538
- "ref": "app.bsky.graph.defs#listViewBasic"
2539
- },
2540
- "blockedBy": { "type": "boolean" },
2541
- "blocking": {
2542
- "type": "string",
2543
- "format": "at-uri"
2544
- },
2545
- "blockingByList": {
2546
- "type": "ref",
2547
- "ref": "app.bsky.graph.defs#listViewBasic"
2548
- },
2549
- "following": {
2550
- "type": "string",
2551
- "format": "at-uri"
2552
- },
2553
- "followedBy": {
2554
- "type": "string",
2555
- "format": "at-uri"
2556
- },
2557
- "knownFollowers": {
2558
- "description": "This property is present only in selected cases, as an optimization.",
2559
- "type": "ref",
2560
- "ref": "#knownFollowers"
2561
- },
2562
- "activitySubscription": {
2563
- "description": "This property is present only in selected cases, as an optimization.",
2564
- "type": "ref",
2565
- "ref": "app.bsky.notification.defs#activitySubscription"
2566
- }
2567
- }
2568
- },
2569
- "knownFollowers": {
2570
- "type": "object",
2571
- "description": "The subject's followers whom you also follow",
2572
- "required": ["count", "followers"],
2573
- "properties": {
2574
- "count": { "type": "integer" },
2575
- "followers": {
2576
- "type": "array",
2577
- "minLength": 0,
2578
- "maxLength": 5,
2579
- "items": {
2580
- "type": "ref",
2581
- "ref": "#profileViewBasic"
2582
- }
2583
- }
2584
- }
2585
- },
2586
- "verificationState": {
2587
- "type": "object",
2588
- "description": "Represents the verification information about the user this object is attached to.",
2589
- "required": [
2590
- "verifications",
2591
- "verifiedStatus",
2592
- "trustedVerifierStatus"
2593
- ],
2594
- "properties": {
2595
- "verifications": {
2596
- "type": "array",
2597
- "description": "All verifications issued by trusted verifiers on behalf of this user. Verifications by untrusted verifiers are not included.",
2598
- "items": {
2599
- "type": "ref",
2600
- "ref": "#verificationView"
2601
- }
2602
- },
2603
- "verifiedStatus": {
2604
- "type": "string",
2605
- "description": "The user's status as a verified account.",
2606
- "knownValues": [
2607
- "valid",
2608
- "invalid",
2609
- "none"
2610
- ]
2611
- },
2612
- "trustedVerifierStatus": {
2613
- "type": "string",
2614
- "description": "The user's status as a trusted verifier.",
2615
- "knownValues": [
2616
- "valid",
2617
- "invalid",
2618
- "none"
2619
- ]
2620
- }
2621
- }
2622
- },
2623
- "verificationView": {
2624
- "type": "object",
2625
- "description": "An individual verification for an associated subject.",
2626
- "required": [
2627
- "issuer",
2628
- "uri",
2629
- "isValid",
2630
- "createdAt"
2631
- ],
2632
- "properties": {
2633
- "issuer": {
2634
- "type": "string",
2635
- "description": "The user who issued this verification.",
2636
- "format": "did"
2637
- },
2638
- "uri": {
2639
- "type": "string",
2640
- "description": "The AT-URI of the verification record.",
2641
- "format": "at-uri"
2642
- },
2643
- "isValid": {
2644
- "type": "boolean",
2645
- "description": "True if the verification passes validation, otherwise false."
2646
- },
2647
- "createdAt": {
2648
- "type": "string",
2649
- "description": "Timestamp when the verification was created.",
2650
- "format": "datetime"
2651
- }
2652
- }
2653
- },
2654
- "preferences": {
2655
- "type": "array",
2656
- "items": {
2657
- "type": "union",
2658
- "refs": [
2659
- "#adultContentPref",
2660
- "#contentLabelPref",
2661
- "#savedFeedsPref",
2662
- "#savedFeedsPrefV2",
2663
- "#personalDetailsPref",
2664
- "#declaredAgePref",
2665
- "#feedViewPref",
2666
- "#threadViewPref",
2667
- "#interestsPref",
2668
- "#mutedWordsPref",
2669
- "#hiddenPostsPref",
2670
- "#bskyAppStatePref",
2671
- "#labelersPref",
2672
- "#postInteractionSettingsPref",
2673
- "#verificationPrefs"
2674
- ]
2675
- }
2676
- },
2677
- "adultContentPref": {
2678
- "type": "object",
2679
- "required": ["enabled"],
2680
- "properties": { "enabled": {
2681
- "type": "boolean",
2682
- "default": false
2683
- } }
2684
- },
2685
- "contentLabelPref": {
2686
- "type": "object",
2687
- "required": ["label", "visibility"],
2688
- "properties": {
2689
- "labelerDid": {
2690
- "type": "string",
2691
- "description": "Which labeler does this preference apply to? If undefined, applies globally.",
2692
- "format": "did"
2693
- },
2694
- "label": { "type": "string" },
2695
- "visibility": {
2696
- "type": "string",
2697
- "knownValues": [
2698
- "ignore",
2699
- "show",
2700
- "warn",
2701
- "hide"
2702
- ]
2703
- }
2704
- }
2705
- },
2706
- "savedFeed": {
2707
- "type": "object",
2708
- "required": [
2709
- "id",
2710
- "type",
2711
- "value",
2712
- "pinned"
2713
- ],
2714
- "properties": {
2715
- "id": { "type": "string" },
2716
- "type": {
2717
- "type": "string",
2718
- "knownValues": [
2719
- "feed",
2720
- "list",
2721
- "timeline"
2722
- ]
2723
- },
2724
- "value": { "type": "string" },
2725
- "pinned": { "type": "boolean" }
2726
- }
2727
- },
2728
- "savedFeedsPrefV2": {
2729
- "type": "object",
2730
- "required": ["items"],
2731
- "properties": { "items": {
2732
- "type": "array",
2733
- "items": {
2734
- "type": "ref",
2735
- "ref": "app.bsky.actor.defs#savedFeed"
2736
- }
2737
- } }
2738
- },
2739
- "savedFeedsPref": {
2740
- "type": "object",
2741
- "required": ["pinned", "saved"],
2742
- "properties": {
2743
- "pinned": {
2744
- "type": "array",
2745
- "items": {
2746
- "type": "string",
2747
- "format": "at-uri"
2748
- }
2749
- },
2750
- "saved": {
2751
- "type": "array",
2752
- "items": {
2753
- "type": "string",
2754
- "format": "at-uri"
2755
- }
2756
- },
2757
- "timelineIndex": { "type": "integer" }
2758
- }
2759
- },
2760
- "personalDetailsPref": {
2761
- "type": "object",
2762
- "properties": { "birthDate": {
2763
- "type": "string",
2764
- "format": "datetime",
2765
- "description": "The birth date of account owner."
2766
- } }
2767
- },
2768
- "declaredAgePref": {
2769
- "type": "object",
2770
- "description": "Read-only preference containing value(s) inferred from the user's declared birthdate. Absence of this preference object in the response indicates that the user has not made a declaration.",
2771
- "properties": {
2772
- "isOverAge13": {
2773
- "type": "boolean",
2774
- "description": "Indicates if the user has declared that they are over 13 years of age."
2775
- },
2776
- "isOverAge16": {
2777
- "type": "boolean",
2778
- "description": "Indicates if the user has declared that they are over 16 years of age."
2779
- },
2780
- "isOverAge18": {
2781
- "type": "boolean",
2782
- "description": "Indicates if the user has declared that they are over 18 years of age."
2783
- }
2784
- }
2785
- },
2786
- "feedViewPref": {
2787
- "type": "object",
2788
- "required": ["feed"],
2789
- "properties": {
2790
- "feed": {
2791
- "type": "string",
2792
- "description": "The URI of the feed, or an identifier which describes the feed."
2793
- },
2794
- "hideReplies": {
2795
- "type": "boolean",
2796
- "description": "Hide replies in the feed."
2797
- },
2798
- "hideRepliesByUnfollowed": {
2799
- "type": "boolean",
2800
- "description": "Hide replies in the feed if they are not by followed users.",
2801
- "default": true
2802
- },
2803
- "hideRepliesByLikeCount": {
2804
- "type": "integer",
2805
- "description": "Hide replies in the feed if they do not have this number of likes."
2806
- },
2807
- "hideReposts": {
2808
- "type": "boolean",
2809
- "description": "Hide reposts in the feed."
2810
- },
2811
- "hideQuotePosts": {
2812
- "type": "boolean",
2813
- "description": "Hide quote posts in the feed."
2814
- }
2815
- }
2816
- },
2817
- "threadViewPref": {
2818
- "type": "object",
2819
- "properties": { "sort": {
2820
- "type": "string",
2821
- "description": "Sorting mode for threads.",
2822
- "knownValues": [
2823
- "oldest",
2824
- "newest",
2825
- "most-likes",
2826
- "random",
2827
- "hotness"
2828
- ]
2829
- } }
2830
- },
2831
- "interestsPref": {
2832
- "type": "object",
2833
- "required": ["tags"],
2834
- "properties": { "tags": {
2835
- "type": "array",
2836
- "maxLength": 100,
2837
- "items": {
2838
- "type": "string",
2839
- "maxLength": 640,
2840
- "maxGraphemes": 64
2841
- },
2842
- "description": "A list of tags which describe the account owner's interests gathered during onboarding."
2843
- } }
2844
- },
2845
- "mutedWordTarget": {
2846
- "type": "string",
2847
- "knownValues": ["content", "tag"],
2848
- "maxLength": 640,
2849
- "maxGraphemes": 64
2850
- },
2851
- "mutedWord": {
2852
- "type": "object",
2853
- "description": "A word that the account owner has muted.",
2854
- "required": ["value", "targets"],
2855
- "properties": {
2856
- "id": { "type": "string" },
2857
- "value": {
2858
- "type": "string",
2859
- "description": "The muted word itself.",
2860
- "maxLength": 1e4,
2861
- "maxGraphemes": 1e3
2862
- },
2863
- "targets": {
2864
- "type": "array",
2865
- "description": "The intended targets of the muted word.",
2866
- "items": {
2867
- "type": "ref",
2868
- "ref": "app.bsky.actor.defs#mutedWordTarget"
2869
- }
2870
- },
2871
- "actorTarget": {
2872
- "type": "string",
2873
- "description": "Groups of users to apply the muted word to. If undefined, applies to all users.",
2874
- "knownValues": ["all", "exclude-following"],
2875
- "default": "all"
2876
- },
2877
- "expiresAt": {
2878
- "type": "string",
2879
- "format": "datetime",
2880
- "description": "The date and time at which the muted word will expire and no longer be applied."
2881
- }
2353
+ //#region src/validation.ts
2354
+ /**
2355
+ * Map of collection NSID to validation schema.
2356
+ * Only includes record types that can be created in repositories.
2357
+ */
2358
+ const recordSchemas = {
2359
+ "app.bsky.actor.profile": AppBskyActorProfile.mainSchema,
2360
+ "app.bsky.feed.generator": AppBskyFeedGenerator.mainSchema,
2361
+ "app.bsky.feed.like": AppBskyFeedLike.mainSchema,
2362
+ "app.bsky.feed.post": AppBskyFeedPost.mainSchema,
2363
+ "app.bsky.feed.postgate": AppBskyFeedPostgate.mainSchema,
2364
+ "app.bsky.feed.repost": AppBskyFeedRepost.mainSchema,
2365
+ "app.bsky.feed.threadgate": AppBskyFeedThreadgate.mainSchema,
2366
+ "app.bsky.graph.block": AppBskyGraphBlock.mainSchema,
2367
+ "app.bsky.graph.follow": AppBskyGraphFollow.mainSchema,
2368
+ "app.bsky.graph.list": AppBskyGraphList.mainSchema,
2369
+ "app.bsky.graph.listblock": AppBskyGraphListblock.mainSchema,
2370
+ "app.bsky.graph.listitem": AppBskyGraphListitem.mainSchema,
2371
+ "app.bsky.graph.starterpack": AppBskyGraphStarterpack.mainSchema,
2372
+ "app.bsky.graph.verification": AppBskyGraphVerification.mainSchema,
2373
+ "app.bsky.labeler.service": AppBskyLabelerService.mainSchema
2374
+ };
2375
+ /**
2376
+ * Record validator for AT Protocol records.
2377
+ *
2378
+ * Validates records against official Bluesky lexicon schemas from @atcute/bluesky.
2379
+ * Uses optimistic validation strategy:
2380
+ * - If a schema is loaded for the collection, validate the record
2381
+ * - If no schema is loaded, allow the record (fail-open)
2382
+ *
2383
+ * This allows the PDS to accept records for new or unknown collection types
2384
+ * while still validating known types.
2385
+ */
2386
+ var RecordValidator = class {
2387
+ strictMode;
2388
+ constructor(options = {}) {
2389
+ this.strictMode = options.strict ?? false;
2390
+ }
2391
+ /**
2392
+ * Validate a record against its lexicon schema.
2393
+ *
2394
+ * @param collection - The NSID of the record type (e.g., "app.bsky.feed.post")
2395
+ * @param record - The record object to validate
2396
+ * @throws {Error} If validation fails and schema is loaded
2397
+ */
2398
+ validateRecord(collection, record) {
2399
+ const schema = recordSchemas[collection];
2400
+ if (!schema) {
2401
+ if (this.strictMode) throw new Error(`No lexicon schema loaded for collection: ${collection}. Enable optimistic validation or add the schema.`);
2402
+ return;
2882
2403
  }
2883
- },
2884
- "mutedWordsPref": {
2885
- "type": "object",
2886
- "required": ["items"],
2887
- "properties": { "items": {
2888
- "type": "array",
2889
- "items": {
2890
- "type": "ref",
2891
- "ref": "app.bsky.actor.defs#mutedWord"
2892
- },
2893
- "description": "A list of words the account owner has muted."
2894
- } }
2895
- },
2896
- "hiddenPostsPref": {
2897
- "type": "object",
2898
- "required": ["items"],
2899
- "properties": { "items": {
2900
- "type": "array",
2901
- "items": {
2902
- "type": "string",
2903
- "format": "at-uri"
2904
- },
2905
- "description": "A list of URIs of posts the account owner has hidden."
2906
- } }
2907
- },
2908
- "labelersPref": {
2909
- "type": "object",
2910
- "required": ["labelers"],
2911
- "properties": { "labelers": {
2912
- "type": "array",
2913
- "items": {
2914
- "type": "ref",
2915
- "ref": "#labelerPrefItem"
2916
- }
2917
- } }
2918
- },
2919
- "labelerPrefItem": {
2920
- "type": "object",
2921
- "required": ["did"],
2922
- "properties": { "did": {
2923
- "type": "string",
2924
- "format": "did"
2925
- } }
2926
- },
2927
- "bskyAppStatePref": {
2928
- "description": "A grab bag of state that's specific to the bsky.app program. Third-party apps shouldn't use this.",
2929
- "type": "object",
2930
- "properties": {
2931
- "activeProgressGuide": {
2932
- "type": "ref",
2933
- "ref": "#bskyAppProgressGuide"
2934
- },
2935
- "queuedNudges": {
2936
- "description": "An array of tokens which identify nudges (modals, popups, tours, highlight dots) that should be shown to the user.",
2937
- "type": "array",
2938
- "maxLength": 1e3,
2939
- "items": {
2940
- "type": "string",
2941
- "maxLength": 100
2942
- }
2943
- },
2944
- "nuxs": {
2945
- "description": "Storage for NUXs the user has encountered.",
2946
- "type": "array",
2947
- "maxLength": 100,
2948
- "items": {
2949
- "type": "ref",
2950
- "ref": "app.bsky.actor.defs#nux"
2951
- }
2952
- }
2953
- }
2954
- },
2955
- "bskyAppProgressGuide": {
2956
- "description": "If set, an active progress guide. Once completed, can be set to undefined. Should have unspecced fields tracking progress.",
2957
- "type": "object",
2958
- "required": ["guide"],
2959
- "properties": { "guide": {
2960
- "type": "string",
2961
- "maxLength": 100
2962
- } }
2963
- },
2964
- "nux": {
2965
- "type": "object",
2966
- "description": "A new user experiences (NUX) storage object",
2967
- "required": ["id", "completed"],
2968
- "properties": {
2969
- "id": {
2970
- "type": "string",
2971
- "maxLength": 100
2972
- },
2973
- "completed": {
2974
- "type": "boolean",
2975
- "default": false
2976
- },
2977
- "data": {
2978
- "description": "Arbitrary data for the NUX. The structure is defined by the NUX itself. Limited to 300 characters.",
2979
- "type": "string",
2980
- "maxLength": 3e3,
2981
- "maxGraphemes": 300
2982
- },
2983
- "expiresAt": {
2984
- "type": "string",
2985
- "format": "datetime",
2986
- "description": "The date and time at which the NUX will expire and should be considered completed."
2987
- }
2988
- }
2989
- },
2990
- "verificationPrefs": {
2991
- "type": "object",
2992
- "description": "Preferences for how verified accounts appear in the app.",
2993
- "required": [],
2994
- "properties": { "hideBadges": {
2995
- "description": "Hide the blue check badges for verified accounts and trusted verifiers.",
2996
- "type": "boolean",
2997
- "default": false
2998
- } }
2999
- },
3000
- "postInteractionSettingsPref": {
3001
- "type": "object",
3002
- "description": "Default post interaction settings for the account. These values should be applied as default values when creating new posts. These refs should mirror the threadgate and postgate records exactly.",
3003
- "required": [],
3004
- "properties": {
3005
- "threadgateAllowRules": {
3006
- "description": "Matches threadgate record. List of rules defining who can reply to this users posts. If value is an empty array, no one can reply. If value is undefined, anyone can reply.",
3007
- "type": "array",
3008
- "maxLength": 5,
3009
- "items": {
3010
- "type": "union",
3011
- "refs": [
3012
- "app.bsky.feed.threadgate#mentionRule",
3013
- "app.bsky.feed.threadgate#followerRule",
3014
- "app.bsky.feed.threadgate#followingRule",
3015
- "app.bsky.feed.threadgate#listRule"
3016
- ]
3017
- }
3018
- },
3019
- "postgateEmbeddingRules": {
3020
- "description": "Matches postgate record. List of rules defining who can embed this users posts. If value is an empty array or is undefined, no particular rules apply and anyone can embed.",
3021
- "type": "array",
3022
- "maxLength": 5,
3023
- "items": {
3024
- "type": "union",
3025
- "refs": ["app.bsky.feed.postgate#disableRule"]
3026
- }
3027
- }
3028
- }
3029
- },
3030
- "statusView": {
3031
- "type": "object",
3032
- "required": ["status", "record"],
3033
- "properties": {
3034
- "status": {
3035
- "type": "string",
3036
- "description": "The status for the account.",
3037
- "knownValues": ["app.bsky.actor.status#live"]
3038
- },
3039
- "record": { "type": "unknown" },
3040
- "embed": {
3041
- "type": "union",
3042
- "description": "An optional embed associated with the status.",
3043
- "refs": ["app.bsky.embed.external#view"]
3044
- },
3045
- "expiresAt": {
3046
- "type": "string",
3047
- "description": "The date when this status will expire. The application might choose to no longer return the status after expiration.",
3048
- "format": "datetime"
3049
- },
3050
- "isActive": {
3051
- "type": "boolean",
3052
- "description": "True if the status is not expired, false if it is expired. Only present if expiration was set."
3053
- }
3054
- }
3055
- }
3056
- };
3057
- var app_bsky_actor_defs_default = {
3058
- lexicon: lexicon$21,
3059
- id: id$21,
3060
- defs: defs$21
3061
- };
3062
-
3063
- //#endregion
3064
- //#region src/lexicons/app.bsky.actor.profile.json
3065
- var app_bsky_actor_profile_exports = /* @__PURE__ */ __exportAll({
3066
- default: () => app_bsky_actor_profile_default,
3067
- defs: () => defs$20,
3068
- id: () => id$20,
3069
- lexicon: () => lexicon$20
3070
- });
3071
- var lexicon$20 = 1;
3072
- var id$20 = "app.bsky.actor.profile";
3073
- var defs$20 = { "main": {
3074
- "type": "record",
3075
- "description": "A declaration of a Bluesky account profile.",
3076
- "key": "literal:self",
3077
- "record": {
3078
- "type": "object",
3079
- "properties": {
3080
- "displayName": {
3081
- "type": "string",
3082
- "maxGraphemes": 64,
3083
- "maxLength": 640
3084
- },
3085
- "description": {
3086
- "type": "string",
3087
- "description": "Free-form profile description text.",
3088
- "maxGraphemes": 256,
3089
- "maxLength": 2560
3090
- },
3091
- "pronouns": {
3092
- "type": "string",
3093
- "description": "Free-form pronouns text.",
3094
- "maxGraphemes": 20,
3095
- "maxLength": 200
3096
- },
3097
- "website": {
3098
- "type": "string",
3099
- "format": "uri"
3100
- },
3101
- "avatar": {
3102
- "type": "blob",
3103
- "description": "Small image to be displayed next to posts from account. AKA, 'profile picture'",
3104
- "accept": ["image/png", "image/jpeg"],
3105
- "maxSize": 1e6
3106
- },
3107
- "banner": {
3108
- "type": "blob",
3109
- "description": "Larger horizontal image to display behind profile view.",
3110
- "accept": ["image/png", "image/jpeg"],
3111
- "maxSize": 1e6
3112
- },
3113
- "labels": {
3114
- "type": "union",
3115
- "description": "Self-label values, specific to the Bluesky application, on the overall account.",
3116
- "refs": ["com.atproto.label.defs#selfLabels"]
3117
- },
3118
- "joinedViaStarterPack": {
3119
- "type": "ref",
3120
- "ref": "com.atproto.repo.strongRef"
3121
- },
3122
- "pinnedPost": {
3123
- "type": "ref",
3124
- "ref": "com.atproto.repo.strongRef"
3125
- },
3126
- "createdAt": {
3127
- "type": "string",
3128
- "format": "datetime"
3129
- }
3130
- }
3131
- }
3132
- } };
3133
- var app_bsky_actor_profile_default = {
3134
- lexicon: lexicon$20,
3135
- id: id$20,
3136
- defs: defs$20
3137
- };
3138
-
3139
- //#endregion
3140
- //#region src/lexicons/app.bsky.embed.defs.json
3141
- var app_bsky_embed_defs_exports = /* @__PURE__ */ __exportAll({
3142
- default: () => app_bsky_embed_defs_default,
3143
- defs: () => defs$19,
3144
- id: () => id$19,
3145
- lexicon: () => lexicon$19
3146
- });
3147
- var lexicon$19 = 1;
3148
- var id$19 = "app.bsky.embed.defs";
3149
- var defs$19 = { "aspectRatio": {
3150
- "type": "object",
3151
- "description": "width:height represents an aspect ratio. It may be approximate, and may not correspond to absolute dimensions in any given unit.",
3152
- "required": ["width", "height"],
3153
- "properties": {
3154
- "width": {
3155
- "type": "integer",
3156
- "minimum": 1
3157
- },
3158
- "height": {
3159
- "type": "integer",
3160
- "minimum": 1
3161
- }
3162
- }
3163
- } };
3164
- var app_bsky_embed_defs_default = {
3165
- lexicon: lexicon$19,
3166
- id: id$19,
3167
- defs: defs$19
3168
- };
3169
-
3170
- //#endregion
3171
- //#region src/lexicons/app.bsky.embed.external.json
3172
- var app_bsky_embed_external_exports = /* @__PURE__ */ __exportAll({
3173
- default: () => app_bsky_embed_external_default,
3174
- defs: () => defs$18,
3175
- id: () => id$18,
3176
- lexicon: () => lexicon$18
3177
- });
3178
- var lexicon$18 = 1;
3179
- var id$18 = "app.bsky.embed.external";
3180
- var defs$18 = {
3181
- "main": {
3182
- "type": "object",
3183
- "description": "A representation of some externally linked content (eg, a URL and 'card'), embedded in a Bluesky record (eg, a post).",
3184
- "required": ["external"],
3185
- "properties": { "external": {
3186
- "type": "ref",
3187
- "ref": "#external"
3188
- } }
3189
- },
3190
- "external": {
3191
- "type": "object",
3192
- "required": [
3193
- "uri",
3194
- "title",
3195
- "description"
3196
- ],
3197
- "properties": {
3198
- "uri": {
3199
- "type": "string",
3200
- "format": "uri"
3201
- },
3202
- "title": { "type": "string" },
3203
- "description": { "type": "string" },
3204
- "thumb": {
3205
- "type": "blob",
3206
- "accept": ["image/*"],
3207
- "maxSize": 1e6
3208
- }
3209
- }
3210
- },
3211
- "view": {
3212
- "type": "object",
3213
- "required": ["external"],
3214
- "properties": { "external": {
3215
- "type": "ref",
3216
- "ref": "#viewExternal"
3217
- } }
3218
- },
3219
- "viewExternal": {
3220
- "type": "object",
3221
- "required": [
3222
- "uri",
3223
- "title",
3224
- "description"
3225
- ],
3226
- "properties": {
3227
- "uri": {
3228
- "type": "string",
3229
- "format": "uri"
3230
- },
3231
- "title": { "type": "string" },
3232
- "description": { "type": "string" },
3233
- "thumb": {
3234
- "type": "string",
3235
- "format": "uri"
3236
- }
3237
- }
3238
- }
3239
- };
3240
- var app_bsky_embed_external_default = {
3241
- lexicon: lexicon$18,
3242
- id: id$18,
3243
- defs: defs$18
3244
- };
3245
-
3246
- //#endregion
3247
- //#region src/lexicons/app.bsky.embed.images.json
3248
- var app_bsky_embed_images_exports = /* @__PURE__ */ __exportAll({
3249
- default: () => app_bsky_embed_images_default,
3250
- defs: () => defs$17,
3251
- description: () => description$4,
3252
- id: () => id$17,
3253
- lexicon: () => lexicon$17
3254
- });
3255
- var lexicon$17 = 1;
3256
- var id$17 = "app.bsky.embed.images";
3257
- var description$4 = "A set of images embedded in a Bluesky record (eg, a post).";
3258
- var defs$17 = {
3259
- "main": {
3260
- "type": "object",
3261
- "required": ["images"],
3262
- "properties": { "images": {
3263
- "type": "array",
3264
- "items": {
3265
- "type": "ref",
3266
- "ref": "#image"
3267
- },
3268
- "maxLength": 4
3269
- } }
3270
- },
3271
- "image": {
3272
- "type": "object",
3273
- "required": ["image", "alt"],
3274
- "properties": {
3275
- "image": {
3276
- "type": "blob",
3277
- "accept": ["image/*"],
3278
- "maxSize": 1e6
3279
- },
3280
- "alt": {
3281
- "type": "string",
3282
- "description": "Alt text description of the image, for accessibility."
3283
- },
3284
- "aspectRatio": {
3285
- "type": "ref",
3286
- "ref": "app.bsky.embed.defs#aspectRatio"
3287
- }
3288
- }
3289
- },
3290
- "view": {
3291
- "type": "object",
3292
- "required": ["images"],
3293
- "properties": { "images": {
3294
- "type": "array",
3295
- "items": {
3296
- "type": "ref",
3297
- "ref": "#viewImage"
3298
- },
3299
- "maxLength": 4
3300
- } }
3301
- },
3302
- "viewImage": {
3303
- "type": "object",
3304
- "required": [
3305
- "thumb",
3306
- "fullsize",
3307
- "alt"
3308
- ],
3309
- "properties": {
3310
- "thumb": {
3311
- "type": "string",
3312
- "format": "uri",
3313
- "description": "Fully-qualified URL where a thumbnail of the image can be fetched. For example, CDN location provided by the App View."
3314
- },
3315
- "fullsize": {
3316
- "type": "string",
3317
- "format": "uri",
3318
- "description": "Fully-qualified URL where a large version of the image can be fetched. May or may not be the exact original blob. For example, CDN location provided by the App View."
3319
- },
3320
- "alt": {
3321
- "type": "string",
3322
- "description": "Alt text description of the image, for accessibility."
3323
- },
3324
- "aspectRatio": {
3325
- "type": "ref",
3326
- "ref": "app.bsky.embed.defs#aspectRatio"
3327
- }
3328
- }
3329
- }
3330
- };
3331
- var app_bsky_embed_images_default = {
3332
- lexicon: lexicon$17,
3333
- id: id$17,
3334
- description: description$4,
3335
- defs: defs$17
3336
- };
3337
-
3338
- //#endregion
3339
- //#region src/lexicons/app.bsky.embed.record.json
3340
- var app_bsky_embed_record_exports = /* @__PURE__ */ __exportAll({
3341
- default: () => app_bsky_embed_record_default,
3342
- defs: () => defs$16,
3343
- description: () => description$3,
3344
- id: () => id$16,
3345
- lexicon: () => lexicon$16
3346
- });
3347
- var lexicon$16 = 1;
3348
- var id$16 = "app.bsky.embed.record";
3349
- var description$3 = "A representation of a record embedded in a Bluesky record (eg, a post). For example, a quote-post, or sharing a feed generator record.";
3350
- var defs$16 = {
3351
- "main": {
3352
- "type": "object",
3353
- "required": ["record"],
3354
- "properties": { "record": {
3355
- "type": "ref",
3356
- "ref": "com.atproto.repo.strongRef"
3357
- } }
3358
- },
3359
- "view": {
3360
- "type": "object",
3361
- "required": ["record"],
3362
- "properties": { "record": {
3363
- "type": "union",
3364
- "refs": [
3365
- "#viewRecord",
3366
- "#viewNotFound",
3367
- "#viewBlocked",
3368
- "#viewDetached",
3369
- "app.bsky.feed.defs#generatorView",
3370
- "app.bsky.graph.defs#listView",
3371
- "app.bsky.labeler.defs#labelerView",
3372
- "app.bsky.graph.defs#starterPackViewBasic"
3373
- ]
3374
- } }
3375
- },
3376
- "viewRecord": {
3377
- "type": "object",
3378
- "required": [
3379
- "uri",
3380
- "cid",
3381
- "author",
3382
- "value",
3383
- "indexedAt"
3384
- ],
3385
- "properties": {
3386
- "uri": {
3387
- "type": "string",
3388
- "format": "at-uri"
3389
- },
3390
- "cid": {
3391
- "type": "string",
3392
- "format": "cid"
3393
- },
3394
- "author": {
3395
- "type": "ref",
3396
- "ref": "app.bsky.actor.defs#profileViewBasic"
3397
- },
3398
- "value": {
3399
- "type": "unknown",
3400
- "description": "The record data itself."
3401
- },
3402
- "labels": {
3403
- "type": "array",
3404
- "items": {
3405
- "type": "ref",
3406
- "ref": "com.atproto.label.defs#label"
3407
- }
3408
- },
3409
- "replyCount": { "type": "integer" },
3410
- "repostCount": { "type": "integer" },
3411
- "likeCount": { "type": "integer" },
3412
- "quoteCount": { "type": "integer" },
3413
- "embeds": {
3414
- "type": "array",
3415
- "items": {
3416
- "type": "union",
3417
- "refs": [
3418
- "app.bsky.embed.images#view",
3419
- "app.bsky.embed.video#view",
3420
- "app.bsky.embed.external#view",
3421
- "app.bsky.embed.record#view",
3422
- "app.bsky.embed.recordWithMedia#view"
3423
- ]
3424
- }
3425
- },
3426
- "indexedAt": {
3427
- "type": "string",
3428
- "format": "datetime"
3429
- }
3430
- }
3431
- },
3432
- "viewNotFound": {
3433
- "type": "object",
3434
- "required": ["uri", "notFound"],
3435
- "properties": {
3436
- "uri": {
3437
- "type": "string",
3438
- "format": "at-uri"
3439
- },
3440
- "notFound": {
3441
- "type": "boolean",
3442
- "const": true
3443
- }
3444
- }
3445
- },
3446
- "viewBlocked": {
3447
- "type": "object",
3448
- "required": [
3449
- "uri",
3450
- "blocked",
3451
- "author"
3452
- ],
3453
- "properties": {
3454
- "uri": {
3455
- "type": "string",
3456
- "format": "at-uri"
3457
- },
3458
- "blocked": {
3459
- "type": "boolean",
3460
- "const": true
3461
- },
3462
- "author": {
3463
- "type": "ref",
3464
- "ref": "app.bsky.feed.defs#blockedAuthor"
3465
- }
3466
- }
3467
- },
3468
- "viewDetached": {
3469
- "type": "object",
3470
- "required": ["uri", "detached"],
3471
- "properties": {
3472
- "uri": {
3473
- "type": "string",
3474
- "format": "at-uri"
3475
- },
3476
- "detached": {
3477
- "type": "boolean",
3478
- "const": true
3479
- }
3480
- }
3481
- }
3482
- };
3483
- var app_bsky_embed_record_default = {
3484
- lexicon: lexicon$16,
3485
- id: id$16,
3486
- description: description$3,
3487
- defs: defs$16
3488
- };
3489
-
3490
- //#endregion
3491
- //#region src/lexicons/app.bsky.embed.recordWithMedia.json
3492
- var app_bsky_embed_recordWithMedia_exports = /* @__PURE__ */ __exportAll({
3493
- default: () => app_bsky_embed_recordWithMedia_default,
3494
- defs: () => defs$15,
3495
- description: () => description$2,
3496
- id: () => id$15,
3497
- lexicon: () => lexicon$15
3498
- });
3499
- var lexicon$15 = 1;
3500
- var id$15 = "app.bsky.embed.recordWithMedia";
3501
- var description$2 = "A representation of a record embedded in a Bluesky record (eg, a post), alongside other compatible embeds. For example, a quote post and image, or a quote post and external URL card.";
3502
- var defs$15 = {
3503
- "main": {
3504
- "type": "object",
3505
- "required": ["record", "media"],
3506
- "properties": {
3507
- "record": {
3508
- "type": "ref",
3509
- "ref": "app.bsky.embed.record"
3510
- },
3511
- "media": {
3512
- "type": "union",
3513
- "refs": [
3514
- "app.bsky.embed.images",
3515
- "app.bsky.embed.video",
3516
- "app.bsky.embed.external"
3517
- ]
3518
- }
3519
- }
3520
- },
3521
- "view": {
3522
- "type": "object",
3523
- "required": ["record", "media"],
3524
- "properties": {
3525
- "record": {
3526
- "type": "ref",
3527
- "ref": "app.bsky.embed.record#view"
3528
- },
3529
- "media": {
3530
- "type": "union",
3531
- "refs": [
3532
- "app.bsky.embed.images#view",
3533
- "app.bsky.embed.video#view",
3534
- "app.bsky.embed.external#view"
3535
- ]
3536
- }
3537
- }
3538
- }
3539
- };
3540
- var app_bsky_embed_recordWithMedia_default = {
3541
- lexicon: lexicon$15,
3542
- id: id$15,
3543
- description: description$2,
3544
- defs: defs$15
3545
- };
3546
-
3547
- //#endregion
3548
- //#region src/lexicons/app.bsky.embed.video.json
3549
- var app_bsky_embed_video_exports = /* @__PURE__ */ __exportAll({
3550
- default: () => app_bsky_embed_video_default,
3551
- defs: () => defs$14,
3552
- description: () => description$1,
3553
- id: () => id$14,
3554
- lexicon: () => lexicon$14
3555
- });
3556
- var lexicon$14 = 1;
3557
- var id$14 = "app.bsky.embed.video";
3558
- var description$1 = "A video embedded in a Bluesky record (eg, a post).";
3559
- var defs$14 = {
3560
- "main": {
3561
- "type": "object",
3562
- "required": ["video"],
3563
- "properties": {
3564
- "video": {
3565
- "type": "blob",
3566
- "description": "The mp4 video file. May be up to 100mb, formerly limited to 50mb.",
3567
- "accept": ["video/mp4"],
3568
- "maxSize": 1e8
3569
- },
3570
- "captions": {
3571
- "type": "array",
3572
- "items": {
3573
- "type": "ref",
3574
- "ref": "#caption"
3575
- },
3576
- "maxLength": 20
3577
- },
3578
- "alt": {
3579
- "type": "string",
3580
- "description": "Alt text description of the video, for accessibility.",
3581
- "maxGraphemes": 1e3,
3582
- "maxLength": 1e4
3583
- },
3584
- "aspectRatio": {
3585
- "type": "ref",
3586
- "ref": "app.bsky.embed.defs#aspectRatio"
3587
- }
3588
- }
3589
- },
3590
- "caption": {
3591
- "type": "object",
3592
- "required": ["lang", "file"],
3593
- "properties": {
3594
- "lang": {
3595
- "type": "string",
3596
- "format": "language"
3597
- },
3598
- "file": {
3599
- "type": "blob",
3600
- "accept": ["text/vtt"],
3601
- "maxSize": 2e4
3602
- }
3603
- }
3604
- },
3605
- "view": {
3606
- "type": "object",
3607
- "required": ["cid", "playlist"],
3608
- "properties": {
3609
- "cid": {
3610
- "type": "string",
3611
- "format": "cid"
3612
- },
3613
- "playlist": {
3614
- "type": "string",
3615
- "format": "uri"
3616
- },
3617
- "thumbnail": {
3618
- "type": "string",
3619
- "format": "uri"
3620
- },
3621
- "alt": {
3622
- "type": "string",
3623
- "maxGraphemes": 1e3,
3624
- "maxLength": 1e4
3625
- },
3626
- "aspectRatio": {
3627
- "type": "ref",
3628
- "ref": "app.bsky.embed.defs#aspectRatio"
3629
- }
3630
- }
3631
- }
3632
- };
3633
- var app_bsky_embed_video_default = {
3634
- lexicon: lexicon$14,
3635
- id: id$14,
3636
- description: description$1,
3637
- defs: defs$14
3638
- };
3639
-
3640
- //#endregion
3641
- //#region src/lexicons/app.bsky.feed.defs.json
3642
- var app_bsky_feed_defs_exports = /* @__PURE__ */ __exportAll({
3643
- default: () => app_bsky_feed_defs_default,
3644
- defs: () => defs$13,
3645
- id: () => id$13,
3646
- lexicon: () => lexicon$13
3647
- });
3648
- var lexicon$13 = 1;
3649
- var id$13 = "app.bsky.feed.defs";
3650
- var defs$13 = {
3651
- "postView": {
3652
- "type": "object",
3653
- "required": [
3654
- "uri",
3655
- "cid",
3656
- "author",
3657
- "record",
3658
- "indexedAt"
3659
- ],
3660
- "properties": {
3661
- "uri": {
3662
- "type": "string",
3663
- "format": "at-uri"
3664
- },
3665
- "cid": {
3666
- "type": "string",
3667
- "format": "cid"
3668
- },
3669
- "author": {
3670
- "type": "ref",
3671
- "ref": "app.bsky.actor.defs#profileViewBasic"
3672
- },
3673
- "record": { "type": "unknown" },
3674
- "embed": {
3675
- "type": "union",
3676
- "refs": [
3677
- "app.bsky.embed.images#view",
3678
- "app.bsky.embed.video#view",
3679
- "app.bsky.embed.external#view",
3680
- "app.bsky.embed.record#view",
3681
- "app.bsky.embed.recordWithMedia#view"
3682
- ]
3683
- },
3684
- "bookmarkCount": { "type": "integer" },
3685
- "replyCount": { "type": "integer" },
3686
- "repostCount": { "type": "integer" },
3687
- "likeCount": { "type": "integer" },
3688
- "quoteCount": { "type": "integer" },
3689
- "indexedAt": {
3690
- "type": "string",
3691
- "format": "datetime"
3692
- },
3693
- "viewer": {
3694
- "type": "ref",
3695
- "ref": "#viewerState"
3696
- },
3697
- "labels": {
3698
- "type": "array",
3699
- "items": {
3700
- "type": "ref",
3701
- "ref": "com.atproto.label.defs#label"
3702
- }
3703
- },
3704
- "threadgate": {
3705
- "type": "ref",
3706
- "ref": "#threadgateView"
3707
- },
3708
- "debug": {
3709
- "type": "unknown",
3710
- "description": "Debug information for internal development"
3711
- }
3712
- }
3713
- },
3714
- "viewerState": {
3715
- "type": "object",
3716
- "description": "Metadata about the requesting account's relationship with the subject content. Only has meaningful content for authed requests.",
3717
- "properties": {
3718
- "repost": {
3719
- "type": "string",
3720
- "format": "at-uri"
3721
- },
3722
- "like": {
3723
- "type": "string",
3724
- "format": "at-uri"
3725
- },
3726
- "bookmarked": { "type": "boolean" },
3727
- "threadMuted": { "type": "boolean" },
3728
- "replyDisabled": { "type": "boolean" },
3729
- "embeddingDisabled": { "type": "boolean" },
3730
- "pinned": { "type": "boolean" }
3731
- }
3732
- },
3733
- "threadContext": {
3734
- "type": "object",
3735
- "description": "Metadata about this post within the context of the thread it is in.",
3736
- "properties": { "rootAuthorLike": {
3737
- "type": "string",
3738
- "format": "at-uri"
3739
- } }
3740
- },
3741
- "feedViewPost": {
3742
- "type": "object",
3743
- "required": ["post"],
3744
- "properties": {
3745
- "post": {
3746
- "type": "ref",
3747
- "ref": "#postView"
3748
- },
3749
- "reply": {
3750
- "type": "ref",
3751
- "ref": "#replyRef"
3752
- },
3753
- "reason": {
3754
- "type": "union",
3755
- "refs": ["#reasonRepost", "#reasonPin"]
3756
- },
3757
- "feedContext": {
3758
- "type": "string",
3759
- "description": "Context provided by feed generator that may be passed back alongside interactions.",
3760
- "maxLength": 2e3
3761
- },
3762
- "reqId": {
3763
- "type": "string",
3764
- "description": "Unique identifier per request that may be passed back alongside interactions.",
3765
- "maxLength": 100
3766
- }
3767
- }
3768
- },
3769
- "replyRef": {
3770
- "type": "object",
3771
- "required": ["root", "parent"],
3772
- "properties": {
3773
- "root": {
3774
- "type": "union",
3775
- "refs": [
3776
- "#postView",
3777
- "#notFoundPost",
3778
- "#blockedPost"
3779
- ]
3780
- },
3781
- "parent": {
3782
- "type": "union",
3783
- "refs": [
3784
- "#postView",
3785
- "#notFoundPost",
3786
- "#blockedPost"
3787
- ]
3788
- },
3789
- "grandparentAuthor": {
3790
- "type": "ref",
3791
- "ref": "app.bsky.actor.defs#profileViewBasic",
3792
- "description": "When parent is a reply to another post, this is the author of that post."
3793
- }
3794
- }
3795
- },
3796
- "reasonRepost": {
3797
- "type": "object",
3798
- "required": ["by", "indexedAt"],
3799
- "properties": {
3800
- "by": {
3801
- "type": "ref",
3802
- "ref": "app.bsky.actor.defs#profileViewBasic"
3803
- },
3804
- "uri": {
3805
- "type": "string",
3806
- "format": "at-uri"
3807
- },
3808
- "cid": {
3809
- "type": "string",
3810
- "format": "cid"
3811
- },
3812
- "indexedAt": {
3813
- "type": "string",
3814
- "format": "datetime"
3815
- }
3816
- }
3817
- },
3818
- "reasonPin": {
3819
- "type": "object",
3820
- "properties": {}
3821
- },
3822
- "threadViewPost": {
3823
- "type": "object",
3824
- "required": ["post"],
3825
- "properties": {
3826
- "post": {
3827
- "type": "ref",
3828
- "ref": "#postView"
3829
- },
3830
- "parent": {
3831
- "type": "union",
3832
- "refs": [
3833
- "#threadViewPost",
3834
- "#notFoundPost",
3835
- "#blockedPost"
3836
- ]
3837
- },
3838
- "replies": {
3839
- "type": "array",
3840
- "items": {
3841
- "type": "union",
3842
- "refs": [
3843
- "#threadViewPost",
3844
- "#notFoundPost",
3845
- "#blockedPost"
3846
- ]
3847
- }
3848
- },
3849
- "threadContext": {
3850
- "type": "ref",
3851
- "ref": "#threadContext"
3852
- }
3853
- }
3854
- },
3855
- "notFoundPost": {
3856
- "type": "object",
3857
- "required": ["uri", "notFound"],
3858
- "properties": {
3859
- "uri": {
3860
- "type": "string",
3861
- "format": "at-uri"
3862
- },
3863
- "notFound": {
3864
- "type": "boolean",
3865
- "const": true
3866
- }
3867
- }
3868
- },
3869
- "blockedPost": {
3870
- "type": "object",
3871
- "required": [
3872
- "uri",
3873
- "blocked",
3874
- "author"
3875
- ],
3876
- "properties": {
3877
- "uri": {
3878
- "type": "string",
3879
- "format": "at-uri"
3880
- },
3881
- "blocked": {
3882
- "type": "boolean",
3883
- "const": true
3884
- },
3885
- "author": {
3886
- "type": "ref",
3887
- "ref": "#blockedAuthor"
3888
- }
3889
- }
3890
- },
3891
- "blockedAuthor": {
3892
- "type": "object",
3893
- "required": ["did"],
3894
- "properties": {
3895
- "did": {
3896
- "type": "string",
3897
- "format": "did"
3898
- },
3899
- "viewer": {
3900
- "type": "ref",
3901
- "ref": "app.bsky.actor.defs#viewerState"
3902
- }
3903
- }
3904
- },
3905
- "generatorView": {
3906
- "type": "object",
3907
- "required": [
3908
- "uri",
3909
- "cid",
3910
- "did",
3911
- "creator",
3912
- "displayName",
3913
- "indexedAt"
3914
- ],
3915
- "properties": {
3916
- "uri": {
3917
- "type": "string",
3918
- "format": "at-uri"
3919
- },
3920
- "cid": {
3921
- "type": "string",
3922
- "format": "cid"
3923
- },
3924
- "did": {
3925
- "type": "string",
3926
- "format": "did"
3927
- },
3928
- "creator": {
3929
- "type": "ref",
3930
- "ref": "app.bsky.actor.defs#profileView"
3931
- },
3932
- "displayName": { "type": "string" },
3933
- "description": {
3934
- "type": "string",
3935
- "maxGraphemes": 300,
3936
- "maxLength": 3e3
3937
- },
3938
- "descriptionFacets": {
3939
- "type": "array",
3940
- "items": {
3941
- "type": "ref",
3942
- "ref": "app.bsky.richtext.facet"
3943
- }
3944
- },
3945
- "avatar": {
3946
- "type": "string",
3947
- "format": "uri"
3948
- },
3949
- "likeCount": {
3950
- "type": "integer",
3951
- "minimum": 0
3952
- },
3953
- "acceptsInteractions": { "type": "boolean" },
3954
- "labels": {
3955
- "type": "array",
3956
- "items": {
3957
- "type": "ref",
3958
- "ref": "com.atproto.label.defs#label"
3959
- }
3960
- },
3961
- "viewer": {
3962
- "type": "ref",
3963
- "ref": "#generatorViewerState"
3964
- },
3965
- "contentMode": {
3966
- "type": "string",
3967
- "knownValues": ["app.bsky.feed.defs#contentModeUnspecified", "app.bsky.feed.defs#contentModeVideo"]
3968
- },
3969
- "indexedAt": {
3970
- "type": "string",
3971
- "format": "datetime"
3972
- }
3973
- }
3974
- },
3975
- "generatorViewerState": {
3976
- "type": "object",
3977
- "properties": { "like": {
3978
- "type": "string",
3979
- "format": "at-uri"
3980
- } }
3981
- },
3982
- "skeletonFeedPost": {
3983
- "type": "object",
3984
- "required": ["post"],
3985
- "properties": {
3986
- "post": {
3987
- "type": "string",
3988
- "format": "at-uri"
3989
- },
3990
- "reason": {
3991
- "type": "union",
3992
- "refs": ["#skeletonReasonRepost", "#skeletonReasonPin"]
3993
- },
3994
- "feedContext": {
3995
- "type": "string",
3996
- "description": "Context that will be passed through to client and may be passed to feed generator back alongside interactions.",
3997
- "maxLength": 2e3
3998
- }
3999
- }
4000
- },
4001
- "skeletonReasonRepost": {
4002
- "type": "object",
4003
- "required": ["repost"],
4004
- "properties": { "repost": {
4005
- "type": "string",
4006
- "format": "at-uri"
4007
- } }
4008
- },
4009
- "skeletonReasonPin": {
4010
- "type": "object",
4011
- "properties": {}
4012
- },
4013
- "threadgateView": {
4014
- "type": "object",
4015
- "properties": {
4016
- "uri": {
4017
- "type": "string",
4018
- "format": "at-uri"
4019
- },
4020
- "cid": {
4021
- "type": "string",
4022
- "format": "cid"
4023
- },
4024
- "record": { "type": "unknown" },
4025
- "lists": {
4026
- "type": "array",
4027
- "items": {
4028
- "type": "ref",
4029
- "ref": "app.bsky.graph.defs#listViewBasic"
4030
- }
4031
- }
4032
- }
4033
- },
4034
- "interaction": {
4035
- "type": "object",
4036
- "properties": {
4037
- "item": {
4038
- "type": "string",
4039
- "format": "at-uri"
4040
- },
4041
- "event": {
4042
- "type": "string",
4043
- "knownValues": [
4044
- "app.bsky.feed.defs#requestLess",
4045
- "app.bsky.feed.defs#requestMore",
4046
- "app.bsky.feed.defs#clickthroughItem",
4047
- "app.bsky.feed.defs#clickthroughAuthor",
4048
- "app.bsky.feed.defs#clickthroughReposter",
4049
- "app.bsky.feed.defs#clickthroughEmbed",
4050
- "app.bsky.feed.defs#interactionSeen",
4051
- "app.bsky.feed.defs#interactionLike",
4052
- "app.bsky.feed.defs#interactionRepost",
4053
- "app.bsky.feed.defs#interactionReply",
4054
- "app.bsky.feed.defs#interactionQuote",
4055
- "app.bsky.feed.defs#interactionShare"
4056
- ]
4057
- },
4058
- "feedContext": {
4059
- "type": "string",
4060
- "description": "Context on a feed item that was originally supplied by the feed generator on getFeedSkeleton.",
4061
- "maxLength": 2e3
4062
- },
4063
- "reqId": {
4064
- "type": "string",
4065
- "description": "Unique identifier per request that may be passed back alongside interactions.",
4066
- "maxLength": 100
4067
- }
4068
- }
4069
- },
4070
- "requestLess": {
4071
- "type": "token",
4072
- "description": "Request that less content like the given feed item be shown in the feed"
4073
- },
4074
- "requestMore": {
4075
- "type": "token",
4076
- "description": "Request that more content like the given feed item be shown in the feed"
4077
- },
4078
- "clickthroughItem": {
4079
- "type": "token",
4080
- "description": "User clicked through to the feed item"
4081
- },
4082
- "clickthroughAuthor": {
4083
- "type": "token",
4084
- "description": "User clicked through to the author of the feed item"
4085
- },
4086
- "clickthroughReposter": {
4087
- "type": "token",
4088
- "description": "User clicked through to the reposter of the feed item"
4089
- },
4090
- "clickthroughEmbed": {
4091
- "type": "token",
4092
- "description": "User clicked through to the embedded content of the feed item"
4093
- },
4094
- "contentModeUnspecified": {
4095
- "type": "token",
4096
- "description": "Declares the feed generator returns any types of posts."
4097
- },
4098
- "contentModeVideo": {
4099
- "type": "token",
4100
- "description": "Declares the feed generator returns posts containing app.bsky.embed.video embeds."
4101
- },
4102
- "interactionSeen": {
4103
- "type": "token",
4104
- "description": "Feed item was seen by user"
4105
- },
4106
- "interactionLike": {
4107
- "type": "token",
4108
- "description": "User liked the feed item"
4109
- },
4110
- "interactionRepost": {
4111
- "type": "token",
4112
- "description": "User reposted the feed item"
4113
- },
4114
- "interactionReply": {
4115
- "type": "token",
4116
- "description": "User replied to the feed item"
4117
- },
4118
- "interactionQuote": {
4119
- "type": "token",
4120
- "description": "User quoted the feed item"
4121
- },
4122
- "interactionShare": {
4123
- "type": "token",
4124
- "description": "User shared the feed item"
4125
- }
4126
- };
4127
- var app_bsky_feed_defs_default = {
4128
- lexicon: lexicon$13,
4129
- id: id$13,
4130
- defs: defs$13
4131
- };
4132
-
4133
- //#endregion
4134
- //#region src/lexicons/app.bsky.feed.like.json
4135
- var app_bsky_feed_like_exports = /* @__PURE__ */ __exportAll({
4136
- default: () => app_bsky_feed_like_default,
4137
- defs: () => defs$12,
4138
- id: () => id$12,
4139
- lexicon: () => lexicon$12
4140
- });
4141
- var lexicon$12 = 1;
4142
- var id$12 = "app.bsky.feed.like";
4143
- var defs$12 = { "main": {
4144
- "type": "record",
4145
- "description": "Record declaring a 'like' of a piece of subject content.",
4146
- "key": "tid",
4147
- "record": {
4148
- "type": "object",
4149
- "required": ["subject", "createdAt"],
4150
- "properties": {
4151
- "subject": {
4152
- "type": "ref",
4153
- "ref": "com.atproto.repo.strongRef"
4154
- },
4155
- "createdAt": {
4156
- "type": "string",
4157
- "format": "datetime"
4158
- },
4159
- "via": {
4160
- "type": "ref",
4161
- "ref": "com.atproto.repo.strongRef"
4162
- }
4163
- }
4164
- }
4165
- } };
4166
- var app_bsky_feed_like_default = {
4167
- lexicon: lexicon$12,
4168
- id: id$12,
4169
- defs: defs$12
4170
- };
4171
-
4172
- //#endregion
4173
- //#region src/lexicons/app.bsky.feed.post.json
4174
- var app_bsky_feed_post_exports = /* @__PURE__ */ __exportAll({
4175
- default: () => app_bsky_feed_post_default,
4176
- defs: () => defs$11,
4177
- id: () => id$11,
4178
- lexicon: () => lexicon$11
4179
- });
4180
- var lexicon$11 = 1;
4181
- var id$11 = "app.bsky.feed.post";
4182
- var defs$11 = {
4183
- "main": {
4184
- "type": "record",
4185
- "description": "Record containing a Bluesky post.",
4186
- "key": "tid",
4187
- "record": {
4188
- "type": "object",
4189
- "required": ["text", "createdAt"],
4190
- "properties": {
4191
- "text": {
4192
- "type": "string",
4193
- "maxLength": 3e3,
4194
- "maxGraphemes": 300,
4195
- "description": "The primary post content. May be an empty string, if there are embeds."
4196
- },
4197
- "entities": {
4198
- "type": "array",
4199
- "description": "DEPRECATED: replaced by app.bsky.richtext.facet.",
4200
- "items": {
4201
- "type": "ref",
4202
- "ref": "#entity"
4203
- }
4204
- },
4205
- "facets": {
4206
- "type": "array",
4207
- "description": "Annotations of text (mentions, URLs, hashtags, etc)",
4208
- "items": {
4209
- "type": "ref",
4210
- "ref": "app.bsky.richtext.facet"
4211
- }
4212
- },
4213
- "reply": {
4214
- "type": "ref",
4215
- "ref": "#replyRef"
4216
- },
4217
- "embed": {
4218
- "type": "union",
4219
- "refs": [
4220
- "app.bsky.embed.images",
4221
- "app.bsky.embed.video",
4222
- "app.bsky.embed.external",
4223
- "app.bsky.embed.record",
4224
- "app.bsky.embed.recordWithMedia"
4225
- ]
4226
- },
4227
- "langs": {
4228
- "type": "array",
4229
- "description": "Indicates human language of post primary text content.",
4230
- "maxLength": 3,
4231
- "items": {
4232
- "type": "string",
4233
- "format": "language"
4234
- }
4235
- },
4236
- "labels": {
4237
- "type": "union",
4238
- "description": "Self-label values for this post. Effectively content warnings.",
4239
- "refs": ["com.atproto.label.defs#selfLabels"]
4240
- },
4241
- "tags": {
4242
- "type": "array",
4243
- "description": "Additional hashtags, in addition to any included in post text and facets.",
4244
- "maxLength": 8,
4245
- "items": {
4246
- "type": "string",
4247
- "maxLength": 640,
4248
- "maxGraphemes": 64
4249
- }
4250
- },
4251
- "createdAt": {
4252
- "type": "string",
4253
- "format": "datetime",
4254
- "description": "Client-declared timestamp when this post was originally created."
4255
- }
4256
- }
4257
- }
4258
- },
4259
- "replyRef": {
4260
- "type": "object",
4261
- "required": ["root", "parent"],
4262
- "properties": {
4263
- "root": {
4264
- "type": "ref",
4265
- "ref": "com.atproto.repo.strongRef"
4266
- },
4267
- "parent": {
4268
- "type": "ref",
4269
- "ref": "com.atproto.repo.strongRef"
4270
- }
4271
- }
4272
- },
4273
- "entity": {
4274
- "type": "object",
4275
- "description": "Deprecated: use facets instead.",
4276
- "required": [
4277
- "index",
4278
- "type",
4279
- "value"
4280
- ],
4281
- "properties": {
4282
- "index": {
4283
- "type": "ref",
4284
- "ref": "#textSlice"
4285
- },
4286
- "type": {
4287
- "type": "string",
4288
- "description": "Expected values are 'mention' and 'link'."
4289
- },
4290
- "value": { "type": "string" }
4291
- }
4292
- },
4293
- "textSlice": {
4294
- "type": "object",
4295
- "description": "Deprecated. Use app.bsky.richtext instead -- A text segment. Start is inclusive, end is exclusive. Indices are for utf16-encoded strings.",
4296
- "required": ["start", "end"],
4297
- "properties": {
4298
- "start": {
4299
- "type": "integer",
4300
- "minimum": 0
4301
- },
4302
- "end": {
4303
- "type": "integer",
4304
- "minimum": 0
4305
- }
4306
- }
4307
- }
4308
- };
4309
- var app_bsky_feed_post_default = {
4310
- lexicon: lexicon$11,
4311
- id: id$11,
4312
- defs: defs$11
4313
- };
4314
-
4315
- //#endregion
4316
- //#region src/lexicons/app.bsky.feed.repost.json
4317
- var app_bsky_feed_repost_exports = /* @__PURE__ */ __exportAll({
4318
- default: () => app_bsky_feed_repost_default,
4319
- defs: () => defs$10,
4320
- id: () => id$10,
4321
- lexicon: () => lexicon$10
4322
- });
4323
- var lexicon$10 = 1;
4324
- var id$10 = "app.bsky.feed.repost";
4325
- var defs$10 = { "main": {
4326
- "description": "Record representing a 'repost' of an existing Bluesky post.",
4327
- "type": "record",
4328
- "key": "tid",
4329
- "record": {
4330
- "type": "object",
4331
- "required": ["subject", "createdAt"],
4332
- "properties": {
4333
- "subject": {
4334
- "type": "ref",
4335
- "ref": "com.atproto.repo.strongRef"
4336
- },
4337
- "createdAt": {
4338
- "type": "string",
4339
- "format": "datetime"
4340
- },
4341
- "via": {
4342
- "type": "ref",
4343
- "ref": "com.atproto.repo.strongRef"
4344
- }
4345
- }
4346
- }
4347
- } };
4348
- var app_bsky_feed_repost_default = {
4349
- lexicon: lexicon$10,
4350
- id: id$10,
4351
- defs: defs$10
4352
- };
4353
-
4354
- //#endregion
4355
- //#region src/lexicons/app.bsky.feed.threadgate.json
4356
- var app_bsky_feed_threadgate_exports = /* @__PURE__ */ __exportAll({
4357
- default: () => app_bsky_feed_threadgate_default,
4358
- defs: () => defs$9,
4359
- id: () => id$9,
4360
- lexicon: () => lexicon$9
4361
- });
4362
- var lexicon$9 = 1;
4363
- var id$9 = "app.bsky.feed.threadgate";
4364
- var defs$9 = {
4365
- "main": {
4366
- "type": "record",
4367
- "key": "tid",
4368
- "description": "Record defining interaction gating rules for a thread (aka, reply controls). The record key (rkey) of the threadgate record must match the record key of the thread's root post, and that record must be in the same repository.",
4369
- "record": {
4370
- "type": "object",
4371
- "required": ["post", "createdAt"],
4372
- "properties": {
4373
- "post": {
4374
- "type": "string",
4375
- "format": "at-uri",
4376
- "description": "Reference (AT-URI) to the post record."
4377
- },
4378
- "allow": {
4379
- "description": "List of rules defining who can reply to this post. If value is an empty array, no one can reply. If value is undefined, anyone can reply.",
4380
- "type": "array",
4381
- "maxLength": 5,
4382
- "items": {
4383
- "type": "union",
4384
- "refs": [
4385
- "#mentionRule",
4386
- "#followerRule",
4387
- "#followingRule",
4388
- "#listRule"
4389
- ]
4390
- }
4391
- },
4392
- "createdAt": {
4393
- "type": "string",
4394
- "format": "datetime"
4395
- },
4396
- "hiddenReplies": {
4397
- "type": "array",
4398
- "maxLength": 300,
4399
- "items": {
4400
- "type": "string",
4401
- "format": "at-uri"
4402
- },
4403
- "description": "List of hidden reply URIs."
4404
- }
4405
- }
4406
- }
4407
- },
4408
- "mentionRule": {
4409
- "type": "object",
4410
- "description": "Allow replies from actors mentioned in your post.",
4411
- "properties": {}
4412
- },
4413
- "followerRule": {
4414
- "type": "object",
4415
- "description": "Allow replies from actors who follow you.",
4416
- "properties": {}
4417
- },
4418
- "followingRule": {
4419
- "type": "object",
4420
- "description": "Allow replies from actors you follow.",
4421
- "properties": {}
4422
- },
4423
- "listRule": {
4424
- "type": "object",
4425
- "description": "Allow replies from actors on a list.",
4426
- "required": ["list"],
4427
- "properties": { "list": {
4428
- "type": "string",
4429
- "format": "at-uri"
4430
- } }
4431
- }
4432
- };
4433
- var app_bsky_feed_threadgate_default = {
4434
- lexicon: lexicon$9,
4435
- id: id$9,
4436
- defs: defs$9
4437
- };
4438
-
4439
- //#endregion
4440
- //#region src/lexicons/app.bsky.graph.block.json
4441
- var app_bsky_graph_block_exports = /* @__PURE__ */ __exportAll({
4442
- default: () => app_bsky_graph_block_default,
4443
- defs: () => defs$8,
4444
- id: () => id$8,
4445
- lexicon: () => lexicon$8
4446
- });
4447
- var lexicon$8 = 1;
4448
- var id$8 = "app.bsky.graph.block";
4449
- var defs$8 = { "main": {
4450
- "type": "record",
4451
- "description": "Record declaring a 'block' relationship against another account. NOTE: blocks are public in Bluesky; see blog posts for details.",
4452
- "key": "tid",
4453
- "record": {
4454
- "type": "object",
4455
- "required": ["subject", "createdAt"],
4456
- "properties": {
4457
- "subject": {
4458
- "type": "string",
4459
- "format": "did",
4460
- "description": "DID of the account to be blocked."
4461
- },
4462
- "createdAt": {
4463
- "type": "string",
4464
- "format": "datetime"
4465
- }
4466
- }
4467
- }
4468
- } };
4469
- var app_bsky_graph_block_default = {
4470
- lexicon: lexicon$8,
4471
- id: id$8,
4472
- defs: defs$8
4473
- };
4474
-
4475
- //#endregion
4476
- //#region src/lexicons/app.bsky.graph.defs.json
4477
- var app_bsky_graph_defs_exports = /* @__PURE__ */ __exportAll({
4478
- default: () => app_bsky_graph_defs_default,
4479
- defs: () => defs$7,
4480
- id: () => id$7,
4481
- lexicon: () => lexicon$7
4482
- });
4483
- var lexicon$7 = 1;
4484
- var id$7 = "app.bsky.graph.defs";
4485
- var defs$7 = {
4486
- "listViewBasic": {
4487
- "type": "object",
4488
- "required": [
4489
- "uri",
4490
- "cid",
4491
- "name",
4492
- "purpose"
4493
- ],
4494
- "properties": {
4495
- "uri": {
4496
- "type": "string",
4497
- "format": "at-uri"
4498
- },
4499
- "cid": {
4500
- "type": "string",
4501
- "format": "cid"
4502
- },
4503
- "name": {
4504
- "type": "string",
4505
- "maxLength": 64,
4506
- "minLength": 1
4507
- },
4508
- "purpose": {
4509
- "type": "ref",
4510
- "ref": "#listPurpose"
4511
- },
4512
- "avatar": {
4513
- "type": "string",
4514
- "format": "uri"
4515
- },
4516
- "listItemCount": {
4517
- "type": "integer",
4518
- "minimum": 0
4519
- },
4520
- "labels": {
4521
- "type": "array",
4522
- "items": {
4523
- "type": "ref",
4524
- "ref": "com.atproto.label.defs#label"
4525
- }
4526
- },
4527
- "viewer": {
4528
- "type": "ref",
4529
- "ref": "#listViewerState"
4530
- },
4531
- "indexedAt": {
4532
- "type": "string",
4533
- "format": "datetime"
4534
- }
4535
- }
4536
- },
4537
- "listView": {
4538
- "type": "object",
4539
- "required": [
4540
- "uri",
4541
- "cid",
4542
- "creator",
4543
- "name",
4544
- "purpose",
4545
- "indexedAt"
4546
- ],
4547
- "properties": {
4548
- "uri": {
4549
- "type": "string",
4550
- "format": "at-uri"
4551
- },
4552
- "cid": {
4553
- "type": "string",
4554
- "format": "cid"
4555
- },
4556
- "creator": {
4557
- "type": "ref",
4558
- "ref": "app.bsky.actor.defs#profileView"
4559
- },
4560
- "name": {
4561
- "type": "string",
4562
- "maxLength": 64,
4563
- "minLength": 1
4564
- },
4565
- "purpose": {
4566
- "type": "ref",
4567
- "ref": "#listPurpose"
4568
- },
4569
- "description": {
4570
- "type": "string",
4571
- "maxGraphemes": 300,
4572
- "maxLength": 3e3
4573
- },
4574
- "descriptionFacets": {
4575
- "type": "array",
4576
- "items": {
4577
- "type": "ref",
4578
- "ref": "app.bsky.richtext.facet"
4579
- }
4580
- },
4581
- "avatar": {
4582
- "type": "string",
4583
- "format": "uri"
4584
- },
4585
- "listItemCount": {
4586
- "type": "integer",
4587
- "minimum": 0
4588
- },
4589
- "labels": {
4590
- "type": "array",
4591
- "items": {
4592
- "type": "ref",
4593
- "ref": "com.atproto.label.defs#label"
4594
- }
4595
- },
4596
- "viewer": {
4597
- "type": "ref",
4598
- "ref": "#listViewerState"
4599
- },
4600
- "indexedAt": {
4601
- "type": "string",
4602
- "format": "datetime"
4603
- }
4604
- }
4605
- },
4606
- "listItemView": {
4607
- "type": "object",
4608
- "required": ["uri", "subject"],
4609
- "properties": {
4610
- "uri": {
4611
- "type": "string",
4612
- "format": "at-uri"
4613
- },
4614
- "subject": {
4615
- "type": "ref",
4616
- "ref": "app.bsky.actor.defs#profileView"
4617
- }
4618
- }
4619
- },
4620
- "starterPackView": {
4621
- "type": "object",
4622
- "required": [
4623
- "uri",
4624
- "cid",
4625
- "record",
4626
- "creator",
4627
- "indexedAt"
4628
- ],
4629
- "properties": {
4630
- "uri": {
4631
- "type": "string",
4632
- "format": "at-uri"
4633
- },
4634
- "cid": {
4635
- "type": "string",
4636
- "format": "cid"
4637
- },
4638
- "record": { "type": "unknown" },
4639
- "creator": {
4640
- "type": "ref",
4641
- "ref": "app.bsky.actor.defs#profileViewBasic"
4642
- },
4643
- "list": {
4644
- "type": "ref",
4645
- "ref": "#listViewBasic"
4646
- },
4647
- "listItemsSample": {
4648
- "type": "array",
4649
- "maxLength": 12,
4650
- "items": {
4651
- "type": "ref",
4652
- "ref": "#listItemView"
4653
- }
4654
- },
4655
- "feeds": {
4656
- "type": "array",
4657
- "maxLength": 3,
4658
- "items": {
4659
- "type": "ref",
4660
- "ref": "app.bsky.feed.defs#generatorView"
4661
- }
4662
- },
4663
- "joinedWeekCount": {
4664
- "type": "integer",
4665
- "minimum": 0
4666
- },
4667
- "joinedAllTimeCount": {
4668
- "type": "integer",
4669
- "minimum": 0
4670
- },
4671
- "labels": {
4672
- "type": "array",
4673
- "items": {
4674
- "type": "ref",
4675
- "ref": "com.atproto.label.defs#label"
4676
- }
4677
- },
4678
- "indexedAt": {
4679
- "type": "string",
4680
- "format": "datetime"
4681
- }
4682
- }
4683
- },
4684
- "starterPackViewBasic": {
4685
- "type": "object",
4686
- "required": [
4687
- "uri",
4688
- "cid",
4689
- "record",
4690
- "creator",
4691
- "indexedAt"
4692
- ],
4693
- "properties": {
4694
- "uri": {
4695
- "type": "string",
4696
- "format": "at-uri"
4697
- },
4698
- "cid": {
4699
- "type": "string",
4700
- "format": "cid"
4701
- },
4702
- "record": { "type": "unknown" },
4703
- "creator": {
4704
- "type": "ref",
4705
- "ref": "app.bsky.actor.defs#profileViewBasic"
4706
- },
4707
- "listItemCount": {
4708
- "type": "integer",
4709
- "minimum": 0
4710
- },
4711
- "joinedWeekCount": {
4712
- "type": "integer",
4713
- "minimum": 0
4714
- },
4715
- "joinedAllTimeCount": {
4716
- "type": "integer",
4717
- "minimum": 0
4718
- },
4719
- "labels": {
4720
- "type": "array",
4721
- "items": {
4722
- "type": "ref",
4723
- "ref": "com.atproto.label.defs#label"
4724
- }
4725
- },
4726
- "indexedAt": {
4727
- "type": "string",
4728
- "format": "datetime"
4729
- }
4730
- }
4731
- },
4732
- "listPurpose": {
4733
- "type": "string",
4734
- "knownValues": [
4735
- "app.bsky.graph.defs#modlist",
4736
- "app.bsky.graph.defs#curatelist",
4737
- "app.bsky.graph.defs#referencelist"
4738
- ]
4739
- },
4740
- "modlist": {
4741
- "type": "token",
4742
- "description": "A list of actors to apply an aggregate moderation action (mute/block) on."
4743
- },
4744
- "curatelist": {
4745
- "type": "token",
4746
- "description": "A list of actors used for curation purposes such as list feeds or interaction gating."
4747
- },
4748
- "referencelist": {
4749
- "type": "token",
4750
- "description": "A list of actors used for only for reference purposes such as within a starter pack."
4751
- },
4752
- "listViewerState": {
4753
- "type": "object",
4754
- "properties": {
4755
- "muted": { "type": "boolean" },
4756
- "blocked": {
4757
- "type": "string",
4758
- "format": "at-uri"
4759
- }
4760
- }
4761
- },
4762
- "notFoundActor": {
4763
- "type": "object",
4764
- "description": "indicates that a handle or DID could not be resolved",
4765
- "required": ["actor", "notFound"],
4766
- "properties": {
4767
- "actor": {
4768
- "type": "string",
4769
- "format": "at-identifier"
4770
- },
4771
- "notFound": {
4772
- "type": "boolean",
4773
- "const": true
4774
- }
4775
- }
4776
- },
4777
- "relationship": {
4778
- "type": "object",
4779
- "description": "lists the bi-directional graph relationships between one actor (not indicated in the object), and the target actors (the DID included in the object)",
4780
- "required": ["did"],
4781
- "properties": {
4782
- "did": {
4783
- "type": "string",
4784
- "format": "did"
4785
- },
4786
- "following": {
4787
- "type": "string",
4788
- "format": "at-uri",
4789
- "description": "if the actor follows this DID, this is the AT-URI of the follow record"
4790
- },
4791
- "followedBy": {
4792
- "type": "string",
4793
- "format": "at-uri",
4794
- "description": "if the actor is followed by this DID, contains the AT-URI of the follow record"
4795
- },
4796
- "blocking": {
4797
- "type": "string",
4798
- "format": "at-uri",
4799
- "description": "if the actor blocks this DID, this is the AT-URI of the block record"
4800
- },
4801
- "blockedBy": {
4802
- "type": "string",
4803
- "format": "at-uri",
4804
- "description": "if the actor is blocked by this DID, contains the AT-URI of the block record"
4805
- },
4806
- "blockingByList": {
4807
- "type": "string",
4808
- "format": "at-uri",
4809
- "description": "if the actor blocks this DID via a block list, this is the AT-URI of the listblock record"
4810
- },
4811
- "blockedByList": {
4812
- "type": "string",
4813
- "format": "at-uri",
4814
- "description": "if the actor is blocked by this DID via a block list, contains the AT-URI of the listblock record"
4815
- }
4816
- }
4817
- }
4818
- };
4819
- var app_bsky_graph_defs_default = {
4820
- lexicon: lexicon$7,
4821
- id: id$7,
4822
- defs: defs$7
4823
- };
4824
-
4825
- //#endregion
4826
- //#region src/lexicons/app.bsky.graph.follow.json
4827
- var app_bsky_graph_follow_exports = /* @__PURE__ */ __exportAll({
4828
- default: () => app_bsky_graph_follow_default,
4829
- defs: () => defs$6,
4830
- id: () => id$6,
4831
- lexicon: () => lexicon$6
4832
- });
4833
- var lexicon$6 = 1;
4834
- var id$6 = "app.bsky.graph.follow";
4835
- var defs$6 = { "main": {
4836
- "type": "record",
4837
- "description": "Record declaring a social 'follow' relationship of another account. Duplicate follows will be ignored by the AppView.",
4838
- "key": "tid",
4839
- "record": {
4840
- "type": "object",
4841
- "required": ["subject", "createdAt"],
4842
- "properties": {
4843
- "subject": {
4844
- "type": "string",
4845
- "format": "did"
4846
- },
4847
- "createdAt": {
4848
- "type": "string",
4849
- "format": "datetime"
4850
- },
4851
- "via": {
4852
- "type": "ref",
4853
- "ref": "com.atproto.repo.strongRef"
4854
- }
4855
- }
4856
- }
4857
- } };
4858
- var app_bsky_graph_follow_default = {
4859
- lexicon: lexicon$6,
4860
- id: id$6,
4861
- defs: defs$6
4862
- };
4863
-
4864
- //#endregion
4865
- //#region src/lexicons/app.bsky.graph.list.json
4866
- var app_bsky_graph_list_exports = /* @__PURE__ */ __exportAll({
4867
- default: () => app_bsky_graph_list_default,
4868
- defs: () => defs$5,
4869
- id: () => id$5,
4870
- lexicon: () => lexicon$5
4871
- });
4872
- var lexicon$5 = 1;
4873
- var id$5 = "app.bsky.graph.list";
4874
- var defs$5 = { "main": {
4875
- "type": "record",
4876
- "description": "Record representing a list of accounts (actors). Scope includes both moderation-oriented lists and curration-oriented lists.",
4877
- "key": "tid",
4878
- "record": {
4879
- "type": "object",
4880
- "required": [
4881
- "name",
4882
- "purpose",
4883
- "createdAt"
4884
- ],
4885
- "properties": {
4886
- "purpose": {
4887
- "type": "ref",
4888
- "description": "Defines the purpose of the list (aka, moderation-oriented or curration-oriented)",
4889
- "ref": "app.bsky.graph.defs#listPurpose"
4890
- },
4891
- "name": {
4892
- "type": "string",
4893
- "maxLength": 64,
4894
- "minLength": 1,
4895
- "description": "Display name for list; can not be empty."
4896
- },
4897
- "description": {
4898
- "type": "string",
4899
- "maxGraphemes": 300,
4900
- "maxLength": 3e3
4901
- },
4902
- "descriptionFacets": {
4903
- "type": "array",
4904
- "items": {
4905
- "type": "ref",
4906
- "ref": "app.bsky.richtext.facet"
4907
- }
4908
- },
4909
- "avatar": {
4910
- "type": "blob",
4911
- "accept": ["image/png", "image/jpeg"],
4912
- "maxSize": 1e6
4913
- },
4914
- "labels": {
4915
- "type": "union",
4916
- "refs": ["com.atproto.label.defs#selfLabels"]
4917
- },
4918
- "createdAt": {
4919
- "type": "string",
4920
- "format": "datetime"
4921
- }
4922
- }
4923
- }
4924
- } };
4925
- var app_bsky_graph_list_default = {
4926
- lexicon: lexicon$5,
4927
- id: id$5,
4928
- defs: defs$5
4929
- };
4930
-
4931
- //#endregion
4932
- //#region src/lexicons/app.bsky.graph.listitem.json
4933
- var app_bsky_graph_listitem_exports = /* @__PURE__ */ __exportAll({
4934
- default: () => app_bsky_graph_listitem_default,
4935
- defs: () => defs$4,
4936
- id: () => id$4,
4937
- lexicon: () => lexicon$4
4938
- });
4939
- var lexicon$4 = 1;
4940
- var id$4 = "app.bsky.graph.listitem";
4941
- var defs$4 = { "main": {
4942
- "type": "record",
4943
- "description": "Record representing an account's inclusion on a specific list. The AppView will ignore duplicate listitem records.",
4944
- "key": "tid",
4945
- "record": {
4946
- "type": "object",
4947
- "required": [
4948
- "subject",
4949
- "list",
4950
- "createdAt"
4951
- ],
4952
- "properties": {
4953
- "subject": {
4954
- "type": "string",
4955
- "format": "did",
4956
- "description": "The account which is included on the list."
4957
- },
4958
- "list": {
4959
- "type": "string",
4960
- "format": "at-uri",
4961
- "description": "Reference (AT-URI) to the list record (app.bsky.graph.list)."
4962
- },
4963
- "createdAt": {
4964
- "type": "string",
4965
- "format": "datetime"
4966
- }
4967
- }
4968
- }
4969
- } };
4970
- var app_bsky_graph_listitem_default = {
4971
- lexicon: lexicon$4,
4972
- id: id$4,
4973
- defs: defs$4
4974
- };
4975
-
4976
- //#endregion
4977
- //#region src/lexicons/app.bsky.notification.defs.json
4978
- var app_bsky_notification_defs_exports = /* @__PURE__ */ __exportAll({
4979
- default: () => app_bsky_notification_defs_default,
4980
- defs: () => defs$3,
4981
- id: () => id$3,
4982
- lexicon: () => lexicon$3
4983
- });
4984
- var lexicon$3 = 1;
4985
- var id$3 = "app.bsky.notification.defs";
4986
- var defs$3 = {
4987
- "recordDeleted": {
4988
- "type": "object",
4989
- "properties": {}
4990
- },
4991
- "chatPreference": {
4992
- "type": "object",
4993
- "required": ["include", "push"],
4994
- "properties": {
4995
- "include": {
4996
- "type": "string",
4997
- "knownValues": ["all", "accepted"]
4998
- },
4999
- "push": { "type": "boolean" }
5000
- }
5001
- },
5002
- "filterablePreference": {
5003
- "type": "object",
5004
- "required": [
5005
- "include",
5006
- "list",
5007
- "push"
5008
- ],
5009
- "properties": {
5010
- "include": {
5011
- "type": "string",
5012
- "knownValues": ["all", "follows"]
5013
- },
5014
- "list": { "type": "boolean" },
5015
- "push": { "type": "boolean" }
5016
- }
5017
- },
5018
- "preference": {
5019
- "type": "object",
5020
- "required": ["list", "push"],
5021
- "properties": {
5022
- "list": { "type": "boolean" },
5023
- "push": { "type": "boolean" }
5024
- }
5025
- },
5026
- "preferences": {
5027
- "type": "object",
5028
- "required": [
5029
- "chat",
5030
- "follow",
5031
- "like",
5032
- "likeViaRepost",
5033
- "mention",
5034
- "quote",
5035
- "reply",
5036
- "repost",
5037
- "repostViaRepost",
5038
- "starterpackJoined",
5039
- "subscribedPost",
5040
- "unverified",
5041
- "verified"
5042
- ],
5043
- "properties": {
5044
- "chat": {
5045
- "type": "ref",
5046
- "ref": "#chatPreference"
5047
- },
5048
- "follow": {
5049
- "type": "ref",
5050
- "ref": "#filterablePreference"
5051
- },
5052
- "like": {
5053
- "type": "ref",
5054
- "ref": "#filterablePreference"
5055
- },
5056
- "likeViaRepost": {
5057
- "type": "ref",
5058
- "ref": "#filterablePreference"
5059
- },
5060
- "mention": {
5061
- "type": "ref",
5062
- "ref": "#filterablePreference"
5063
- },
5064
- "quote": {
5065
- "type": "ref",
5066
- "ref": "#filterablePreference"
5067
- },
5068
- "reply": {
5069
- "type": "ref",
5070
- "ref": "#filterablePreference"
5071
- },
5072
- "repost": {
5073
- "type": "ref",
5074
- "ref": "#filterablePreference"
5075
- },
5076
- "repostViaRepost": {
5077
- "type": "ref",
5078
- "ref": "#filterablePreference"
5079
- },
5080
- "starterpackJoined": {
5081
- "type": "ref",
5082
- "ref": "#preference"
5083
- },
5084
- "subscribedPost": {
5085
- "type": "ref",
5086
- "ref": "#preference"
5087
- },
5088
- "unverified": {
5089
- "type": "ref",
5090
- "ref": "#preference"
5091
- },
5092
- "verified": {
5093
- "type": "ref",
5094
- "ref": "#preference"
5095
- }
5096
- }
5097
- },
5098
- "activitySubscription": {
5099
- "type": "object",
5100
- "required": ["post", "reply"],
5101
- "properties": {
5102
- "post": { "type": "boolean" },
5103
- "reply": { "type": "boolean" }
5104
- }
5105
- },
5106
- "subjectActivitySubscription": {
5107
- "description": "Object used to store activity subscription data in stash.",
5108
- "type": "object",
5109
- "required": ["subject", "activitySubscription"],
5110
- "properties": {
5111
- "subject": {
5112
- "type": "string",
5113
- "format": "did"
5114
- },
5115
- "activitySubscription": {
5116
- "type": "ref",
5117
- "ref": "#activitySubscription"
5118
- }
5119
- }
5120
- }
5121
- };
5122
- var app_bsky_notification_defs_default = {
5123
- lexicon: lexicon$3,
5124
- id: id$3,
5125
- defs: defs$3
5126
- };
5127
-
5128
- //#endregion
5129
- //#region src/lexicons/app.bsky.richtext.facet.json
5130
- var app_bsky_richtext_facet_exports = /* @__PURE__ */ __exportAll({
5131
- default: () => app_bsky_richtext_facet_default,
5132
- defs: () => defs$2,
5133
- id: () => id$2,
5134
- lexicon: () => lexicon$2
5135
- });
5136
- var lexicon$2 = 1;
5137
- var id$2 = "app.bsky.richtext.facet";
5138
- var defs$2 = {
5139
- "main": {
5140
- "type": "object",
5141
- "description": "Annotation of a sub-string within rich text.",
5142
- "required": ["index", "features"],
5143
- "properties": {
5144
- "index": {
5145
- "type": "ref",
5146
- "ref": "#byteSlice"
5147
- },
5148
- "features": {
5149
- "type": "array",
5150
- "items": {
5151
- "type": "union",
5152
- "refs": [
5153
- "#mention",
5154
- "#link",
5155
- "#tag"
5156
- ]
5157
- }
5158
- }
5159
- }
5160
- },
5161
- "mention": {
5162
- "type": "object",
5163
- "description": "Facet feature for mention of another account. The text is usually a handle, including a '@' prefix, but the facet reference is a DID.",
5164
- "required": ["did"],
5165
- "properties": { "did": {
5166
- "type": "string",
5167
- "format": "did"
5168
- } }
5169
- },
5170
- "link": {
5171
- "type": "object",
5172
- "description": "Facet feature for a URL. The text URL may have been simplified or truncated, but the facet reference should be a complete URL.",
5173
- "required": ["uri"],
5174
- "properties": { "uri": {
5175
- "type": "string",
5176
- "format": "uri"
5177
- } }
5178
- },
5179
- "tag": {
5180
- "type": "object",
5181
- "description": "Facet feature for a hashtag. The text usually includes a '#' prefix, but the facet reference should not (except in the case of 'double hash tags').",
5182
- "required": ["tag"],
5183
- "properties": { "tag": {
5184
- "type": "string",
5185
- "maxLength": 640,
5186
- "maxGraphemes": 64
5187
- } }
5188
- },
5189
- "byteSlice": {
5190
- "type": "object",
5191
- "description": "Specifies the sub-string range a facet feature applies to. Start index is inclusive, end index is exclusive. Indices are zero-indexed, counting bytes of the UTF-8 encoded text. NOTE: some languages, like Javascript, use UTF-16 or Unicode codepoints for string slice indexing; in these languages, convert to byte arrays before working with facets.",
5192
- "required": ["byteStart", "byteEnd"],
5193
- "properties": {
5194
- "byteStart": {
5195
- "type": "integer",
5196
- "minimum": 0
5197
- },
5198
- "byteEnd": {
5199
- "type": "integer",
5200
- "minimum": 0
5201
- }
5202
- }
5203
- }
5204
- };
5205
- var app_bsky_richtext_facet_default = {
5206
- lexicon: lexicon$2,
5207
- id: id$2,
5208
- defs: defs$2
5209
- };
5210
-
5211
- //#endregion
5212
- //#region src/lexicons/com.atproto.label.defs.json
5213
- var com_atproto_label_defs_exports = /* @__PURE__ */ __exportAll({
5214
- default: () => com_atproto_label_defs_default,
5215
- defs: () => defs$1,
5216
- id: () => id$1,
5217
- lexicon: () => lexicon$1
5218
- });
5219
- var lexicon$1 = 1;
5220
- var id$1 = "com.atproto.label.defs";
5221
- var defs$1 = {
5222
- "label": {
5223
- "type": "object",
5224
- "description": "Metadata tag on an atproto resource (eg, repo or record).",
5225
- "required": [
5226
- "src",
5227
- "uri",
5228
- "val",
5229
- "cts"
5230
- ],
5231
- "properties": {
5232
- "ver": {
5233
- "type": "integer",
5234
- "description": "The AT Protocol version of the label object."
5235
- },
5236
- "src": {
5237
- "type": "string",
5238
- "format": "did",
5239
- "description": "DID of the actor who created this label."
5240
- },
5241
- "uri": {
5242
- "type": "string",
5243
- "format": "uri",
5244
- "description": "AT URI of the record, repository (account), or other resource that this label applies to."
5245
- },
5246
- "cid": {
5247
- "type": "string",
5248
- "format": "cid",
5249
- "description": "Optionally, CID specifying the specific version of 'uri' resource this label applies to."
5250
- },
5251
- "val": {
5252
- "type": "string",
5253
- "maxLength": 128,
5254
- "description": "The short string name of the value or type of this label."
5255
- },
5256
- "neg": {
5257
- "type": "boolean",
5258
- "description": "If true, this is a negation label, overwriting a previous label."
5259
- },
5260
- "cts": {
5261
- "type": "string",
5262
- "format": "datetime",
5263
- "description": "Timestamp when this label was created."
5264
- },
5265
- "exp": {
5266
- "type": "string",
5267
- "format": "datetime",
5268
- "description": "Timestamp at which this label expires (no longer applies)."
5269
- },
5270
- "sig": {
5271
- "type": "bytes",
5272
- "description": "Signature of dag-cbor encoded label."
5273
- }
5274
- }
5275
- },
5276
- "selfLabels": {
5277
- "type": "object",
5278
- "description": "Metadata tags on an atproto record, published by the author within the record.",
5279
- "required": ["values"],
5280
- "properties": { "values": {
5281
- "type": "array",
5282
- "items": {
5283
- "type": "ref",
5284
- "ref": "#selfLabel"
5285
- },
5286
- "maxLength": 10
5287
- } }
5288
- },
5289
- "selfLabel": {
5290
- "type": "object",
5291
- "description": "Metadata tag on an atproto record, published by the author within the record. Note that schemas should use #selfLabels, not #selfLabel.",
5292
- "required": ["val"],
5293
- "properties": { "val": {
5294
- "type": "string",
5295
- "maxLength": 128,
5296
- "description": "The short string name of the value or type of this label."
5297
- } }
5298
- },
5299
- "labelValueDefinition": {
5300
- "type": "object",
5301
- "description": "Declares a label value and its expected interpretations and behaviors.",
5302
- "required": [
5303
- "identifier",
5304
- "severity",
5305
- "blurs",
5306
- "locales"
5307
- ],
5308
- "properties": {
5309
- "identifier": {
5310
- "type": "string",
5311
- "description": "The value of the label being defined. Must only include lowercase ascii and the '-' character ([a-z-]+).",
5312
- "maxLength": 100,
5313
- "maxGraphemes": 100
5314
- },
5315
- "severity": {
5316
- "type": "string",
5317
- "description": "How should a client visually convey this label? 'inform' means neutral and informational; 'alert' means negative and warning; 'none' means show nothing.",
5318
- "knownValues": [
5319
- "inform",
5320
- "alert",
5321
- "none"
5322
- ]
5323
- },
5324
- "blurs": {
5325
- "type": "string",
5326
- "description": "What should this label hide in the UI, if applied? 'content' hides all of the target; 'media' hides the images/video/audio; 'none' hides nothing.",
5327
- "knownValues": [
5328
- "content",
5329
- "media",
5330
- "none"
5331
- ]
5332
- },
5333
- "defaultSetting": {
5334
- "type": "string",
5335
- "description": "The default setting for this label.",
5336
- "knownValues": [
5337
- "ignore",
5338
- "warn",
5339
- "hide"
5340
- ],
5341
- "default": "warn"
5342
- },
5343
- "adultOnly": {
5344
- "type": "boolean",
5345
- "description": "Does the user need to have adult content enabled in order to configure this label?"
5346
- },
5347
- "locales": {
5348
- "type": "array",
5349
- "items": {
5350
- "type": "ref",
5351
- "ref": "#labelValueDefinitionStrings"
5352
- }
5353
- }
5354
- }
5355
- },
5356
- "labelValueDefinitionStrings": {
5357
- "type": "object",
5358
- "description": "Strings which describe the label in the UI, localized into a specific language.",
5359
- "required": [
5360
- "lang",
5361
- "name",
5362
- "description"
5363
- ],
5364
- "properties": {
5365
- "lang": {
5366
- "type": "string",
5367
- "description": "The code of the language these strings are written in.",
5368
- "format": "language"
5369
- },
5370
- "name": {
5371
- "type": "string",
5372
- "description": "A short human-readable name for the label.",
5373
- "maxGraphemes": 64,
5374
- "maxLength": 640
5375
- },
5376
- "description": {
5377
- "type": "string",
5378
- "description": "A longer description of what the label means and why it might be applied.",
5379
- "maxGraphemes": 1e4,
5380
- "maxLength": 1e5
5381
- }
5382
- }
5383
- },
5384
- "labelValue": {
5385
- "type": "string",
5386
- "knownValues": [
5387
- "!hide",
5388
- "!no-promote",
5389
- "!warn",
5390
- "!no-unauthenticated",
5391
- "dmca-violation",
5392
- "doxxing",
5393
- "porn",
5394
- "sexual",
5395
- "nudity",
5396
- "nsfl",
5397
- "gore"
5398
- ]
5399
- }
5400
- };
5401
- var com_atproto_label_defs_default = {
5402
- lexicon: lexicon$1,
5403
- id: id$1,
5404
- defs: defs$1
5405
- };
5406
-
5407
- //#endregion
5408
- //#region src/lexicons/com.atproto.repo.strongRef.json
5409
- var com_atproto_repo_strongRef_exports = /* @__PURE__ */ __exportAll({
5410
- default: () => com_atproto_repo_strongRef_default,
5411
- defs: () => defs,
5412
- description: () => description,
5413
- id: () => id,
5414
- lexicon: () => lexicon
5415
- });
5416
- var lexicon = 1;
5417
- var id = "com.atproto.repo.strongRef";
5418
- var description = "A URI with a content-hash fingerprint.";
5419
- var defs = { "main": {
5420
- "type": "object",
5421
- "required": ["uri", "cid"],
5422
- "properties": {
5423
- "uri": {
5424
- "type": "string",
5425
- "format": "at-uri"
5426
- },
5427
- "cid": {
5428
- "type": "string",
5429
- "format": "cid"
5430
- }
5431
- }
5432
- } };
5433
- var com_atproto_repo_strongRef_default = {
5434
- lexicon,
5435
- id,
5436
- description,
5437
- defs
5438
- };
5439
-
5440
- //#endregion
5441
- //#region src/validation.ts
5442
- /**
5443
- * Record validator for AT Protocol records.
5444
- *
5445
- * Validates records against official Bluesky lexicon schemas.
5446
- * Uses optimistic validation strategy:
5447
- * - If a lexicon schema is loaded for the collection, validate the record
5448
- * - If no schema is loaded, allow the record (fail-open)
5449
- *
5450
- * This allows the PDS to accept records for new or unknown collection types
5451
- * while still validating known types when schemas are available.
5452
- */
5453
- var RecordValidator = class {
5454
- lex;
5455
- strictMode;
5456
- constructor(options = {}) {
5457
- this.lex = options.lexicons ?? new Lexicons();
5458
- this.strictMode = options.strict ?? false;
5459
- this.loadBlueskySchemas();
5460
- }
5461
- /**
5462
- * Load official Bluesky lexicon schemas from vendored JSON files.
5463
- * Uses Vite's glob import to automatically load all schema files.
5464
- */
5465
- loadBlueskySchemas() {
5466
- const schemas = {
5467
- "./lexicons/app.bsky.actor.defs.json": app_bsky_actor_defs_exports,
5468
- "./lexicons/app.bsky.actor.profile.json": app_bsky_actor_profile_exports,
5469
- "./lexicons/app.bsky.embed.defs.json": app_bsky_embed_defs_exports,
5470
- "./lexicons/app.bsky.embed.external.json": app_bsky_embed_external_exports,
5471
- "./lexicons/app.bsky.embed.images.json": app_bsky_embed_images_exports,
5472
- "./lexicons/app.bsky.embed.record.json": app_bsky_embed_record_exports,
5473
- "./lexicons/app.bsky.embed.recordWithMedia.json": app_bsky_embed_recordWithMedia_exports,
5474
- "./lexicons/app.bsky.embed.video.json": app_bsky_embed_video_exports,
5475
- "./lexicons/app.bsky.feed.defs.json": app_bsky_feed_defs_exports,
5476
- "./lexicons/app.bsky.feed.like.json": app_bsky_feed_like_exports,
5477
- "./lexicons/app.bsky.feed.post.json": app_bsky_feed_post_exports,
5478
- "./lexicons/app.bsky.feed.repost.json": app_bsky_feed_repost_exports,
5479
- "./lexicons/app.bsky.feed.threadgate.json": app_bsky_feed_threadgate_exports,
5480
- "./lexicons/app.bsky.graph.block.json": app_bsky_graph_block_exports,
5481
- "./lexicons/app.bsky.graph.defs.json": app_bsky_graph_defs_exports,
5482
- "./lexicons/app.bsky.graph.follow.json": app_bsky_graph_follow_exports,
5483
- "./lexicons/app.bsky.graph.list.json": app_bsky_graph_list_exports,
5484
- "./lexicons/app.bsky.graph.listitem.json": app_bsky_graph_listitem_exports,
5485
- "./lexicons/app.bsky.notification.defs.json": app_bsky_notification_defs_exports,
5486
- "./lexicons/app.bsky.richtext.facet.json": app_bsky_richtext_facet_exports,
5487
- "./lexicons/com.atproto.label.defs.json": com_atproto_label_defs_exports,
5488
- "./lexicons/com.atproto.repo.strongRef.json": com_atproto_repo_strongRef_exports
5489
- };
5490
- for (const schema of Object.values(schemas)) this.lex.add(schema.default);
5491
- }
5492
- /**
5493
- * Validate a record against its lexicon schema.
5494
- *
5495
- * @param collection - The NSID of the record type (e.g., "app.bsky.feed.post")
5496
- * @param record - The record object to validate
5497
- * @throws {Error} If validation fails and schema is loaded
5498
- */
5499
- validateRecord(collection, record) {
5500
- if (!this.hasSchema(collection)) {
5501
- if (this.strictMode) throw new Error(`No lexicon schema loaded for collection: ${collection}. Enable optimistic validation or add the schema.`);
5502
- return;
5503
- }
5504
- const lexRecord = jsonToLex(record);
5505
- try {
5506
- this.lex.assertValidRecord(collection, lexRecord);
5507
- } catch (error) {
5508
- const message = error instanceof Error ? error.message : String(error);
5509
- throw new Error(`Lexicon validation failed for ${collection}: ${message}`);
2404
+ try {
2405
+ parse(schema, record);
2406
+ } catch (error) {
2407
+ if (error instanceof ValidationError) throw new Error(`Lexicon validation failed for ${collection}: ${error.message}`);
2408
+ throw error;
5510
2409
  }
5511
2410
  }
5512
2411
  /**
5513
2412
  * Check if a schema is loaded for a collection.
5514
2413
  */
5515
2414
  hasSchema(collection) {
5516
- try {
5517
- this.lex.getDefOrThrow(collection);
5518
- return true;
5519
- } catch {
5520
- return false;
5521
- }
5522
- }
5523
- /**
5524
- * Add a lexicon schema to the validator.
5525
- *
5526
- * @param doc - The lexicon document to add
5527
- *
5528
- * @example
5529
- * ```ts
5530
- * validator.addSchema({
5531
- * lexicon: 1,
5532
- * id: "com.example.post",
5533
- * defs: { ... }
5534
- * })
5535
- * ```
5536
- */
5537
- addSchema(doc) {
5538
- this.lex.add(doc);
2415
+ return collection in recordSchemas;
5539
2416
  }
5540
2417
  /**
5541
2418
  * Get list of all loaded schema NSIDs.
5542
2419
  */
5543
2420
  getLoadedSchemas() {
5544
- return Array.from(this.lex).map((doc) => doc.id);
5545
- }
5546
- /**
5547
- * Get the underlying Lexicons instance for advanced usage.
5548
- */
5549
- getLexicons() {
5550
- return this.lex;
2421
+ return Object.keys(recordSchemas);
5551
2422
  }
5552
2423
  };
5553
2424
  /**
5554
2425
  * Shared validator instance (singleton pattern).
5555
2426
  * Uses optimistic validation by default (strict: false).
5556
- *
5557
- * Automatically loads all schemas from ./lexicons/*.json
5558
- *
5559
- * Additional schemas can be added:
5560
- * ```ts
5561
- * import { validator } from './validation'
5562
- * validator.addSchema(myCustomSchema)
5563
- * ```
5564
2427
  */
5565
2428
  const validator = new RecordValidator({ strict: false });
5566
2429
 
@@ -5592,14 +2455,10 @@ async function describeRepo(c, accountDO) {
5592
2455
  error: "InvalidRequest",
5593
2456
  message: "Missing required parameter: repo"
5594
2457
  }, 400);
5595
- try {
5596
- ensureValidDid(repo);
5597
- } catch (err) {
5598
- return c.json({
5599
- error: "InvalidRequest",
5600
- message: `Invalid DID format: ${err instanceof Error ? err.message : String(err)}`
5601
- }, 400);
5602
- }
2458
+ if (!isDid(repo)) return c.json({
2459
+ error: "InvalidRequest",
2460
+ message: "Invalid DID format"
2461
+ }, 400);
5603
2462
  if (repo !== c.env.DID) return c.json({
5604
2463
  error: "RepoNotFound",
5605
2464
  message: `Repository not found: ${repo}`
@@ -5631,14 +2490,10 @@ async function getRecord(c, accountDO) {
5631
2490
  error: "InvalidRequest",
5632
2491
  message: "Missing required parameters: repo, collection, rkey"
5633
2492
  }, 400);
5634
- try {
5635
- ensureValidDid(repo);
5636
- } catch (err) {
5637
- return c.json({
5638
- error: "InvalidRequest",
5639
- message: `Invalid DID format: ${err instanceof Error ? err.message : String(err)}`
5640
- }, 400);
5641
- }
2493
+ if (!isDid(repo)) return c.json({
2494
+ error: "InvalidRequest",
2495
+ message: "Invalid DID format"
2496
+ }, 400);
5642
2497
  if (repo !== c.env.DID) return c.json({
5643
2498
  error: "RepoNotFound",
5644
2499
  message: `Repository not found: ${repo}`
@@ -5649,7 +2504,7 @@ async function getRecord(c, accountDO) {
5649
2504
  message: `Record not found: ${collection}/${rkey}`
5650
2505
  }, 404);
5651
2506
  return c.json({
5652
- uri: AtUri.make(repo, collection, rkey).toString(),
2507
+ uri: `at://${repo}/${collection}/${rkey}`,
5653
2508
  cid: result.cid,
5654
2509
  value: result.record
5655
2510
  });
@@ -5664,14 +2519,10 @@ async function listRecords(c, accountDO) {
5664
2519
  error: "InvalidRequest",
5665
2520
  message: "Missing required parameters: repo, collection"
5666
2521
  }, 400);
5667
- try {
5668
- ensureValidDid(repo);
5669
- } catch (err) {
5670
- return c.json({
5671
- error: "InvalidRequest",
5672
- message: `Invalid DID format: ${err instanceof Error ? err.message : String(err)}`
5673
- }, 400);
5674
- }
2522
+ if (!isDid(repo)) return c.json({
2523
+ error: "InvalidRequest",
2524
+ message: "Invalid DID format"
2525
+ }, 400);
5675
2526
  if (repo !== c.env.DID) return c.json({
5676
2527
  error: "RepoNotFound",
5677
2528
  message: `Repository not found: ${repo}`
@@ -5981,7 +2832,9 @@ async function getAccountStatus(c, accountDO) {
5981
2832
  accountDO.rpcCountExpectedBlobs(),
5982
2833
  accountDO.rpcCountImportedBlobs()
5983
2834
  ]);
2835
+ const activated = active || indexedRecords > 0;
5984
2836
  return c.json({
2837
+ activated,
5985
2838
  active,
5986
2839
  validDid: true,
5987
2840
  repoCommit: status.head,
@@ -5994,6 +2847,7 @@ async function getAccountStatus(c, accountDO) {
5994
2847
  });
5995
2848
  } catch (err) {
5996
2849
  return c.json({
2850
+ activated: false,
5997
2851
  active: false,
5998
2852
  validDid: true,
5999
2853
  repoCommit: null,
@@ -6077,7 +2931,7 @@ async function resetMigration(c, accountDO) {
6077
2931
 
6078
2932
  //#endregion
6079
2933
  //#region package.json
6080
- var version = "0.2.5";
2934
+ var version = "0.3.0";
6081
2935
 
6082
2936
  //#endregion
6083
2937
  //#region src/index.ts
@@ -6092,12 +2946,8 @@ for (const key of [
6092
2946
  "JWT_SECRET",
6093
2947
  "PASSWORD_HASH"
6094
2948
  ]) if (!env$1[key]) throw new Error(`Missing required environment variable: ${key}`);
6095
- try {
6096
- ensureValidDid(env$1.DID);
6097
- ensureValidHandle(env$1.HANDLE);
6098
- } catch (err) {
6099
- throw new Error(`Invalid DID or handle: ${err instanceof Error ? err.message : String(err)}`);
6100
- }
2949
+ if (!isDid(env$1.DID)) throw new Error(`Invalid DID format: ${env$1.DID}`);
2950
+ if (!isHandle(env$1.HANDLE)) throw new Error(`Invalid handle format: ${env$1.HANDLE}`);
6101
2951
  const didResolver = new DidResolver({
6102
2952
  didCache: new WorkersDidCache(),
6103
2953
  timeout: 3e3,
@@ -6123,11 +2973,11 @@ app.use("*", cors({
6123
2973
  maxAge: 86400
6124
2974
  }));
6125
2975
  function getAccountDO(env$2) {
6126
- const id$22 = env$2.ACCOUNT.idFromName("account");
6127
- return env$2.ACCOUNT.get(id$22);
2976
+ const id = env$2.ACCOUNT.idFromName("account");
2977
+ return env$2.ACCOUNT.get(id);
6128
2978
  }
6129
2979
  app.get("/.well-known/did.json", (c) => {
6130
- const didDocument$1 = {
2980
+ const didDocument = {
6131
2981
  "@context": [
6132
2982
  "https://www.w3.org/ns/did/v1",
6133
2983
  "https://w3id.org/security/multikey/v1",
@@ -6147,16 +2997,26 @@ app.get("/.well-known/did.json", (c) => {
6147
2997
  serviceEndpoint: `https://${c.env.PDS_HOSTNAME}`
6148
2998
  }]
6149
2999
  };
6150
- return c.json(didDocument$1);
3000
+ return c.json(didDocument);
6151
3001
  });
6152
3002
  app.get("/.well-known/atproto-did", (c) => {
6153
3003
  if (c.env.HANDLE !== c.env.PDS_HOSTNAME) return c.notFound();
6154
3004
  return new Response(c.env.DID, { headers: { "Content-Type": "text/plain" } });
6155
3005
  });
6156
- app.get("/health", (c) => c.json({
6157
- status: "ok",
6158
- version
6159
- }));
3006
+ app.get("/xrpc/_health", async (c) => {
3007
+ try {
3008
+ await getAccountDO(c.env).rpcHealthCheck();
3009
+ return c.json({
3010
+ status: "ok",
3011
+ version
3012
+ });
3013
+ } catch {
3014
+ return c.json({
3015
+ status: "unhealthy",
3016
+ version
3017
+ }, 503);
3018
+ }
3019
+ });
6160
3020
  app.get("/", (c) => {
6161
3021
  const html = `<!DOCTYPE html>
6162
3022
  <html lang="en">
@@ -6256,6 +3116,10 @@ app.post("/admin/emit-identity", requireAuth, async (c) => {
6256
3116
  const result = await getAccountDO(c.env).rpcEmitIdentityEvent(c.env.HANDLE);
6257
3117
  return c.json(result);
6258
3118
  });
3119
+ app.get("/xrpc/gg.mk.experimental.getFirehoseStatus", requireAuth, async (c) => {
3120
+ const accountDO = getAccountDO(c.env);
3121
+ return c.json(await accountDO.rpcGetFirehoseStatus());
3122
+ });
6259
3123
  const oauthApp = createOAuthApp(getAccountDO);
6260
3124
  app.route("/", oauthApp);
6261
3125
  app.all("/xrpc/*", (c) => handleXrpcProxy(c, didResolver, getKeypair));