@graffiti-garden/implementation-decentralized 0.0.2 → 0.0.4

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.
Files changed (48) hide show
  1. package/dist/1-services/4-inboxes-tests.d.ts.map +1 -1
  2. package/dist/1-services/4-inboxes.d.ts +3 -3
  3. package/dist/1-services/4-inboxes.d.ts.map +1 -1
  4. package/dist/3-protocol/3-object-encoding.d.ts.map +1 -1
  5. package/dist/3-protocol/4-graffiti.d.ts +2 -1
  6. package/dist/3-protocol/4-graffiti.d.ts.map +1 -1
  7. package/dist/3-protocol/login-dialog.html.d.ts +1 -1
  8. package/dist/3-protocol/login-dialog.html.d.ts.map +1 -1
  9. package/dist/browser/index.js +7 -7
  10. package/dist/browser/index.js.map +3 -3
  11. package/dist/browser/login-dialog.html-VTDKJZBG.js +44 -0
  12. package/dist/browser/login-dialog.html-VTDKJZBG.js.map +7 -0
  13. package/dist/browser/{style-YUTCEBZV-RWYJV575.js → style-RMTPI5KV-Y5KAOOZR.js} +19 -36
  14. package/dist/browser/style-RMTPI5KV-Y5KAOOZR.js.map +7 -0
  15. package/dist/cjs/1-services/4-inboxes-tests.js +2 -0
  16. package/dist/cjs/1-services/4-inboxes-tests.js.map +2 -2
  17. package/dist/cjs/1-services/4-inboxes.js +17 -8
  18. package/dist/cjs/1-services/4-inboxes.js.map +2 -2
  19. package/dist/cjs/3-protocol/1-sessions.js +1 -1
  20. package/dist/cjs/3-protocol/1-sessions.js.map +2 -2
  21. package/dist/cjs/3-protocol/3-object-encoding.js +3 -2
  22. package/dist/cjs/3-protocol/3-object-encoding.js.map +2 -2
  23. package/dist/cjs/3-protocol/4-graffiti.js +193 -135
  24. package/dist/cjs/3-protocol/4-graffiti.js.map +3 -3
  25. package/dist/cjs/3-protocol/login-dialog.html.js +9 -9
  26. package/dist/cjs/3-protocol/login-dialog.html.js.map +1 -1
  27. package/dist/esm/1-services/4-inboxes-tests.js +2 -0
  28. package/dist/esm/1-services/4-inboxes-tests.js.map +2 -2
  29. package/dist/esm/1-services/4-inboxes.js +19 -9
  30. package/dist/esm/1-services/4-inboxes.js.map +2 -2
  31. package/dist/esm/3-protocol/1-sessions.js +1 -1
  32. package/dist/esm/3-protocol/1-sessions.js.map +2 -2
  33. package/dist/esm/3-protocol/3-object-encoding.js +3 -2
  34. package/dist/esm/3-protocol/3-object-encoding.js.map +2 -2
  35. package/dist/esm/3-protocol/4-graffiti.js +194 -135
  36. package/dist/esm/3-protocol/4-graffiti.js.map +3 -3
  37. package/dist/esm/3-protocol/login-dialog.html.js +9 -9
  38. package/dist/esm/3-protocol/login-dialog.html.js.map +1 -1
  39. package/package.json +7 -7
  40. package/src/1-services/4-inboxes-tests.ts +2 -0
  41. package/src/1-services/4-inboxes.ts +25 -15
  42. package/src/3-protocol/1-sessions.ts +1 -1
  43. package/src/3-protocol/3-object-encoding.ts +4 -2
  44. package/src/3-protocol/4-graffiti.ts +260 -170
  45. package/src/3-protocol/login-dialog.html.ts +9 -9
  46. package/dist/browser/login-dialog.html-XUWYDNNI.js +0 -44
  47. package/dist/browser/login-dialog.html-XUWYDNNI.js.map +0 -7
  48. package/dist/browser/style-YUTCEBZV-RWYJV575.js.map +0 -7
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/1-services/4-inboxes.ts"],
4
- "sourcesContent": ["import type { JSONSchema, GraffitiObject } from \"@graffiti-garden/api\";\nimport {\n getAuthorizationEndpoint,\n fetchWithErrorHandling,\n verifyHTTPSEndpoint,\n} from \"./utilities\";\nimport {\n compileGraffitiObjectSchema,\n GraffitiErrorCursorExpired,\n} from \"@graffiti-garden/api\";\nimport {\n encode as dagCborEncode,\n decode as dagCborDecode,\n} from \"@ipld/dag-cbor\";\nimport {\n type infer as infer_,\n string,\n url,\n array,\n optional,\n nullable,\n strictObject,\n looseObject,\n nonnegative,\n int,\n boolean,\n custom,\n number,\n union,\n} from \"zod/mini\";\n\nexport class Inboxes {\n getAuthorizationEndpoint = getAuthorizationEndpoint;\n protected cache_: Promise<Cache> | null = null;\n protected get cache() {\n if (!this.cache_) {\n this.cache_ = createCache();\n }\n return this.cache_;\n }\n\n async send(inboxUrl: string, message: Message<{}>): Promise<string> {\n verifyHTTPSEndpoint(inboxUrl);\n const url = `${inboxUrl}/send`;\n\n const response = await fetchWithErrorHandling(url, {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/cbor\",\n },\n body: new Uint8Array(dagCborEncode({ m: message })),\n });\n\n const blob = await response.blob();\n const cbor = dagCborDecode(await blob.arrayBuffer());\n const parsed = SendResponseSchema.parse(cbor);\n return parsed.id;\n }\n\n async get(\n inboxUrl: string,\n messageId: string,\n inboxToken?: string | null,\n ): Promise<LabeledMessageBase> {\n const messageCacheKey = getMessageCacheKey(inboxUrl, messageId);\n const cache = await this.cache;\n const cached = await cache.messages.get(messageCacheKey);\n if (cached) return cached;\n\n const url = `${inboxUrl}/message/${messageId}`;\n const response = await fetchWithErrorHandling(url, {\n method: \"GET\",\n headers: {\n ...(inboxToken\n ? {\n Authorization: `Bearer ${inboxToken}`,\n }\n : {}),\n },\n });\n\n const blob = await response.blob();\n const cbor = dagCborDecode(await blob.arrayBuffer());\n const parsed = LabeledMessageBaseSchema.parse(cbor);\n\n await cache.messages.set(messageCacheKey, parsed);\n return parsed;\n }\n\n async label(\n inboxUrl: string,\n messageId: string,\n label: number,\n inboxToken?: string | null,\n ): Promise<void> {\n verifyHTTPSEndpoint(inboxUrl);\n\n if (inboxToken) {\n const url = `${inboxUrl}/label/${messageId}`;\n\n await fetchWithErrorHandling(url, {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/cbor\",\n Authorization: `Bearer ${inboxToken}`,\n },\n body: new Uint8Array(dagCborEncode({ l: label })),\n });\n }\n\n // Update the cache, even if no token.\n // Therefore people not logged in do not need to\n // repeatedly re-validate objects.\n const cache = await this.cache;\n const messageCacheKey = getMessageCacheKey(inboxUrl, messageId);\n const result = await cache.messages.get(messageCacheKey);\n if (result) {\n await cache.messages.set(messageCacheKey, {\n ...result,\n l: label,\n });\n }\n }\n\n protected async fetchMessageBatch(\n inboxUrl: string,\n type: \"query\" | \"export\",\n body: Uint8Array<ArrayBuffer> | undefined,\n inboxToken?: string | null,\n cursor?: string,\n ) {\n const response = await fetchWithErrorHandling(\n `${inboxUrl}/${type}${cursor ? `?cursor=${encodeURIComponent(cursor)}` : \"\"}`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/cbor\",\n ...(inboxToken\n ? {\n Authorization: `Bearer ${inboxToken}`,\n }\n : {}),\n },\n body,\n },\n );\n const retryAfterHeader = response.headers.get(\"Retry-After\");\n const retryAfter = retryAfterHeader\n ? parseInt(retryAfterHeader)\n : undefined;\n\n const waitTil =\n retryAfter && Number.isFinite(retryAfter)\n ? Date.now() + retryAfter * 1000\n : undefined;\n\n return { response, waitTil };\n }\n\n protected async *yieldFromCache(\n cache: Cache,\n inboxUrl: string,\n messageIdsCacheKey: string,\n cachedMessageIds: CacheQueryValue,\n cacheNumSeen: number = 0,\n ): AsyncGenerator<LabeledMessageBase> {\n // Filter out all messageIds before\n // the number already seen\n const messageIds = cachedMessageIds.messageIds.slice(cacheNumSeen);\n\n // Get all the messages pointed to in the cache\n const messages = await Promise.all(\n messageIds.map(async (id) => {\n const message = await cache.messages.get(\n getMessageCacheKey(inboxUrl, id),\n );\n if (!message) {\n // Something is very wrong with the cache,\n // it refers to message IDs that are not cached\n try {\n await cache.messageIds.del(messageIdsCacheKey);\n } catch {}\n throw new Error(\"Cache out of sync - perhaps clear browser storage\");\n }\n return message;\n }),\n );\n\n yield* messages;\n }\n\n protected async *lockedMessageStreamer<Schema extends JSONSchema>(\n ...args: Parameters<typeof this.messageStreamer<Schema>>\n ): MessageStream<Schema> {\n if (typeof window === \"undefined\" || !(await canUseIDB())) {\n // TODO: implement locking in node as well, but not\n // high priority since most use will be in browser\n const streamer = this.messageStreamer<Schema>(...args);\n while (true) {\n const next = await streamer.next();\n if (next.done) return next.value;\n yield next.value;\n }\n }\n\n // Request the lock\n const messageIdsCacheKey = await args[0];\n const lockKey = `graffiti:inbox:${messageIdsCacheKey}`;\n let releaseLock = () => {};\n let hasLock: boolean = false;\n await new Promise<void>((resolvehasLock) => {\n window.navigator.locks.request(\n lockKey,\n {\n mode: \"exclusive\",\n ifAvailable: true,\n },\n async (lock) => {\n // Immediately return whether we\n // acquired the lock or not\n hasLock = !!lock;\n resolvehasLock();\n\n // Then wait for the release to be called\n await new Promise<void>((r) => (releaseLock = r));\n },\n );\n });\n if (hasLock) {\n // If we have the lock, simply proceed with the regular streamer\n try {\n const streamer = this.messageStreamer<Schema>(...args);\n while (true) {\n const next = await streamer.next();\n if (next.done) return next.value;\n yield next.value;\n }\n } finally {\n // Release the lock when all done\n releaseLock();\n }\n }\n\n // Someone else has the lock,\n // so wait until the lock is released,\n // then just return from the cache\n releaseLock();\n await window.navigator.locks.request(lockKey, () => {});\n\n // TODO: the arguments here are brittle\n // at some point, refactor things\n const inboxUrl = args[1];\n const objectSchema = args[5] ?? {};\n const cacheVersion = args[6];\n const cacheNumSeen = args[7];\n\n const cache = await this.cache;\n const cachedMessageIds = await cache.messageIds.get(messageIdsCacheKey);\n if (!cachedMessageIds) {\n throw new Error(\"Cache not found\");\n }\n if (\n cacheVersion !== undefined &&\n cacheVersion !== cachedMessageIds.version\n ) {\n throw new GraffitiErrorCursorExpired(\"Cursor is stale\");\n }\n\n const iterator = this.yieldFromCache(\n cache,\n inboxUrl,\n messageIdsCacheKey,\n cachedMessageIds,\n cacheNumSeen,\n );\n for await (const m of iterator) yield m as LabeledMessage<Schema>;\n\n const outputCursor: infer_<typeof CursorSchema> = {\n numSeen: cachedMessageIds.messageIds.length,\n version: cachedMessageIds.version,\n messageIdsCacheKey,\n objectSchema,\n };\n\n return JSON.stringify(outputCursor);\n }\n\n protected async *messageStreamer<Schema extends JSONSchema>(\n messageIdsCacheKey_: Promise<string>,\n inboxUrl: string,\n type: \"export\" | \"query\",\n body: Uint8Array<ArrayBuffer> | undefined,\n inboxToken?: string | null,\n objectSchema: Schema = {} as Schema,\n cacheVersion?: string,\n cacheNumSeen: number = 0,\n ): MessageStream<Schema> {\n const validator = await compileGraffitiObjectSchema(objectSchema);\n const messageIdsCacheKey = await messageIdsCacheKey_;\n const cache = await this.cache;\n\n let cachedMessageIds = await cache.messageIds.get(messageIdsCacheKey);\n if (\n cacheVersion !== undefined &&\n cacheVersion !== cachedMessageIds?.version\n ) {\n throw new GraffitiErrorCursorExpired(\"Cursor is stale\");\n }\n\n // If we are rate-limited, wait\n let waitTil = cachedMessageIds?.waitTil;\n await waitFor(waitTil);\n\n // See if the cursor is still active by\n // requesting an initial batch of messages\n const cachedCursor = cachedMessageIds?.cursor;\n let firstResponse: Response | undefined = undefined;\n try {\n const out = await this.fetchMessageBatch(\n inboxUrl,\n type,\n body,\n inboxToken,\n cachedCursor,\n );\n firstResponse = out.response;\n waitTil = out.waitTil;\n } catch (e) {\n if (!(e instanceof GraffitiErrorCursorExpired && cachedCursor)) {\n console.error(\n \"Unexpected error in stream, waiting 5 seconds before continuing...\",\n );\n await new Promise((resolve) => setTimeout(resolve, 5000));\n throw e;\n }\n\n // The cursor is stale\n await cache.messageIds.del(messageIdsCacheKey);\n if (cacheVersion === undefined) {\n // The query is not a continuation\n // so we can effectively ignore the error\n cachedMessageIds = undefined;\n } else {\n // Otherwise propogate it up so the\n // consumer can clear their message history\n throw e;\n }\n }\n\n if (firstResponse !== undefined && cachedMessageIds) {\n // Cursor is valid! Yield from the cache\n const iterator = this.yieldFromCache(\n cache,\n inboxUrl,\n messageIdsCacheKey,\n cachedMessageIds,\n cacheNumSeen,\n );\n for await (const m of iterator) yield m as LabeledMessage<Schema>;\n }\n\n if (firstResponse === undefined) {\n // The cursor was stale: try again\n const out = await this.fetchMessageBatch(\n inboxUrl,\n type,\n body,\n inboxToken,\n );\n firstResponse = out.response;\n waitTil = out.waitTil;\n }\n\n // Continue streaming results\n let response = firstResponse;\n let cursor: string;\n const version = cachedMessageIds?.version ?? crypto.randomUUID();\n let messageIds = cachedMessageIds?.messageIds ?? [];\n while (true) {\n const blob = await response.blob();\n const decoded = dagCborDecode(await blob.arrayBuffer());\n const {\n results,\n hasMore,\n cursor: nextCursor,\n } = MessageResultSchema.parse(decoded);\n cursor = nextCursor;\n\n const labeledMessages: LabeledMessage<Schema>[] = results.map(\n (result) => {\n const object =\n result[LABELED_MESSAGE_MESSAGE_KEY][MESSAGE_OBJECT_KEY];\n if (!validator(object)) {\n throw new Error(\"Server returned data that does not match schema\");\n }\n return {\n ...result,\n [LABELED_MESSAGE_MESSAGE_KEY]: {\n ...result[LABELED_MESSAGE_MESSAGE_KEY],\n [MESSAGE_OBJECT_KEY]: object,\n },\n };\n },\n );\n\n // First cache the messages with their labels\n await Promise.all(\n labeledMessages.map((m: LabeledMessageBase) =>\n cache.messages.set(\n getMessageCacheKey(inboxUrl, m[LABELED_MESSAGE_ID_KEY]),\n m,\n ),\n ),\n );\n // Then store all the messageids\n messageIds = [\n ...messageIds,\n ...labeledMessages.map(\n (m: LabeledMessageBase) => m[LABELED_MESSAGE_ID_KEY],\n ),\n ];\n await cache.messageIds.set(messageIdsCacheKey, {\n cursor,\n version,\n messageIds,\n waitTil,\n });\n\n // Update how many we've seen\n cacheNumSeen += labeledMessages.length;\n\n // Return the values\n for (const m of labeledMessages) yield m;\n\n if (!hasMore) break;\n\n // Otherwise get another response (after waiting for rate-limit)\n await waitFor(waitTil);\n const out = await this.fetchMessageBatch(\n inboxUrl,\n type,\n undefined, // Body is never past the first time\n inboxToken,\n cursor,\n );\n response = out.response;\n waitTil = out.waitTil;\n }\n\n const outputCursor: infer_<typeof CursorSchema> = {\n numSeen: cacheNumSeen,\n version,\n messageIdsCacheKey,\n objectSchema,\n };\n\n return JSON.stringify(outputCursor);\n }\n\n query<Schema extends JSONSchema>(\n inboxUrl: string,\n tags: Uint8Array[],\n objectSchema: Schema,\n inboxToken?: string | null,\n ): MessageStream<Schema> {\n verifyHTTPSEndpoint(inboxUrl);\n\n const body = dagCborEncode({\n tags,\n schema: objectSchema,\n });\n\n const messageIdsCacheKey = getMessageIdsCacheKey(inboxUrl, \"query\", body);\n return this.lockedMessageStreamer<Schema>(\n messageIdsCacheKey,\n inboxUrl,\n \"query\",\n new Uint8Array(body),\n inboxToken,\n objectSchema,\n );\n }\n\n continueQuery(\n inboxUrl: string,\n cursor: string,\n inboxToken?: string | null,\n ): MessageStream<{}> {\n verifyHTTPSEndpoint(inboxUrl);\n\n const decodedCursor = JSON.parse(cursor);\n const { messageIdsCacheKey, numSeen, objectSchema, version } =\n CursorSchema.parse(decodedCursor);\n\n return this.lockedMessageStreamer<{}>(\n Promise.resolve(messageIdsCacheKey),\n inboxUrl,\n \"query\",\n undefined,\n inboxToken,\n objectSchema,\n version,\n numSeen,\n );\n }\n\n export(inboxUrl: string, inboxToken: string): MessageStream<{}> {\n verifyHTTPSEndpoint(inboxUrl);\n const messageIdsCacheKey = getMessageIdsCacheKey(inboxUrl, \"export\");\n return this.lockedMessageStreamer<{}>(\n messageIdsCacheKey,\n inboxUrl,\n \"export\",\n undefined,\n inboxToken,\n );\n }\n}\n\nconst GraffitiObjectSchema = strictObject({\n value: looseObject({}),\n channels: array(string()),\n allowed: optional(nullable(array(url()))),\n url: url(),\n actor: url(),\n});\nexport const Uint8ArraySchema = custom<Uint8Array>(\n (v): v is Uint8Array => v instanceof Uint8Array,\n);\nexport const TagsSchema = array(Uint8ArraySchema);\n\nexport const MESSAGE_TAGS_KEY = \"t\";\nexport const MESSAGE_OBJECT_KEY = \"o\";\nexport const MESSAGE_METADATA_KEY = \"m\";\nexport const MessageBaseSchema = strictObject({\n [MESSAGE_TAGS_KEY]: TagsSchema,\n [MESSAGE_OBJECT_KEY]: GraffitiObjectSchema,\n [MESSAGE_METADATA_KEY]: Uint8ArraySchema,\n});\ntype MessageBase = infer_<typeof MessageBaseSchema>;\n\nexport const LABELED_MESSAGE_ID_KEY = \"id\";\nexport const LABELED_MESSAGE_MESSAGE_KEY = \"m\";\nexport const LABELED_MESSAGE_LABEL_KEY = \"l\";\nexport const LabeledMessageBaseSchema = strictObject({\n [LABELED_MESSAGE_ID_KEY]: string(),\n [LABELED_MESSAGE_MESSAGE_KEY]: MessageBaseSchema,\n [LABELED_MESSAGE_LABEL_KEY]: number(),\n});\ntype LabeledMessageBase = infer_<typeof LabeledMessageBaseSchema>;\n\nexport type Message<Schema extends JSONSchema> = MessageBase & {\n [MESSAGE_OBJECT_KEY]: GraffitiObject<Schema>;\n};\nexport type LabeledMessage<Schema extends JSONSchema> = LabeledMessageBase & {\n [LABELED_MESSAGE_MESSAGE_KEY]: {\n [MESSAGE_OBJECT_KEY]: GraffitiObject<Schema>;\n };\n};\n\nconst SendResponseSchema = strictObject({ id: string() });\n\nconst MessageResultSchema = strictObject({\n results: array(LabeledMessageBaseSchema),\n hasMore: boolean(),\n cursor: string(),\n});\n\nconst CursorSchema = strictObject({\n messageIdsCacheKey: string(),\n version: string(),\n numSeen: int().check(nonnegative()),\n objectSchema: union([looseObject({}), boolean()]),\n});\n\nexport interface MessageStream<\n Schema extends JSONSchema,\n> extends AsyncGenerator<LabeledMessage<Schema>, string> {}\n\ntype CacheQueryValue = {\n cursor: string;\n version: string;\n messageIds: string[];\n waitTil?: number;\n};\n\nasync function canUseIDB(): Promise<boolean> {\n try {\n if (!globalThis.indexedDB) return false;\n\n // Small probe database\n await new Promise<void>((resolve, reject) => {\n const req = indexedDB.open(\"__idb_probe__\", 1);\n req.onupgradeneeded = () => req.result.createObjectStore(\"k\");\n req.onsuccess = () => {\n req.result.close();\n resolve();\n };\n req.onerror = () => reject(req.error);\n });\n\n return true;\n } catch {\n return false;\n }\n}\n\ntype Cache = {\n messages: {\n get(k: string): Promise<LabeledMessageBase | undefined>;\n set(k: string, value: LabeledMessageBase): Promise<void>;\n del(k: string): Promise<void>;\n };\n messageIds: {\n get(k: string): Promise<CacheQueryValue | undefined>;\n set(k: string, value: CacheQueryValue): Promise<void>;\n del(k: string): Promise<void>;\n };\n};\n\nfunction getMessageCacheKey(inboxUrl: string, messageId: string) {\n return `${encodeURIComponent(inboxUrl)}:${encodeURIComponent(messageId)}`;\n}\nasync function getMessageIdsCacheKey(\n inboxUrl: string,\n type: \"query\" | \"export\",\n body?: Uint8Array,\n): Promise<string> {\n const cacheIdData = dagCborEncode({\n inboxUrl,\n type,\n body: body ?? null,\n });\n return crypto.subtle\n .digest(\"SHA-256\", new Uint8Array(cacheIdData))\n .then((bytes) =>\n Array.from(new Uint8Array(bytes))\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\"),\n );\n}\n\nasync function resetCacheDB() {\n await new Promise<void>((resolve) => {\n const req = indexedDB.deleteDatabase(\"graffiti-inbox-cache\");\n req.onsuccess = () => resolve();\n req.onerror = () => resolve(); // best effort\n req.onblocked = () => resolve(); // best effort\n });\n}\n\nasync function createCache(): Promise<Cache> {\n if (await canUseIDB()) {\n const { openDB } = await import(\"idb\");\n const db = await openDB(\"graffiti-inbox-cache\", 1, {\n upgrade(db) {\n if (!db.objectStoreNames.contains(\"m\")) db.createObjectStore(\"m\");\n if (!db.objectStoreNames.contains(\"q\")) db.createObjectStore(\"q\");\n },\n });\n\n return {\n messages: {\n get: async (k) => {\n try {\n return db.get(\"m\", k);\n } catch (error) {\n console.error(\"Error getting message from cache:\", error);\n console.error(\"resetting cache...\");\n await resetCacheDB();\n return undefined;\n }\n },\n set: async (k, v) => {\n await db.put(\"m\", v, k);\n },\n del: (k) => db.delete(\"m\", k),\n },\n messageIds: {\n get: async (k) => {\n try {\n return await db.get(\"q\", k);\n } catch (error) {\n console.error(\"Error getting message IDs from cache:\", error);\n console.error(\"resetting cache...\");\n await resetCacheDB();\n return undefined;\n }\n },\n set: async (k, v) => {\n await db.put(\"q\", v, k);\n },\n del: (k) => db.delete(\"q\", k),\n },\n };\n }\n\n const m = new Map<string, LabeledMessageBase>();\n const q = new Map<string, CacheQueryValue>();\n\n return {\n messages: {\n get: async (k) => m.get(k),\n set: async (k, v) => void m.set(k, v),\n del: async (k) => void m.delete(k),\n },\n messageIds: {\n get: async (k) => q.get(k),\n set: async (k, v) => void q.set(k, v),\n del: async (k) => void q.delete(k),\n },\n };\n}\n\nasync function waitFor(waitTil?: number) {\n if (waitTil !== undefined) {\n const waitFor = waitTil - Date.now();\n if (waitFor > 0) {\n await new Promise((resolve) => setTimeout(resolve, waitFor));\n }\n }\n}\n"],
5
- "mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE,UAAU;AAAA,EACV,UAAU;AAAA,OACL;AACP;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEA,MAAM,QAAQ;AAAA,EACnB,2BAA2B;AAAA,EACjB,SAAgC;AAAA,EAC1C,IAAc,QAAQ;AACpB,QAAI,CAAC,KAAK,QAAQ;AAChB,WAAK,SAAS,YAAY;AAAA,IAC5B;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,KAAK,UAAkB,SAAuC;AAClE,wBAAoB,QAAQ;AAC5B,UAAMA,OAAM,GAAG,QAAQ;AAEvB,UAAM,WAAW,MAAM,uBAAuBA,MAAK;AAAA,MACjD,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,MACA,MAAM,IAAI,WAAW,cAAc,EAAE,GAAG,QAAQ,CAAC,CAAC;AAAA,IACpD,CAAC;AAED,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,UAAM,OAAO,cAAc,MAAM,KAAK,YAAY,CAAC;AACnD,UAAM,SAAS,mBAAmB,MAAM,IAAI;AAC5C,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,MAAM,IACJ,UACA,WACA,YAC6B;AAC7B,UAAM,kBAAkB,mBAAmB,UAAU,SAAS;AAC9D,UAAM,QAAQ,MAAM,KAAK;AACzB,UAAM,SAAS,MAAM,MAAM,SAAS,IAAI,eAAe;AACvD,QAAI,OAAQ,QAAO;AAEnB,UAAMA,OAAM,GAAG,QAAQ,YAAY,SAAS;AAC5C,UAAM,WAAW,MAAM,uBAAuBA,MAAK;AAAA,MACjD,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,GAAI,aACA;AAAA,UACE,eAAe,UAAU,UAAU;AAAA,QACrC,IACA,CAAC;AAAA,MACP;AAAA,IACF,CAAC;AAED,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,UAAM,OAAO,cAAc,MAAM,KAAK,YAAY,CAAC;AACnD,UAAM,SAAS,yBAAyB,MAAM,IAAI;AAElD,UAAM,MAAM,SAAS,IAAI,iBAAiB,MAAM;AAChD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,MACJ,UACA,WACA,OACA,YACe;AACf,wBAAoB,QAAQ;AAE5B,QAAI,YAAY;AACd,YAAMA,OAAM,GAAG,QAAQ,UAAU,SAAS;AAE1C,YAAM,uBAAuBA,MAAK;AAAA,QAChC,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe,UAAU,UAAU;AAAA,QACrC;AAAA,QACA,MAAM,IAAI,WAAW,cAAc,EAAE,GAAG,MAAM,CAAC,CAAC;AAAA,MAClD,CAAC;AAAA,IACH;AAKA,UAAM,QAAQ,MAAM,KAAK;AACzB,UAAM,kBAAkB,mBAAmB,UAAU,SAAS;AAC9D,UAAM,SAAS,MAAM,MAAM,SAAS,IAAI,eAAe;AACvD,QAAI,QAAQ;AACV,YAAM,MAAM,SAAS,IAAI,iBAAiB;AAAA,QACxC,GAAG;AAAA,QACH,GAAG;AAAA,MACL,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAgB,kBACd,UACA,MACA,MACA,YACA,QACA;AACA,UAAM,WAAW,MAAM;AAAA,MACrB,GAAG,QAAQ,IAAI,IAAI,GAAG,SAAS,WAAW,mBAAmB,MAAM,CAAC,KAAK,EAAE;AAAA,MAC3E;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,GAAI,aACA;AAAA,YACE,eAAe,UAAU,UAAU;AAAA,UACrC,IACA,CAAC;AAAA,QACP;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,UAAM,mBAAmB,SAAS,QAAQ,IAAI,aAAa;AAC3D,UAAM,aAAa,mBACf,SAAS,gBAAgB,IACzB;AAEJ,UAAM,UACJ,cAAc,OAAO,SAAS,UAAU,IACpC,KAAK,IAAI,IAAI,aAAa,MAC1B;AAEN,WAAO,EAAE,UAAU,QAAQ;AAAA,EAC7B;AAAA,EAEA,OAAiB,eACf,OACA,UACA,oBACA,kBACA,eAAuB,GACa;AAGpC,UAAM,aAAa,iBAAiB,WAAW,MAAM,YAAY;AAGjE,UAAM,WAAW,MAAM,QAAQ;AAAA,MAC7B,WAAW,IAAI,OAAO,OAAO;AAC3B,cAAM,UAAU,MAAM,MAAM,SAAS;AAAA,UACnC,mBAAmB,UAAU,EAAE;AAAA,QACjC;AACA,YAAI,CAAC,SAAS;AAGZ,cAAI;AACF,kBAAM,MAAM,WAAW,IAAI,kBAAkB;AAAA,UAC/C,QAAQ;AAAA,UAAC;AACT,gBAAM,IAAI,MAAM,mDAAmD;AAAA,QACrE;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,OAAiB,yBACZ,MACoB;AACvB,QAAI,OAAO,WAAW,eAAe,CAAE,MAAM,UAAU,GAAI;AAGzD,YAAM,WAAW,KAAK,gBAAwB,GAAG,IAAI;AACrD,aAAO,MAAM;AACX,cAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAI,KAAK,KAAM,QAAO,KAAK;AAC3B,cAAM,KAAK;AAAA,MACb;AAAA,IACF;AAGA,UAAM,qBAAqB,MAAM,KAAK,CAAC;AACvC,UAAM,UAAU,kBAAkB,kBAAkB;AACpD,QAAI,cAAc,MAAM;AAAA,IAAC;AACzB,QAAI,UAAmB;AACvB,UAAM,IAAI,QAAc,CAAC,mBAAmB;AAC1C,aAAO,UAAU,MAAM;AAAA,QACrB;AAAA,QACA;AAAA,UACE,MAAM;AAAA,UACN,aAAa;AAAA,QACf;AAAA,QACA,OAAO,SAAS;AAGd,oBAAU,CAAC,CAAC;AACZ,yBAAe;AAGf,gBAAM,IAAI,QAAc,CAAC,MAAO,cAAc,CAAE;AAAA,QAClD;AAAA,MACF;AAAA,IACF,CAAC;AACD,QAAI,SAAS;AAEX,UAAI;AACF,cAAM,WAAW,KAAK,gBAAwB,GAAG,IAAI;AACrD,eAAO,MAAM;AACX,gBAAM,OAAO,MAAM,SAAS,KAAK;AACjC,cAAI,KAAK,KAAM,QAAO,KAAK;AAC3B,gBAAM,KAAK;AAAA,QACb;AAAA,MACF,UAAE;AAEA,oBAAY;AAAA,MACd;AAAA,IACF;AAKA,gBAAY;AACZ,UAAM,OAAO,UAAU,MAAM,QAAQ,SAAS,MAAM;AAAA,IAAC,CAAC;AAItD,UAAM,WAAW,KAAK,CAAC;AACvB,UAAM,eAAe,KAAK,CAAC,KAAK,CAAC;AACjC,UAAM,eAAe,KAAK,CAAC;AAC3B,UAAM,eAAe,KAAK,CAAC;AAE3B,UAAM,QAAQ,MAAM,KAAK;AACzB,UAAM,mBAAmB,MAAM,MAAM,WAAW,IAAI,kBAAkB;AACtE,QAAI,CAAC,kBAAkB;AACrB,YAAM,IAAI,MAAM,iBAAiB;AAAA,IACnC;AACA,QACE,iBAAiB,UACjB,iBAAiB,iBAAiB,SAClC;AACA,YAAM,IAAI,2BAA2B,iBAAiB;AAAA,IACxD;AAEA,UAAM,WAAW,KAAK;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,qBAAiB,KAAK,SAAU,OAAM;AAEtC,UAAM,eAA4C;AAAA,MAChD,SAAS,iBAAiB,WAAW;AAAA,MACrC,SAAS,iBAAiB;AAAA,MAC1B;AAAA,MACA;AAAA,IACF;AAEA,WAAO,KAAK,UAAU,YAAY;AAAA,EACpC;AAAA,EAEA,OAAiB,gBACf,qBACA,UACA,MACA,MACA,YACA,eAAuB,CAAC,GACxB,cACA,eAAuB,GACA;AACvB,UAAM,YAAY,MAAM,4BAA4B,YAAY;AAChE,UAAM,qBAAqB,MAAM;AACjC,UAAM,QAAQ,MAAM,KAAK;AAEzB,QAAI,mBAAmB,MAAM,MAAM,WAAW,IAAI,kBAAkB;AACpE,QACE,iBAAiB,UACjB,iBAAiB,kBAAkB,SACnC;AACA,YAAM,IAAI,2BAA2B,iBAAiB;AAAA,IACxD;AAGA,QAAI,UAAU,kBAAkB;AAChC,UAAM,QAAQ,OAAO;AAIrB,UAAM,eAAe,kBAAkB;AACvC,QAAI,gBAAsC;AAC1C,QAAI;AACF,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,sBAAgB,IAAI;AACpB,gBAAU,IAAI;AAAA,IAChB,SAAS,GAAG;AACV,UAAI,EAAE,aAAa,8BAA8B,eAAe;AAC9D,gBAAQ;AAAA,UACN;AAAA,QACF;AACA,cAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,GAAI,CAAC;AACxD,cAAM;AAAA,MACR;AAGA,YAAM,MAAM,WAAW,IAAI,kBAAkB;AAC7C,UAAI,iBAAiB,QAAW;AAG9B,2BAAmB;AAAA,MACrB,OAAO;AAGL,cAAM;AAAA,MACR;AAAA,IACF;AAEA,QAAI,kBAAkB,UAAa,kBAAkB;AAEnD,YAAM,WAAW,KAAK;AAAA,QACpB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,uBAAiB,KAAK,SAAU,OAAM;AAAA,IACxC;AAEA,QAAI,kBAAkB,QAAW;AAE/B,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,sBAAgB,IAAI;AACpB,gBAAU,IAAI;AAAA,IAChB;AAGA,QAAI,WAAW;AACf,QAAI;AACJ,UAAM,UAAU,kBAAkB,WAAW,OAAO,WAAW;AAC/D,QAAI,aAAa,kBAAkB,cAAc,CAAC;AAClD,WAAO,MAAM;AACX,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,UAAU,cAAc,MAAM,KAAK,YAAY,CAAC;AACtD,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,MACV,IAAI,oBAAoB,MAAM,OAAO;AACrC,eAAS;AAET,YAAM,kBAA4C,QAAQ;AAAA,QACxD,CAAC,WAAW;AACV,gBAAM,SACJ,OAAO,2BAA2B,EAAE,kBAAkB;AACxD,cAAI,CAAC,UAAU,MAAM,GAAG;AACtB,kBAAM,IAAI,MAAM,iDAAiD;AAAA,UACnE;AACA,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,CAAC,2BAA2B,GAAG;AAAA,cAC7B,GAAG,OAAO,2BAA2B;AAAA,cACrC,CAAC,kBAAkB,GAAG;AAAA,YACxB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,YAAM,QAAQ;AAAA,QACZ,gBAAgB;AAAA,UAAI,CAAC,MACnB,MAAM,SAAS;AAAA,YACb,mBAAmB,UAAU,EAAE,sBAAsB,CAAC;AAAA,YACtD;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,mBAAa;AAAA,QACX,GAAG;AAAA,QACH,GAAG,gBAAgB;AAAA,UACjB,CAAC,MAA0B,EAAE,sBAAsB;AAAA,QACrD;AAAA,MACF;AACA,YAAM,MAAM,WAAW,IAAI,oBAAoB;AAAA,QAC7C;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAGD,sBAAgB,gBAAgB;AAGhC,iBAAW,KAAK,gBAAiB,OAAM;AAEvC,UAAI,CAAC,QAAS;AAGd,YAAM,QAAQ,OAAO;AACrB,YAAM,MAAM,MAAM,KAAK;AAAA,QACrB;AAAA,QACA;AAAA,QACA;AAAA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,iBAAW,IAAI;AACf,gBAAU,IAAI;AAAA,IAChB;AAEA,UAAM,eAA4C;AAAA,MAChD,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,WAAO,KAAK,UAAU,YAAY;AAAA,EACpC;AAAA,EAEA,MACE,UACA,MACA,cACA,YACuB;AACvB,wBAAoB,QAAQ;AAE5B,UAAM,OAAO,cAAc;AAAA,MACzB;AAAA,MACA,QAAQ;AAAA,IACV,CAAC;AAED,UAAM,qBAAqB,sBAAsB,UAAU,SAAS,IAAI;AACxE,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA,IAAI,WAAW,IAAI;AAAA,MACnB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,cACE,UACA,QACA,YACmB;AACnB,wBAAoB,QAAQ;AAE5B,UAAM,gBAAgB,KAAK,MAAM,MAAM;AACvC,UAAM,EAAE,oBAAoB,SAAS,cAAc,QAAQ,IACzD,aAAa,MAAM,aAAa;AAElC,WAAO,KAAK;AAAA,MACV,QAAQ,QAAQ,kBAAkB;AAAA,MAClC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,OAAO,UAAkB,YAAuC;AAC9D,wBAAoB,QAAQ;AAC5B,UAAM,qBAAqB,sBAAsB,UAAU,QAAQ;AACnE,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,MAAM,uBAAuB,aAAa;AAAA,EACxC,OAAO,YAAY,CAAC,CAAC;AAAA,EACrB,UAAU,MAAM,OAAO,CAAC;AAAA,EACxB,SAAS,SAAS,SAAS,MAAM,IAAI,CAAC,CAAC,CAAC;AAAA,EACxC,KAAK,IAAI;AAAA,EACT,OAAO,IAAI;AACb,CAAC;AACM,MAAM,mBAAmB;AAAA,EAC9B,CAAC,MAAuB,aAAa;AACvC;AACO,MAAM,aAAa,MAAM,gBAAgB;AAEzC,MAAM,mBAAmB;AACzB,MAAM,qBAAqB;AAC3B,MAAM,uBAAuB;AAC7B,MAAM,oBAAoB,aAAa;AAAA,EAC5C,CAAC,gBAAgB,GAAG;AAAA,EACpB,CAAC,kBAAkB,GAAG;AAAA,EACtB,CAAC,oBAAoB,GAAG;AAC1B,CAAC;AAGM,MAAM,yBAAyB;AAC/B,MAAM,8BAA8B;AACpC,MAAM,4BAA4B;AAClC,MAAM,2BAA2B,aAAa;AAAA,EACnD,CAAC,sBAAsB,GAAG,OAAO;AAAA,EACjC,CAAC,2BAA2B,GAAG;AAAA,EAC/B,CAAC,yBAAyB,GAAG,OAAO;AACtC,CAAC;AAYD,MAAM,qBAAqB,aAAa,EAAE,IAAI,OAAO,EAAE,CAAC;AAExD,MAAM,sBAAsB,aAAa;AAAA,EACvC,SAAS,MAAM,wBAAwB;AAAA,EACvC,SAAS,QAAQ;AAAA,EACjB,QAAQ,OAAO;AACjB,CAAC;AAED,MAAM,eAAe,aAAa;AAAA,EAChC,oBAAoB,OAAO;AAAA,EAC3B,SAAS,OAAO;AAAA,EAChB,SAAS,IAAI,EAAE,MAAM,YAAY,CAAC;AAAA,EAClC,cAAc,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;AAClD,CAAC;AAaD,eAAe,YAA8B;AAC3C,MAAI;AACF,QAAI,CAAC,WAAW,UAAW,QAAO;AAGlC,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,YAAM,MAAM,UAAU,KAAK,iBAAiB,CAAC;AAC7C,UAAI,kBAAkB,MAAM,IAAI,OAAO,kBAAkB,GAAG;AAC5D,UAAI,YAAY,MAAM;AACpB,YAAI,OAAO,MAAM;AACjB,gBAAQ;AAAA,MACV;AACA,UAAI,UAAU,MAAM,OAAO,IAAI,KAAK;AAAA,IACtC,CAAC;AAED,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAeA,SAAS,mBAAmB,UAAkB,WAAmB;AAC/D,SAAO,GAAG,mBAAmB,QAAQ,CAAC,IAAI,mBAAmB,SAAS,CAAC;AACzE;AACA,eAAe,sBACb,UACA,MACA,MACiB;AACjB,QAAM,cAAc,cAAc;AAAA,IAChC;AAAA,IACA;AAAA,IACA,MAAM,QAAQ;AAAA,EAChB,CAAC;AACD,SAAO,OAAO,OACX,OAAO,WAAW,IAAI,WAAW,WAAW,CAAC,EAC7C;AAAA,IAAK,CAAC,UACL,MAAM,KAAK,IAAI,WAAW,KAAK,CAAC,EAC7B,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAChD,KAAK,EAAE;AAAA,EACZ;AACJ;AAEA,eAAe,eAAe;AAC5B,QAAM,IAAI,QAAc,CAAC,YAAY;AACnC,UAAM,MAAM,UAAU,eAAe,sBAAsB;AAC3D,QAAI,YAAY,MAAM,QAAQ;AAC9B,QAAI,UAAU,MAAM,QAAQ;AAC5B,QAAI,YAAY,MAAM,QAAQ;AAAA,EAChC,CAAC;AACH;AAEA,eAAe,cAA8B;AAC3C,MAAI,MAAM,UAAU,GAAG;AACrB,UAAM,EAAE,OAAO,IAAI,MAAM,OAAO,KAAK;AACrC,UAAM,KAAK,MAAM,OAAO,wBAAwB,GAAG;AAAA,MACjD,QAAQC,KAAI;AACV,YAAI,CAACA,IAAG,iBAAiB,SAAS,GAAG,EAAG,CAAAA,IAAG,kBAAkB,GAAG;AAChE,YAAI,CAACA,IAAG,iBAAiB,SAAS,GAAG,EAAG,CAAAA,IAAG,kBAAkB,GAAG;AAAA,MAClE;AAAA,IACF,CAAC;AAED,WAAO;AAAA,MACL,UAAU;AAAA,QACR,KAAK,OAAO,MAAM;AAChB,cAAI;AACF,mBAAO,GAAG,IAAI,KAAK,CAAC;AAAA,UACtB,SAAS,OAAO;AACd,oBAAQ,MAAM,qCAAqC,KAAK;AACxD,oBAAQ,MAAM,oBAAoB;AAClC,kBAAM,aAAa;AACnB,mBAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,KAAK,OAAO,GAAG,MAAM;AACnB,gBAAM,GAAG,IAAI,KAAK,GAAG,CAAC;AAAA,QACxB;AAAA,QACA,KAAK,CAAC,MAAM,GAAG,OAAO,KAAK,CAAC;AAAA,MAC9B;AAAA,MACA,YAAY;AAAA,QACV,KAAK,OAAO,MAAM;AAChB,cAAI;AACF,mBAAO,MAAM,GAAG,IAAI,KAAK,CAAC;AAAA,UAC5B,SAAS,OAAO;AACd,oBAAQ,MAAM,yCAAyC,KAAK;AAC5D,oBAAQ,MAAM,oBAAoB;AAClC,kBAAM,aAAa;AACnB,mBAAO;AAAA,UACT;AAAA,QACF;AAAA,QACA,KAAK,OAAO,GAAG,MAAM;AACnB,gBAAM,GAAG,IAAI,KAAK,GAAG,CAAC;AAAA,QACxB;AAAA,QACA,KAAK,CAAC,MAAM,GAAG,OAAO,KAAK,CAAC;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI,oBAAI,IAAgC;AAC9C,QAAM,IAAI,oBAAI,IAA6B;AAE3C,SAAO;AAAA,IACL,UAAU;AAAA,MACR,KAAK,OAAO,MAAM,EAAE,IAAI,CAAC;AAAA,MACzB,KAAK,OAAO,GAAG,MAAM,KAAK,EAAE,IAAI,GAAG,CAAC;AAAA,MACpC,KAAK,OAAO,MAAM,KAAK,EAAE,OAAO,CAAC;AAAA,IACnC;AAAA,IACA,YAAY;AAAA,MACV,KAAK,OAAO,MAAM,EAAE,IAAI,CAAC;AAAA,MACzB,KAAK,OAAO,GAAG,MAAM,KAAK,EAAE,IAAI,GAAG,CAAC;AAAA,MACpC,KAAK,OAAO,MAAM,KAAK,EAAE,OAAO,CAAC;AAAA,IACnC;AAAA,EACF;AACF;AAEA,eAAe,QAAQ,SAAkB;AACvC,MAAI,YAAY,QAAW;AACzB,UAAMC,WAAU,UAAU,KAAK,IAAI;AACnC,QAAIA,WAAU,GAAG;AACf,YAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAASA,QAAO,CAAC;AAAA,IAC7D;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import type { JSONSchema, GraffitiObject } from \"@graffiti-garden/api\";\nimport {\n getAuthorizationEndpoint,\n fetchWithErrorHandling,\n verifyHTTPSEndpoint,\n} from \"./utilities\";\nimport {\n compileGraffitiObjectSchema,\n GraffitiErrorCursorExpired,\n GraffitiErrorNotFound,\n} from \"@graffiti-garden/api\";\nimport {\n encode as dagCborEncode,\n decode as dagCborDecode,\n} from \"@ipld/dag-cbor\";\nimport {\n type infer as infer_,\n string,\n url,\n array,\n optional,\n nullable,\n strictObject,\n looseObject,\n nonnegative,\n int,\n boolean,\n custom,\n number,\n union,\n} from \"zod/mini\";\n\nexport class Inboxes {\n getAuthorizationEndpoint = getAuthorizationEndpoint;\n protected cache_: Promise<Cache> | null = null;\n protected get cache() {\n if (!this.cache_) {\n this.cache_ = createCache();\n }\n return this.cache_;\n }\n\n async send(inboxUrl: string, message: Message<{}>): Promise<string> {\n verifyHTTPSEndpoint(inboxUrl);\n const url = `${inboxUrl}/send`;\n\n const response = await fetchWithErrorHandling(url, {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/cbor\",\n },\n body: new Uint8Array(dagCborEncode({ m: message })),\n });\n\n const blob = await response.blob();\n const cbor = dagCborDecode(await blob.arrayBuffer());\n const parsed = SendResponseSchema.parse(cbor);\n return parsed.id;\n }\n\n async get(\n inboxUrl: string,\n messageId: string,\n inboxToken?: string | null,\n ): Promise<LabeledMessageBase | null> {\n const messageCacheKey = getMessageCacheKey(inboxUrl, messageId);\n const cache = await this.cache;\n const cached = await cache.messages.get(messageCacheKey);\n if (cached !== undefined) return cached;\n\n const url = `${inboxUrl}/message/${messageId}`;\n let response: Response | null = null;\n try {\n response = await fetchWithErrorHandling(url, {\n method: \"GET\",\n headers: {\n ...(inboxToken\n ? {\n Authorization: `Bearer ${inboxToken}`,\n }\n : {}),\n },\n });\n } catch (e) {\n if (e instanceof GraffitiErrorNotFound) {\n await cache.messages.set(messageCacheKey, null);\n return null;\n }\n throw e;\n }\n\n const blob = await response.blob();\n const cbor = dagCborDecode(await blob.arrayBuffer());\n const parsed = LabeledMessageBaseSchema.parse(cbor);\n\n await cache.messages.set(messageCacheKey, parsed);\n return parsed;\n }\n\n async label(\n inboxUrl: string,\n messageId: string,\n label: number,\n inboxToken?: string | null,\n ): Promise<void> {\n verifyHTTPSEndpoint(inboxUrl);\n\n if (inboxToken) {\n const url = `${inboxUrl}/label/${messageId}`;\n\n await fetchWithErrorHandling(url, {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/cbor\",\n Authorization: `Bearer ${inboxToken}`,\n },\n body: new Uint8Array(dagCborEncode({ l: label })),\n });\n }\n\n // Update the cache, even if no token.\n // Therefore people not logged in do not need to\n // repeatedly re-validate objects.\n const cache = await this.cache;\n const messageCacheKey = getMessageCacheKey(inboxUrl, messageId);\n const result = await cache.messages.get(messageCacheKey);\n if (result) {\n await cache.messages.set(messageCacheKey, {\n ...result,\n l: label,\n });\n }\n }\n\n protected async fetchMessageBatch(\n inboxUrl: string,\n type: \"query\" | \"export\",\n body: Uint8Array<ArrayBuffer> | undefined,\n inboxToken?: string | null,\n cursor?: string,\n ) {\n const response = await fetchWithErrorHandling(\n `${inboxUrl}/${type}${cursor ? `?cursor=${encodeURIComponent(cursor)}` : \"\"}`,\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/cbor\",\n ...(inboxToken\n ? {\n Authorization: `Bearer ${inboxToken}`,\n }\n : {}),\n },\n body,\n },\n );\n const retryAfterHeader = response.headers.get(\"Retry-After\");\n const retryAfter = retryAfterHeader\n ? parseInt(retryAfterHeader)\n : undefined;\n\n const waitTil =\n retryAfter && Number.isFinite(retryAfter)\n ? Date.now() + retryAfter * 1000\n : undefined;\n\n return { response, waitTil };\n }\n\n protected async *yieldFromCache(\n cache: Cache,\n inboxUrl: string,\n messageIdsCacheKey: string,\n cachedMessageIds: CacheQueryValue,\n cacheNumSeen: number = 0,\n ): AsyncGenerator<LabeledMessageBase> {\n // Filter out all messageIds before\n // the number already seen\n const messageIds = cachedMessageIds.messageIds.slice(cacheNumSeen);\n\n // Get all the messages pointed to in the cache\n const messages = await Promise.all(\n messageIds.map(async (id) => {\n const message = await cache.messages.get(\n getMessageCacheKey(inboxUrl, id),\n );\n if (!message) {\n // Something is very wrong with the cache,\n // it refers to message IDs that are not cached\n try {\n await cache.messageIds.del(messageIdsCacheKey);\n } catch {}\n throw new Error(\"Cache out of sync - perhaps clear browser storage\");\n }\n return message;\n }),\n );\n\n yield* messages;\n }\n\n protected async *lockedMessageStreamer<Schema extends JSONSchema>(\n ...args: Parameters<typeof this.messageStreamer<Schema>>\n ): MessageStream<Schema> {\n if (typeof window === \"undefined\" || !(await canUseIDB())) {\n // TODO: implement locking in node as well, but not\n // high priority since most use will be in browser\n const streamer = this.messageStreamer<Schema>(...args);\n while (true) {\n const next = await streamer.next();\n if (next.done) return next.value;\n yield next.value;\n }\n }\n\n // Request the lock\n const messageIdsCacheKey = await args[0];\n const lockKey = `graffiti:inbox:${messageIdsCacheKey}`;\n let releaseLock = () => {};\n let hasLock: boolean = false;\n await new Promise<void>((resolvehasLock) => {\n window.navigator.locks.request(\n lockKey,\n {\n mode: \"exclusive\",\n ifAvailable: true,\n },\n async (lock) => {\n // Immediately return whether we\n // acquired the lock or not\n hasLock = !!lock;\n resolvehasLock();\n\n // Then wait for the release to be called\n await new Promise<void>((r) => (releaseLock = r));\n },\n );\n });\n if (hasLock) {\n // If we have the lock, simply proceed with the regular streamer\n try {\n const streamer = this.messageStreamer<Schema>(...args);\n while (true) {\n const next = await streamer.next();\n if (next.done) return next.value;\n yield next.value;\n }\n } finally {\n // Release the lock when all done\n releaseLock();\n }\n }\n\n // Someone else has the lock,\n // so wait until the lock is released,\n // then just return from the cache\n releaseLock();\n await window.navigator.locks.request(lockKey, () => {});\n\n // TODO: the arguments here are brittle\n // at some point, refactor things\n const inboxUrl = args[1];\n const objectSchema = args[5] ?? {};\n const cacheVersion = args[6];\n const cacheNumSeen = args[7];\n\n const cache = await this.cache;\n const cachedMessageIds = await cache.messageIds.get(messageIdsCacheKey);\n if (!cachedMessageIds) {\n throw new Error(\"Cache not found\");\n }\n if (\n cacheVersion !== undefined &&\n cacheVersion !== cachedMessageIds.version\n ) {\n throw new GraffitiErrorCursorExpired(\"Cursor is stale\");\n }\n\n const iterator = this.yieldFromCache(\n cache,\n inboxUrl,\n messageIdsCacheKey,\n cachedMessageIds,\n cacheNumSeen,\n );\n for await (const m of iterator) yield m as LabeledMessage<Schema>;\n\n const outputCursor: infer_<typeof CursorSchema> = {\n numSeen: cachedMessageIds.messageIds.length,\n version: cachedMessageIds.version,\n messageIdsCacheKey,\n objectSchema,\n };\n\n return JSON.stringify(outputCursor);\n }\n\n protected async *messageStreamer<Schema extends JSONSchema>(\n messageIdsCacheKey_: Promise<string>,\n inboxUrl: string,\n type: \"export\" | \"query\",\n body: Uint8Array<ArrayBuffer> | undefined,\n inboxToken?: string | null,\n objectSchema: Schema = {} as Schema,\n cacheVersion?: string,\n cacheNumSeen: number = 0,\n ): MessageStream<Schema> {\n const validator = await compileGraffitiObjectSchema(objectSchema);\n const messageIdsCacheKey = await messageIdsCacheKey_;\n const cache = await this.cache;\n\n let cachedMessageIds = await cache.messageIds.get(messageIdsCacheKey);\n if (\n cacheVersion !== undefined &&\n cacheVersion !== cachedMessageIds?.version\n ) {\n throw new GraffitiErrorCursorExpired(\"Cursor is stale\");\n }\n\n // If we are rate-limited, wait\n let waitTil = cachedMessageIds?.waitTil;\n await waitFor(waitTil);\n\n // See if the cursor is still active by\n // requesting an initial batch of messages\n const cachedCursor = cachedMessageIds?.cursor;\n let firstResponse: Response | undefined = undefined;\n try {\n const out = await this.fetchMessageBatch(\n inboxUrl,\n type,\n body,\n inboxToken,\n cachedCursor,\n );\n firstResponse = out.response;\n waitTil = out.waitTil;\n } catch (e) {\n if (!(e instanceof GraffitiErrorCursorExpired && cachedCursor)) {\n console.error(\n \"Unexpected error in stream, waiting 5 seconds before continuing...\",\n );\n await new Promise((resolve) => setTimeout(resolve, 5000));\n throw e;\n }\n\n // The cursor is stale\n await cache.messageIds.del(messageIdsCacheKey);\n if (cacheVersion === undefined) {\n // The query is not a continuation\n // so we can effectively ignore the error\n cachedMessageIds = undefined;\n } else {\n // Otherwise propogate it up so the\n // consumer can clear their message history\n throw e;\n }\n }\n\n if (firstResponse !== undefined && cachedMessageIds) {\n // Cursor is valid! Yield from the cache\n const iterator = this.yieldFromCache(\n cache,\n inboxUrl,\n messageIdsCacheKey,\n cachedMessageIds,\n cacheNumSeen,\n );\n for await (const m of iterator) yield m as LabeledMessage<Schema>;\n }\n\n if (firstResponse === undefined) {\n // The cursor was stale: try again\n const out = await this.fetchMessageBatch(\n inboxUrl,\n type,\n body,\n inboxToken,\n );\n firstResponse = out.response;\n waitTil = out.waitTil;\n }\n\n // Continue streaming results\n let response = firstResponse;\n let cursor: string;\n const version = cachedMessageIds?.version ?? crypto.randomUUID();\n let messageIds = cachedMessageIds?.messageIds ?? [];\n while (true) {\n const blob = await response.blob();\n const decoded = dagCborDecode(await blob.arrayBuffer());\n const {\n results,\n hasMore,\n cursor: nextCursor,\n } = MessageResultSchema.parse(decoded);\n cursor = nextCursor;\n\n const labeledMessages: LabeledMessage<Schema>[] = results.map(\n (result) => {\n const object =\n result[LABELED_MESSAGE_MESSAGE_KEY][MESSAGE_OBJECT_KEY];\n if (!validator(object)) {\n throw new Error(\"Server returned data that does not match schema\");\n }\n return {\n ...result,\n [LABELED_MESSAGE_MESSAGE_KEY]: {\n ...result[LABELED_MESSAGE_MESSAGE_KEY],\n [MESSAGE_OBJECT_KEY]: object,\n },\n };\n },\n );\n\n // First cache the messages with their labels\n await Promise.all(\n labeledMessages.map((m: LabeledMessageBase) =>\n cache.messages.set(\n getMessageCacheKey(inboxUrl, m[LABELED_MESSAGE_ID_KEY]),\n m,\n ),\n ),\n );\n // Then store all the messageids\n messageIds = [\n ...messageIds,\n ...labeledMessages.map(\n (m: LabeledMessageBase) => m[LABELED_MESSAGE_ID_KEY],\n ),\n ];\n await cache.messageIds.set(messageIdsCacheKey, {\n cursor,\n version,\n messageIds,\n waitTil,\n });\n\n // Update how many we've seen\n cacheNumSeen += labeledMessages.length;\n\n // Return the values\n for (const m of labeledMessages) yield m;\n\n if (!hasMore) break;\n\n // Otherwise get another response (after waiting for rate-limit)\n await waitFor(waitTil);\n const out = await this.fetchMessageBatch(\n inboxUrl,\n type,\n undefined, // Body is never past the first time\n inboxToken,\n cursor,\n );\n response = out.response;\n waitTil = out.waitTil;\n }\n\n const outputCursor: infer_<typeof CursorSchema> = {\n numSeen: cacheNumSeen,\n version,\n messageIdsCacheKey,\n objectSchema,\n };\n\n return JSON.stringify(outputCursor);\n }\n\n query<Schema extends JSONSchema>(\n inboxUrl: string,\n tags: Uint8Array[],\n objectSchema: Schema,\n inboxToken?: string | null,\n ): MessageStream<Schema> {\n verifyHTTPSEndpoint(inboxUrl);\n\n const body = dagCborEncode({\n tags,\n schema: objectSchema,\n });\n\n const messageIdsCacheKey = getMessageIdsCacheKey(inboxUrl, \"query\", body);\n return this.lockedMessageStreamer<Schema>(\n messageIdsCacheKey,\n inboxUrl,\n \"query\",\n new Uint8Array(body),\n inboxToken,\n objectSchema,\n );\n }\n\n continueQuery(\n inboxUrl: string,\n cursor: string,\n inboxToken?: string | null,\n ): MessageStream<{}> {\n verifyHTTPSEndpoint(inboxUrl);\n\n const decodedCursor = JSON.parse(cursor);\n const { messageIdsCacheKey, numSeen, objectSchema, version } =\n CursorSchema.parse(decodedCursor);\n\n return this.lockedMessageStreamer<{}>(\n Promise.resolve(messageIdsCacheKey),\n inboxUrl,\n \"query\",\n undefined,\n inboxToken,\n objectSchema,\n version,\n numSeen,\n );\n }\n\n export(inboxUrl: string, inboxToken: string): MessageStream<{}> {\n verifyHTTPSEndpoint(inboxUrl);\n const messageIdsCacheKey = getMessageIdsCacheKey(inboxUrl, \"export\");\n return this.lockedMessageStreamer<{}>(\n messageIdsCacheKey,\n inboxUrl,\n \"export\",\n undefined,\n inboxToken,\n );\n }\n}\n\nconst GraffitiObjectSchema = strictObject({\n value: looseObject({}),\n channels: array(string()),\n allowed: optional(nullable(array(url()))),\n url: url(),\n actor: url(),\n});\nexport const Uint8ArraySchema = custom<Uint8Array>(\n (v): v is Uint8Array => v instanceof Uint8Array,\n);\nexport const TagsSchema = array(Uint8ArraySchema);\n\nexport const MESSAGE_TAGS_KEY = \"t\";\nexport const MESSAGE_OBJECT_KEY = \"o\";\nexport const MESSAGE_METADATA_KEY = \"m\";\nexport const MessageBaseSchema = strictObject({\n [MESSAGE_TAGS_KEY]: TagsSchema,\n [MESSAGE_OBJECT_KEY]: GraffitiObjectSchema,\n [MESSAGE_METADATA_KEY]: Uint8ArraySchema,\n});\ntype MessageBase = infer_<typeof MessageBaseSchema>;\n\nexport const LABELED_MESSAGE_ID_KEY = \"id\";\nexport const LABELED_MESSAGE_MESSAGE_KEY = \"m\";\nexport const LABELED_MESSAGE_LABEL_KEY = \"l\";\nexport const LabeledMessageBaseSchema = strictObject({\n [LABELED_MESSAGE_ID_KEY]: string(),\n [LABELED_MESSAGE_MESSAGE_KEY]: MessageBaseSchema,\n [LABELED_MESSAGE_LABEL_KEY]: number(),\n});\ntype LabeledMessageBase = infer_<typeof LabeledMessageBaseSchema>;\n\nexport type Message<Schema extends JSONSchema> = MessageBase & {\n [MESSAGE_OBJECT_KEY]: GraffitiObject<Schema>;\n};\nexport type LabeledMessage<Schema extends JSONSchema> = LabeledMessageBase & {\n [LABELED_MESSAGE_MESSAGE_KEY]: {\n [MESSAGE_OBJECT_KEY]: GraffitiObject<Schema>;\n };\n};\n\nconst SendResponseSchema = strictObject({ id: string() });\n\nconst MessageResultSchema = strictObject({\n results: array(LabeledMessageBaseSchema),\n hasMore: boolean(),\n cursor: string(),\n});\n\nconst CursorSchema = strictObject({\n messageIdsCacheKey: string(),\n version: string(),\n numSeen: int().check(nonnegative()),\n objectSchema: union([looseObject({}), boolean()]),\n});\n\nexport interface MessageStream<\n Schema extends JSONSchema,\n> extends AsyncGenerator<LabeledMessage<Schema>, string> {}\n\ntype CacheQueryValue = {\n cursor: string;\n version: string;\n messageIds: string[];\n waitTil?: number;\n};\n\nasync function canUseIDB(): Promise<boolean> {\n try {\n if (!globalThis.indexedDB) return false;\n\n // Small probe database\n await new Promise<void>((resolve, reject) => {\n const req = indexedDB.open(\"__idb_probe__\", 1);\n req.onupgradeneeded = () => req.result.createObjectStore(\"k\");\n req.onsuccess = () => {\n req.result.close();\n resolve();\n };\n req.onerror = () => reject(req.error);\n });\n\n return true;\n } catch {\n return false;\n }\n}\n\ntype Cache = {\n messages: {\n get(k: string): Promise<LabeledMessageBase | null | undefined>;\n set(k: string, value: LabeledMessageBase | null): Promise<void>;\n del(k: string): Promise<void>;\n };\n messageIds: {\n get(k: string): Promise<CacheQueryValue | undefined>;\n set(k: string, value: CacheQueryValue): Promise<void>;\n del(k: string): Promise<void>;\n };\n};\n\nfunction getMessageCacheKey(inboxUrl: string, messageId: string) {\n return `${encodeURIComponent(inboxUrl)}:${encodeURIComponent(messageId)}`;\n}\nasync function getMessageIdsCacheKey(\n inboxUrl: string,\n type: \"query\" | \"export\",\n body?: Uint8Array,\n): Promise<string> {\n const cacheIdData = dagCborEncode({\n inboxUrl,\n type,\n body: body ?? null,\n });\n return crypto.subtle\n .digest(\"SHA-256\", new Uint8Array(cacheIdData))\n .then((bytes) =>\n Array.from(new Uint8Array(bytes))\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\"),\n );\n}\n\nasync function resetCacheDB() {\n await new Promise<void>((resolve) => {\n const req = indexedDB.deleteDatabase(\"graffiti-inbox-cache\");\n req.onsuccess = () => resolve();\n req.onerror = () => resolve(); // best effort\n req.onblocked = () => resolve(); // best effort\n });\n}\n\nasync function createCache(): Promise<Cache> {\n if (await canUseIDB()) {\n const { openDB } = await import(\"idb\");\n const db = await openDB(\"graffiti-inbox-cache\", 1, {\n upgrade(db) {\n if (!db.objectStoreNames.contains(\"m\")) db.createObjectStore(\"m\");\n if (!db.objectStoreNames.contains(\"q\")) db.createObjectStore(\"q\");\n },\n });\n\n return {\n messages: {\n get: async (k) => {\n try {\n return db.get(\"m\", k);\n } catch (error) {\n console.error(\"Error getting message from cache:\", error);\n console.error(\"resetting cache...\");\n await resetCacheDB();\n return undefined;\n }\n },\n set: async (k, v) => {\n await db.put(\"m\", v, k);\n },\n del: (k) => db.delete(\"m\", k),\n },\n messageIds: {\n get: async (k) => {\n try {\n return await db.get(\"q\", k);\n } catch (error) {\n console.error(\"Error getting message IDs from cache:\", error);\n console.error(\"resetting cache...\");\n await resetCacheDB();\n return undefined;\n }\n },\n set: async (k, v) => {\n await db.put(\"q\", v, k);\n },\n del: (k) => db.delete(\"q\", k),\n },\n };\n }\n\n const m = new Map<string, LabeledMessageBase | null>();\n const q = new Map<string, CacheQueryValue>();\n\n return {\n messages: {\n get: async (k) => m.get(k),\n set: async (k, v) => void m.set(k, v),\n del: async (k) => void m.delete(k),\n },\n messageIds: {\n get: async (k) => q.get(k),\n set: async (k, v) => void q.set(k, v),\n del: async (k) => void q.delete(k),\n },\n };\n}\n\nasync function waitFor(waitTil?: number) {\n if (waitTil !== undefined) {\n const waitFor = waitTil - Date.now();\n if (waitFor > 0) {\n await new Promise((resolve) => setTimeout(resolve, waitFor));\n }\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["url", "db", "waitFor"]
7
7
  }
@@ -30,7 +30,7 @@ class Sessions {
30
30
  const parsed = InitializedEventDetailSchema.safeParse(e.detail);
31
31
  if (!parsed.success) return;
32
32
  const error = parsed.data?.error;
33
- if (error) console.log(error);
33
+ if (error) console.error(error);
34
34
  resolve();
35
35
  }
36
36
  );
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/3-protocol/1-sessions.ts"],
4
- "sourcesContent": ["import type {\n Graffiti,\n GraffitiLoginEvent,\n GraffitiLogoutEvent,\n GraffitiSession,\n GraffitiSessionInitializedEvent,\n} from \"@graffiti-garden/api\";\nimport { DecentralizedIdentifiers } from \"../1-services/2-dids\";\nimport {\n InitializedEventDetailSchema,\n LoginEventDetailSchema,\n LogoutEventDetailSchema,\n type Authorization,\n} from \"../1-services/1-authorization\";\nimport { StorageBuckets } from \"../1-services/3-storage-buckets\";\nimport type { Inboxes } from \"../1-services/4-inboxes\";\nimport type { Service } from \"did-resolver\";\nimport {\n type infer as infer_,\n extend,\n array,\n string,\n object,\n url,\n tuple,\n enum as enum_,\n} from \"zod/mini\";\n\nexport const DID_SERVICE_TYPE_GRAFFITI_INBOX = \"GraffitiInbox\";\nexport const DID_SERVICE_TYPE_GRAFFITI_STORAGE_BUCKET = \"GraffitiStorageBucket\";\nexport const DID_SERVICE_ID_GRAFFITI_PERSONAL_INBOX = \"#graffitiPersonalInbox\";\nexport const DID_SERVICE_ID_GRAFFITI_STORAGE_BUCKET = \"#graffitiStorageBucket\";\nexport const DID_SERVICE_ID_GRAFFITI_SHARED_INBOX_PREFIX =\n \"#graffitiSharedInbox_\";\n\nexport class Sessions {\n sessionEvents: Graffiti[\"sessionEvents\"] = new EventTarget();\n\n constructor(\n protected readonly services: {\n readonly dids: DecentralizedIdentifiers;\n readonly authorization: Authorization;\n readonly storageBuckets: StorageBuckets;\n readonly inboxes: Inboxes;\n },\n ) {\n const initializedPromise = new Promise<void>((resolve) => {\n this.services.authorization.eventTarget.addEventListener(\n \"initialized\",\n (e) => {\n if (!(e instanceof CustomEvent)) return;\n const parsed = InitializedEventDetailSchema.safeParse(e.detail);\n if (!parsed.success) return;\n const error = parsed.data?.error;\n if (error) console.log(error);\n resolve();\n },\n );\n });\n this.services.authorization.eventTarget.addEventListener(\n \"login\",\n this.onLogin.bind(this),\n );\n this.services.authorization.eventTarget.addEventListener(\n \"logout\",\n this.onLogout.bind(this),\n );\n\n // Handle account registration redirect immediately,\n // to prevent SPA routers from hijacking the URL too soon\n let loginPromise: Promise<void> | undefined;\n if (typeof window !== \"undefined\") {\n const actorEncoded = new URLSearchParams(window.location.search).get(\n \"actor\",\n );\n if (actorEncoded) {\n try {\n // Get the actor\n const actor = decodeURIComponent(actorEncoded);\n // Strip it from the URL\n const url = new URL(window.location.toString());\n url.searchParams.delete(\"actor\");\n window.history.replaceState({}, \"\", url.toString());\n // Complete the login\n loginPromise = this.login(actor);\n } catch (error) {\n console.error(\"Error decoding actor:\", error);\n }\n }\n }\n\n (async () => {\n // Allow listeners to be added before dispatching events\n await new Promise((resolve) => setTimeout(resolve, 0));\n\n // Wait for login to complete, if there\n await loginPromise;\n\n for (const session of this.loggedInSessions) {\n const loginEvent: GraffitiLoginEvent = new CustomEvent(\"login\", {\n detail: { session: { actor: session.actor } },\n });\n this.sessionEvents.dispatchEvent(loginEvent);\n }\n\n await initializedPromise;\n\n // Send own initialized event\n const initializedEvent: GraffitiSessionInitializedEvent = new CustomEvent(\n \"initialized\",\n );\n this.sessionEvents.dispatchEvent(initializedEvent);\n })();\n }\n\n protected inProgressLogin: infer_<typeof InProgressSchema> | undefined =\n undefined;\n protected inProgressLogout: infer_<typeof InProgressSchema> | undefined =\n undefined;\n\n async login(actor: string) {\n try {\n await this.login_(actor);\n } catch (e) {\n const loginEvent: GraffitiLoginEvent = new CustomEvent(\"login\", {\n detail: {\n error: e instanceof Error ? e : new Error(String(e)),\n session: { actor },\n },\n });\n this.sessionEvents.dispatchEvent(loginEvent);\n }\n }\n protected async login_(actor: string) {\n // First look to see if we're already logged in\n const existingSession = this.loggedInSessions.find(\n (session) => session.actor === actor,\n );\n if (existingSession) {\n this.sessionEvents.dispatchEvent(\n new CustomEvent(\"login\", { detail: { session: { actor } } }),\n );\n return;\n }\n\n const actorDocument = await this.services.dids.resolve(actor);\n\n const services = actorDocument.service;\n if (!services) {\n throw new Error(`No services found in actor document for ${actor}`);\n }\n\n const storageBucketService = services.find(\n (service) =>\n service.id === DID_SERVICE_ID_GRAFFITI_STORAGE_BUCKET &&\n service.type === DID_SERVICE_TYPE_GRAFFITI_STORAGE_BUCKET,\n );\n const personalInboxService = services.find(\n (service) =>\n service.id === DID_SERVICE_ID_GRAFFITI_PERSONAL_INBOX &&\n service.type === DID_SERVICE_TYPE_GRAFFITI_INBOX,\n );\n const sharedInboxServices = services.filter(\n (service) =>\n service.id.match(\n new RegExp(`^${DID_SERVICE_ID_GRAFFITI_SHARED_INBOX_PREFIX}\\\\d+$`),\n ) && service.type === DID_SERVICE_TYPE_GRAFFITI_INBOX,\n );\n\n if (\n !personalInboxService ||\n !storageBucketService ||\n sharedInboxServices.length === 0\n ) {\n throw new Error(\n `Required services not found in actor document for ${actor}`,\n );\n }\n\n // Massage the services into a list of endpoints with types\n const storageBucketEndpoint: string =\n serviceToEndpoint(storageBucketService);\n const personalInboxEndpoint: string =\n serviceToEndpoint(personalInboxService);\n const sharedInboxEndpoints: string[] =\n sharedInboxServices.map(serviceToEndpoint);\n const servicesWithTypes = [\n { endpoint: storageBucketEndpoint, type: \"bucket\" } as const,\n { endpoint: personalInboxEndpoint, type: \"personal-inbox\" } as const,\n ...sharedInboxEndpoints.map(\n (endpoint) =>\n ({\n endpoint,\n type: \"shared-inbox\",\n }) as const,\n ),\n ];\n\n // Fetch the authorization endpoints for each service\n const servicesWithAuthorizationEndpoints = await Promise.all(\n servicesWithTypes.map(async ({ endpoint, type }) => {\n const authorizationEndpoint = await (type === \"bucket\"\n ? this.services.storageBuckets.getAuthorizationEndpoint(endpoint)\n : this.services.inboxes.getAuthorizationEndpoint(endpoint));\n return { endpoint, authorizationEndpoint, type };\n }),\n );\n\n // Group the endpoints according to their authorization endpoints\n const servicesByAuthorizationMap: Map<\n string,\n {\n endpoint: string;\n type: \"bucket\" | \"personal-inbox\" | \"shared-inbox\";\n }[]\n > = new Map();\n servicesWithAuthorizationEndpoints.forEach(\n ({ authorizationEndpoint, endpoint, type }) => {\n if (!servicesByAuthorizationMap.has(authorizationEndpoint)) {\n servicesByAuthorizationMap.set(authorizationEndpoint, []);\n }\n servicesByAuthorizationMap\n .get(authorizationEndpoint)!\n .push({ endpoint, type });\n },\n );\n const servicesByAuthorization = [...servicesByAuthorizationMap.entries()];\n\n const session: GraffitiSession = { actor };\n\n const inProgressLogin: infer_<typeof InProgressSchema> = {\n ...session,\n tokens: [],\n servicesByAuthorization,\n };\n\n if (typeof window !== \"undefined\") {\n // Store the in-progress session in localStorage\n window.localStorage.setItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY,\n JSON.stringify(inProgressLogin),\n );\n } else {\n this.inProgressLogin = inProgressLogin;\n }\n\n // Start the login process with the first endpoint\n const [firstAuthorizationEndpoint, firstServices] =\n servicesByAuthorization[0];\n await this.services.authorization.login(\n firstAuthorizationEndpoint,\n actor,\n firstServices.map((s) => s.endpoint),\n );\n }\n\n protected async onLogin(event: unknown) {\n if (!(event instanceof CustomEvent)) return;\n const parsed = LoginEventDetailSchema.safeParse(event.detail);\n if (!parsed.success) return;\n\n const actor = parsed.data.loginId;\n\n try {\n await this.onLogin_(parsed.data);\n } catch (e) {\n const LoginEvent: GraffitiLoginEvent = new CustomEvent(\"login\", {\n detail: {\n error: e instanceof Error ? e : new Error(String(e)),\n session: { actor },\n },\n });\n this.sessionEvents.dispatchEvent(LoginEvent);\n }\n }\n protected async onLogin_(loginDetail: infer_<typeof LoginEventDetailSchema>) {\n if (loginDetail.error) throw loginDetail.error;\n\n const token = loginDetail.token;\n const actor = loginDetail.loginId;\n\n // Lookup the in-progress session\n let inProgressLogin: infer_<typeof InProgressSchema>;\n if (typeof window !== \"undefined\") {\n const inProgressLoginString = window.localStorage.getItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY,\n );\n if (!inProgressLoginString) {\n throw new Error(\"No in-progress login found\");\n }\n\n const json = JSON.parse(inProgressLoginString);\n inProgressLogin = InProgressSchema.parse(json);\n } else {\n if (!this.inProgressLogin) {\n throw new Error(\"No in-progress login found\");\n }\n inProgressLogin = this.inProgressLogin;\n }\n\n if (inProgressLogin.actor !== actor) {\n throw new Error(\"Actor mismatch in login response - concurrent logins?\");\n }\n\n inProgressLogin.tokens.push(token);\n\n if (\n inProgressLogin.tokens.length ===\n inProgressLogin.servicesByAuthorization.length\n ) {\n // Login complete!\n if (typeof window === \"undefined\") {\n this.inProgressLogin = undefined;\n } else {\n window.localStorage.removeItem(LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY);\n }\n\n // Build the completed session\n const services = inProgressLogin.servicesByAuthorization.flatMap(\n ([authorizationEndpoint, services], index) =>\n services.map((service) => ({\n token: inProgressLogin.tokens[index],\n serviceEndpoint: service.endpoint,\n authorizationEndpoint,\n type: service.type,\n })),\n );\n\n const session: StoredSession = {\n ...inProgressLogin,\n storageBucket: services.find((s) => s.type === \"bucket\")!,\n personalInbox: services.find((s) => s.type === \"personal-inbox\")!,\n sharedInboxes: services.filter((s) => s.type === \"shared-inbox\")!,\n };\n\n // Store the completed session\n const sessions = this.loggedInSessions;\n sessions.push(session);\n this.loggedInSessions = sessions;\n\n // Return the completed session\n const loginEvent: GraffitiLoginEvent = new CustomEvent(\"login\", {\n detail: { session: { actor } },\n });\n this.sessionEvents.dispatchEvent(loginEvent);\n } else {\n // Store the in progress and continue\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY,\n JSON.stringify(inProgressLogin),\n );\n } else {\n this.inProgressLogin = inProgressLogin;\n }\n\n // Continue to the next authorization endpoint\n const [authorizationEndpoint, services] =\n inProgressLogin.servicesByAuthorization[inProgressLogin.tokens.length];\n await this.services.authorization.login(\n authorizationEndpoint,\n actor,\n services.map((s) => s.endpoint),\n );\n }\n }\n\n async logout(actor: string) {\n try {\n await this.logout_(actor);\n } catch (e) {\n const logoutEvent: GraffitiLogoutEvent = new CustomEvent(\"logout\", {\n detail: {\n error: e instanceof Error ? e : new Error(String(e)),\n actor,\n },\n });\n this.sessionEvents.dispatchEvent(logoutEvent);\n }\n }\n protected async logout_(actor: string) {\n const session = this.loggedInSessions.find(\n (session) => session.actor === actor,\n );\n if (!session) {\n throw new Error(`No session found for actor ${actor}`);\n }\n\n // Remove the session(s)\n this.loggedInSessions = this.loggedInSessions.filter(\n (session) => session.actor !== actor,\n );\n\n // Begin the logout\n const token = session.tokens.pop();\n if (!token) {\n throw new Error(\"No tokens found in session\");\n }\n // Store the in progress logout\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY,\n JSON.stringify(session),\n );\n } else {\n this.inProgressLogout = session;\n }\n const [authorizationEndpoint, _] =\n session.servicesByAuthorization[session.tokens.length];\n await this.services.authorization.logout(\n authorizationEndpoint,\n actor,\n token,\n );\n }\n\n protected async onLogout(event: unknown) {\n if (!(event instanceof CustomEvent)) return;\n const parsed = LogoutEventDetailSchema.safeParse(event.detail);\n if (!parsed.success) return;\n\n const actor = parsed.data.logoutId;\n\n try {\n await this.onLogout_(parsed.data);\n } catch (e) {\n const logoutEvent: GraffitiLogoutEvent = new CustomEvent(\"logout\", {\n detail: {\n error: e instanceof Error ? e : new Error(String(e)),\n actor,\n },\n });\n this.sessionEvents.dispatchEvent(logoutEvent);\n }\n }\n protected async onLogout_(\n logoutDetail: infer_<typeof LogoutEventDetailSchema>,\n ) {\n if (logoutDetail.error) throw logoutDetail.error;\n\n const actor = logoutDetail.logoutId;\n\n // Lookup the in-progress session\n let inProgressLogout: infer_<typeof InProgressSchema>;\n if (typeof window !== \"undefined\") {\n const inProgressLogoutString = window.localStorage.getItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY,\n );\n if (!inProgressLogoutString) {\n throw new Error(\"No in-progress logout found\");\n }\n\n const json = JSON.parse(inProgressLogoutString);\n inProgressLogout = InProgressSchema.parse(json);\n } else {\n if (!this.inProgressLogout) {\n throw new Error(\"No in-progress logout found\");\n }\n inProgressLogout = this.inProgressLogout;\n }\n\n if (inProgressLogout.actor !== actor) {\n throw new Error(\n \"Actor mismatch in logout response - concurrent logouts?\",\n );\n }\n\n const token = inProgressLogout.tokens.pop();\n if (!token) {\n // Logout complete\n if (typeof window === \"undefined\") {\n this.inProgressLogout = undefined;\n } else {\n window.localStorage.removeItem(LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY);\n }\n\n const logoutEvent: GraffitiLogoutEvent = new CustomEvent(\"logout\", {\n detail: { actor },\n });\n this.sessionEvents.dispatchEvent(logoutEvent);\n } else {\n // Store the in progress and continue\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY,\n JSON.stringify(inProgressLogout),\n );\n } else {\n this.inProgressLogout = inProgressLogout;\n }\n\n // Continue to the next authorization endpoint\n const [authorizationEndpoint, _] =\n inProgressLogout.servicesByAuthorization[\n inProgressLogout.tokens.length\n ];\n await this.services.authorization.logout(\n authorizationEndpoint,\n actor,\n token,\n );\n }\n }\n\n protected get loggedInSessions(): StoredSession[] {\n if (typeof window === \"undefined\") return loggedInSessions_;\n\n const data = window.localStorage.getItem(\n LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY,\n );\n if (!data) return [];\n\n let json: unknown;\n try {\n json = JSON.parse(data);\n } catch {\n console.error(\"Error parsing stored session data\");\n window.localStorage.removeItem(LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY);\n return [];\n }\n\n const parsed = array(StoredSessionSchema).safeParse(json);\n if (!parsed.success) {\n console.error(\"Stored session data is invalid\");\n window.localStorage.removeItem(LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY);\n return [];\n }\n return parsed.data;\n }\n protected set loggedInSessions(sessions: StoredSession[]) {\n if (typeof window === \"undefined\") {\n loggedInSessions_ = sessions;\n return;\n }\n\n window.localStorage.setItem(\n LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY,\n JSON.stringify(sessions),\n );\n }\n\n resolveSession(session: GraffitiSession): StoredSession {\n const resolvedSession = this.loggedInSessions.find(\n (s) => s.actor === session.actor,\n );\n if (!resolvedSession) {\n const logoutEvent: GraffitiLogoutEvent = new CustomEvent(\"logout\", {\n detail: { actor: session.actor },\n });\n this.sessionEvents.dispatchEvent(logoutEvent);\n throw new Error(\"Not logged in\");\n }\n return resolvedSession;\n }\n}\nlet loggedInSessions_: StoredSession[] = [];\n\nconst LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY = \"graffiti-login-in-progress\";\nconst LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY = \"graffiti-logout-in-progress\";\nconst LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY = \"graffiti-sessions-logged-in\";\n\nconst GraffitiSessionSchema = object({\n actor: url(),\n});\n\nconst ServiceSessionSchema = object({\n token: string(),\n serviceEndpoint: url(),\n authorizationEndpoint: url(),\n});\n\nconst ServicesByAuthorizationSchema = array(\n tuple([\n url(), // Authorization endpoint\n array(\n object({\n endpoint: url(), // Service endpoint\n type: enum_([\"bucket\", \"personal-inbox\", \"shared-inbox\"]),\n }),\n ),\n ]),\n);\n\nconst InProgressSchema = extend(GraffitiSessionSchema, {\n tokens: array(string()),\n servicesByAuthorization: ServicesByAuthorizationSchema,\n});\n\nconst StoredSessionSchema = extend(InProgressSchema, {\n storageBucket: ServiceSessionSchema,\n personalInbox: ServiceSessionSchema,\n sharedInboxes: array(ServiceSessionSchema),\n});\n\ntype StoredSession = infer_<typeof StoredSessionSchema>;\n\nfunction serviceToEndpoint(service: Service): string {\n if (typeof service.serviceEndpoint === \"string\")\n return service.serviceEndpoint;\n throw new Error(`Service endpoint for ${service.id} is not a string`);\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type {\n Graffiti,\n GraffitiLoginEvent,\n GraffitiLogoutEvent,\n GraffitiSession,\n GraffitiSessionInitializedEvent,\n} from \"@graffiti-garden/api\";\nimport { DecentralizedIdentifiers } from \"../1-services/2-dids\";\nimport {\n InitializedEventDetailSchema,\n LoginEventDetailSchema,\n LogoutEventDetailSchema,\n type Authorization,\n} from \"../1-services/1-authorization\";\nimport { StorageBuckets } from \"../1-services/3-storage-buckets\";\nimport type { Inboxes } from \"../1-services/4-inboxes\";\nimport type { Service } from \"did-resolver\";\nimport {\n type infer as infer_,\n extend,\n array,\n string,\n object,\n url,\n tuple,\n enum as enum_,\n} from \"zod/mini\";\n\nexport const DID_SERVICE_TYPE_GRAFFITI_INBOX = \"GraffitiInbox\";\nexport const DID_SERVICE_TYPE_GRAFFITI_STORAGE_BUCKET = \"GraffitiStorageBucket\";\nexport const DID_SERVICE_ID_GRAFFITI_PERSONAL_INBOX = \"#graffitiPersonalInbox\";\nexport const DID_SERVICE_ID_GRAFFITI_STORAGE_BUCKET = \"#graffitiStorageBucket\";\nexport const DID_SERVICE_ID_GRAFFITI_SHARED_INBOX_PREFIX =\n \"#graffitiSharedInbox_\";\n\nexport class Sessions {\n sessionEvents: Graffiti[\"sessionEvents\"] = new EventTarget();\n\n constructor(\n protected readonly services: {\n readonly dids: DecentralizedIdentifiers;\n readonly authorization: Authorization;\n readonly storageBuckets: StorageBuckets;\n readonly inboxes: Inboxes;\n },\n ) {\n const initializedPromise = new Promise<void>((resolve) => {\n this.services.authorization.eventTarget.addEventListener(\n \"initialized\",\n (e) => {\n if (!(e instanceof CustomEvent)) return;\n const parsed = InitializedEventDetailSchema.safeParse(e.detail);\n if (!parsed.success) return;\n const error = parsed.data?.error;\n if (error) console.error(error);\n resolve();\n },\n );\n });\n this.services.authorization.eventTarget.addEventListener(\n \"login\",\n this.onLogin.bind(this),\n );\n this.services.authorization.eventTarget.addEventListener(\n \"logout\",\n this.onLogout.bind(this),\n );\n\n // Handle account registration redirect immediately,\n // to prevent SPA routers from hijacking the URL too soon\n let loginPromise: Promise<void> | undefined;\n if (typeof window !== \"undefined\") {\n const actorEncoded = new URLSearchParams(window.location.search).get(\n \"actor\",\n );\n if (actorEncoded) {\n try {\n // Get the actor\n const actor = decodeURIComponent(actorEncoded);\n // Strip it from the URL\n const url = new URL(window.location.toString());\n url.searchParams.delete(\"actor\");\n window.history.replaceState({}, \"\", url.toString());\n // Complete the login\n loginPromise = this.login(actor);\n } catch (error) {\n console.error(\"Error decoding actor:\", error);\n }\n }\n }\n\n (async () => {\n // Allow listeners to be added before dispatching events\n await new Promise((resolve) => setTimeout(resolve, 0));\n\n // Wait for login to complete, if there\n await loginPromise;\n\n for (const session of this.loggedInSessions) {\n const loginEvent: GraffitiLoginEvent = new CustomEvent(\"login\", {\n detail: { session: { actor: session.actor } },\n });\n this.sessionEvents.dispatchEvent(loginEvent);\n }\n\n await initializedPromise;\n\n // Send own initialized event\n const initializedEvent: GraffitiSessionInitializedEvent = new CustomEvent(\n \"initialized\",\n );\n this.sessionEvents.dispatchEvent(initializedEvent);\n })();\n }\n\n protected inProgressLogin: infer_<typeof InProgressSchema> | undefined =\n undefined;\n protected inProgressLogout: infer_<typeof InProgressSchema> | undefined =\n undefined;\n\n async login(actor: string) {\n try {\n await this.login_(actor);\n } catch (e) {\n const loginEvent: GraffitiLoginEvent = new CustomEvent(\"login\", {\n detail: {\n error: e instanceof Error ? e : new Error(String(e)),\n session: { actor },\n },\n });\n this.sessionEvents.dispatchEvent(loginEvent);\n }\n }\n protected async login_(actor: string) {\n // First look to see if we're already logged in\n const existingSession = this.loggedInSessions.find(\n (session) => session.actor === actor,\n );\n if (existingSession) {\n this.sessionEvents.dispatchEvent(\n new CustomEvent(\"login\", { detail: { session: { actor } } }),\n );\n return;\n }\n\n const actorDocument = await this.services.dids.resolve(actor);\n\n const services = actorDocument.service;\n if (!services) {\n throw new Error(`No services found in actor document for ${actor}`);\n }\n\n const storageBucketService = services.find(\n (service) =>\n service.id === DID_SERVICE_ID_GRAFFITI_STORAGE_BUCKET &&\n service.type === DID_SERVICE_TYPE_GRAFFITI_STORAGE_BUCKET,\n );\n const personalInboxService = services.find(\n (service) =>\n service.id === DID_SERVICE_ID_GRAFFITI_PERSONAL_INBOX &&\n service.type === DID_SERVICE_TYPE_GRAFFITI_INBOX,\n );\n const sharedInboxServices = services.filter(\n (service) =>\n service.id.match(\n new RegExp(`^${DID_SERVICE_ID_GRAFFITI_SHARED_INBOX_PREFIX}\\\\d+$`),\n ) && service.type === DID_SERVICE_TYPE_GRAFFITI_INBOX,\n );\n\n if (\n !personalInboxService ||\n !storageBucketService ||\n sharedInboxServices.length === 0\n ) {\n throw new Error(\n `Required services not found in actor document for ${actor}`,\n );\n }\n\n // Massage the services into a list of endpoints with types\n const storageBucketEndpoint: string =\n serviceToEndpoint(storageBucketService);\n const personalInboxEndpoint: string =\n serviceToEndpoint(personalInboxService);\n const sharedInboxEndpoints: string[] =\n sharedInboxServices.map(serviceToEndpoint);\n const servicesWithTypes = [\n { endpoint: storageBucketEndpoint, type: \"bucket\" } as const,\n { endpoint: personalInboxEndpoint, type: \"personal-inbox\" } as const,\n ...sharedInboxEndpoints.map(\n (endpoint) =>\n ({\n endpoint,\n type: \"shared-inbox\",\n }) as const,\n ),\n ];\n\n // Fetch the authorization endpoints for each service\n const servicesWithAuthorizationEndpoints = await Promise.all(\n servicesWithTypes.map(async ({ endpoint, type }) => {\n const authorizationEndpoint = await (type === \"bucket\"\n ? this.services.storageBuckets.getAuthorizationEndpoint(endpoint)\n : this.services.inboxes.getAuthorizationEndpoint(endpoint));\n return { endpoint, authorizationEndpoint, type };\n }),\n );\n\n // Group the endpoints according to their authorization endpoints\n const servicesByAuthorizationMap: Map<\n string,\n {\n endpoint: string;\n type: \"bucket\" | \"personal-inbox\" | \"shared-inbox\";\n }[]\n > = new Map();\n servicesWithAuthorizationEndpoints.forEach(\n ({ authorizationEndpoint, endpoint, type }) => {\n if (!servicesByAuthorizationMap.has(authorizationEndpoint)) {\n servicesByAuthorizationMap.set(authorizationEndpoint, []);\n }\n servicesByAuthorizationMap\n .get(authorizationEndpoint)!\n .push({ endpoint, type });\n },\n );\n const servicesByAuthorization = [...servicesByAuthorizationMap.entries()];\n\n const session: GraffitiSession = { actor };\n\n const inProgressLogin: infer_<typeof InProgressSchema> = {\n ...session,\n tokens: [],\n servicesByAuthorization,\n };\n\n if (typeof window !== \"undefined\") {\n // Store the in-progress session in localStorage\n window.localStorage.setItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY,\n JSON.stringify(inProgressLogin),\n );\n } else {\n this.inProgressLogin = inProgressLogin;\n }\n\n // Start the login process with the first endpoint\n const [firstAuthorizationEndpoint, firstServices] =\n servicesByAuthorization[0];\n await this.services.authorization.login(\n firstAuthorizationEndpoint,\n actor,\n firstServices.map((s) => s.endpoint),\n );\n }\n\n protected async onLogin(event: unknown) {\n if (!(event instanceof CustomEvent)) return;\n const parsed = LoginEventDetailSchema.safeParse(event.detail);\n if (!parsed.success) return;\n\n const actor = parsed.data.loginId;\n\n try {\n await this.onLogin_(parsed.data);\n } catch (e) {\n const LoginEvent: GraffitiLoginEvent = new CustomEvent(\"login\", {\n detail: {\n error: e instanceof Error ? e : new Error(String(e)),\n session: { actor },\n },\n });\n this.sessionEvents.dispatchEvent(LoginEvent);\n }\n }\n protected async onLogin_(loginDetail: infer_<typeof LoginEventDetailSchema>) {\n if (loginDetail.error) throw loginDetail.error;\n\n const token = loginDetail.token;\n const actor = loginDetail.loginId;\n\n // Lookup the in-progress session\n let inProgressLogin: infer_<typeof InProgressSchema>;\n if (typeof window !== \"undefined\") {\n const inProgressLoginString = window.localStorage.getItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY,\n );\n if (!inProgressLoginString) {\n throw new Error(\"No in-progress login found\");\n }\n\n const json = JSON.parse(inProgressLoginString);\n inProgressLogin = InProgressSchema.parse(json);\n } else {\n if (!this.inProgressLogin) {\n throw new Error(\"No in-progress login found\");\n }\n inProgressLogin = this.inProgressLogin;\n }\n\n if (inProgressLogin.actor !== actor) {\n throw new Error(\"Actor mismatch in login response - concurrent logins?\");\n }\n\n inProgressLogin.tokens.push(token);\n\n if (\n inProgressLogin.tokens.length ===\n inProgressLogin.servicesByAuthorization.length\n ) {\n // Login complete!\n if (typeof window === \"undefined\") {\n this.inProgressLogin = undefined;\n } else {\n window.localStorage.removeItem(LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY);\n }\n\n // Build the completed session\n const services = inProgressLogin.servicesByAuthorization.flatMap(\n ([authorizationEndpoint, services], index) =>\n services.map((service) => ({\n token: inProgressLogin.tokens[index],\n serviceEndpoint: service.endpoint,\n authorizationEndpoint,\n type: service.type,\n })),\n );\n\n const session: StoredSession = {\n ...inProgressLogin,\n storageBucket: services.find((s) => s.type === \"bucket\")!,\n personalInbox: services.find((s) => s.type === \"personal-inbox\")!,\n sharedInboxes: services.filter((s) => s.type === \"shared-inbox\")!,\n };\n\n // Store the completed session\n const sessions = this.loggedInSessions;\n sessions.push(session);\n this.loggedInSessions = sessions;\n\n // Return the completed session\n const loginEvent: GraffitiLoginEvent = new CustomEvent(\"login\", {\n detail: { session: { actor } },\n });\n this.sessionEvents.dispatchEvent(loginEvent);\n } else {\n // Store the in progress and continue\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY,\n JSON.stringify(inProgressLogin),\n );\n } else {\n this.inProgressLogin = inProgressLogin;\n }\n\n // Continue to the next authorization endpoint\n const [authorizationEndpoint, services] =\n inProgressLogin.servicesByAuthorization[inProgressLogin.tokens.length];\n await this.services.authorization.login(\n authorizationEndpoint,\n actor,\n services.map((s) => s.endpoint),\n );\n }\n }\n\n async logout(actor: string) {\n try {\n await this.logout_(actor);\n } catch (e) {\n const logoutEvent: GraffitiLogoutEvent = new CustomEvent(\"logout\", {\n detail: {\n error: e instanceof Error ? e : new Error(String(e)),\n actor,\n },\n });\n this.sessionEvents.dispatchEvent(logoutEvent);\n }\n }\n protected async logout_(actor: string) {\n const session = this.loggedInSessions.find(\n (session) => session.actor === actor,\n );\n if (!session) {\n throw new Error(`No session found for actor ${actor}`);\n }\n\n // Remove the session(s)\n this.loggedInSessions = this.loggedInSessions.filter(\n (session) => session.actor !== actor,\n );\n\n // Begin the logout\n const token = session.tokens.pop();\n if (!token) {\n throw new Error(\"No tokens found in session\");\n }\n // Store the in progress logout\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY,\n JSON.stringify(session),\n );\n } else {\n this.inProgressLogout = session;\n }\n const [authorizationEndpoint, _] =\n session.servicesByAuthorization[session.tokens.length];\n await this.services.authorization.logout(\n authorizationEndpoint,\n actor,\n token,\n );\n }\n\n protected async onLogout(event: unknown) {\n if (!(event instanceof CustomEvent)) return;\n const parsed = LogoutEventDetailSchema.safeParse(event.detail);\n if (!parsed.success) return;\n\n const actor = parsed.data.logoutId;\n\n try {\n await this.onLogout_(parsed.data);\n } catch (e) {\n const logoutEvent: GraffitiLogoutEvent = new CustomEvent(\"logout\", {\n detail: {\n error: e instanceof Error ? e : new Error(String(e)),\n actor,\n },\n });\n this.sessionEvents.dispatchEvent(logoutEvent);\n }\n }\n protected async onLogout_(\n logoutDetail: infer_<typeof LogoutEventDetailSchema>,\n ) {\n if (logoutDetail.error) throw logoutDetail.error;\n\n const actor = logoutDetail.logoutId;\n\n // Lookup the in-progress session\n let inProgressLogout: infer_<typeof InProgressSchema>;\n if (typeof window !== \"undefined\") {\n const inProgressLogoutString = window.localStorage.getItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY,\n );\n if (!inProgressLogoutString) {\n throw new Error(\"No in-progress logout found\");\n }\n\n const json = JSON.parse(inProgressLogoutString);\n inProgressLogout = InProgressSchema.parse(json);\n } else {\n if (!this.inProgressLogout) {\n throw new Error(\"No in-progress logout found\");\n }\n inProgressLogout = this.inProgressLogout;\n }\n\n if (inProgressLogout.actor !== actor) {\n throw new Error(\n \"Actor mismatch in logout response - concurrent logouts?\",\n );\n }\n\n const token = inProgressLogout.tokens.pop();\n if (!token) {\n // Logout complete\n if (typeof window === \"undefined\") {\n this.inProgressLogout = undefined;\n } else {\n window.localStorage.removeItem(LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY);\n }\n\n const logoutEvent: GraffitiLogoutEvent = new CustomEvent(\"logout\", {\n detail: { actor },\n });\n this.sessionEvents.dispatchEvent(logoutEvent);\n } else {\n // Store the in progress and continue\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(\n LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY,\n JSON.stringify(inProgressLogout),\n );\n } else {\n this.inProgressLogout = inProgressLogout;\n }\n\n // Continue to the next authorization endpoint\n const [authorizationEndpoint, _] =\n inProgressLogout.servicesByAuthorization[\n inProgressLogout.tokens.length\n ];\n await this.services.authorization.logout(\n authorizationEndpoint,\n actor,\n token,\n );\n }\n }\n\n protected get loggedInSessions(): StoredSession[] {\n if (typeof window === \"undefined\") return loggedInSessions_;\n\n const data = window.localStorage.getItem(\n LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY,\n );\n if (!data) return [];\n\n let json: unknown;\n try {\n json = JSON.parse(data);\n } catch {\n console.error(\"Error parsing stored session data\");\n window.localStorage.removeItem(LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY);\n return [];\n }\n\n const parsed = array(StoredSessionSchema).safeParse(json);\n if (!parsed.success) {\n console.error(\"Stored session data is invalid\");\n window.localStorage.removeItem(LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY);\n return [];\n }\n return parsed.data;\n }\n protected set loggedInSessions(sessions: StoredSession[]) {\n if (typeof window === \"undefined\") {\n loggedInSessions_ = sessions;\n return;\n }\n\n window.localStorage.setItem(\n LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY,\n JSON.stringify(sessions),\n );\n }\n\n resolveSession(session: GraffitiSession): StoredSession {\n const resolvedSession = this.loggedInSessions.find(\n (s) => s.actor === session.actor,\n );\n if (!resolvedSession) {\n const logoutEvent: GraffitiLogoutEvent = new CustomEvent(\"logout\", {\n detail: { actor: session.actor },\n });\n this.sessionEvents.dispatchEvent(logoutEvent);\n throw new Error(\"Not logged in\");\n }\n return resolvedSession;\n }\n}\nlet loggedInSessions_: StoredSession[] = [];\n\nconst LOCAL_STORAGE_IN_PROGRESS_LOGIN_KEY = \"graffiti-login-in-progress\";\nconst LOCAL_STORAGE_IN_PROGRESS_LOGOUT_KEY = \"graffiti-logout-in-progress\";\nconst LOCAL_STORAGE_LOGGED_IN_SESSIONS_KEY = \"graffiti-sessions-logged-in\";\n\nconst GraffitiSessionSchema = object({\n actor: url(),\n});\n\nconst ServiceSessionSchema = object({\n token: string(),\n serviceEndpoint: url(),\n authorizationEndpoint: url(),\n});\n\nconst ServicesByAuthorizationSchema = array(\n tuple([\n url(), // Authorization endpoint\n array(\n object({\n endpoint: url(), // Service endpoint\n type: enum_([\"bucket\", \"personal-inbox\", \"shared-inbox\"]),\n }),\n ),\n ]),\n);\n\nconst InProgressSchema = extend(GraffitiSessionSchema, {\n tokens: array(string()),\n servicesByAuthorization: ServicesByAuthorizationSchema,\n});\n\nconst StoredSessionSchema = extend(InProgressSchema, {\n storageBucket: ServiceSessionSchema,\n personalInbox: ServiceSessionSchema,\n sharedInboxes: array(ServiceSessionSchema),\n});\n\ntype StoredSession = infer_<typeof StoredSessionSchema>;\n\nfunction serviceToEndpoint(service: Service): string {\n if (typeof service.serviceEndpoint === \"string\")\n return service.serviceEndpoint;\n throw new Error(`Service endpoint for ${service.id} is not a string`);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["url", "session", "services"]
7
7
  }
@@ -25,8 +25,9 @@ class ObjectEncoding {
25
25
  }
26
26
  async encode(partialObject, actor) {
27
27
  partialObject = cleanUndefined(partialObject);
28
+ const uniqueChannels = [...new Set(partialObject.channels)];
28
29
  const channelAttestationAndPublicIds = await Promise.all(
29
- partialObject.channels.map(
30
+ uniqueChannels.map(
30
31
  (channel) => this.primitives.channelAttestations.attest(
31
32
  // TODO: get this from the DID document of the actor
32
33
  CHANNEL_ATTESTATION_METHOD_SHA256_ED25519,
@@ -79,7 +80,7 @@ class ObjectEncoding {
79
80
  const tags = [new TextEncoder().encode(objectUrl), ...channelPublicIds];
80
81
  const object = {
81
82
  value: partialObject.value,
82
- channels: partialObject.channels,
83
+ channels: uniqueChannels,
83
84
  url: objectUrl,
84
85
  actor,
85
86
  ...partialObject.allowed ? {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/3-protocol/3-object-encoding.ts"],
4
- "sourcesContent": ["import type { JSONSchema } from \"json-schema-to-ts\";\nimport type {\n GraffitiObject,\n GraffitiObjectBase,\n GraffitiPostObject,\n} from \"@graffiti-garden/api\";\nimport type { ChannelAttestations } from \"../2-primitives/3-channel-attestations\";\nimport type { AllowedAttestations } from \"../2-primitives/4-allowed-attestations\";\nimport {\n CONTENT_ADDRESS_METHOD_SHA256,\n type ContentAddresses,\n} from \"../2-primitives/2-content-addresses\";\nimport { randomBytes } from \"@noble/hashes/utils.js\";\nimport {\n encode as dagCborEncode,\n decode as dagCborDecode,\n} from \"@ipld/dag-cbor\";\nimport {\n type infer as infer_,\n array,\n custom,\n looseObject,\n optional,\n strictObject,\n} from \"zod/mini\";\nimport { CHANNEL_ATTESTATION_METHOD_SHA256_ED25519 } from \"../2-primitives/3-channel-attestations\";\nimport { ALLOWED_ATTESTATION_METHOD_HMAC_SHA256 } from \"../2-primitives/4-allowed-attestations\";\nimport {\n STRING_ENCODER_METHOD_BASE64URL,\n type StringEncoder,\n} from \"../2-primitives/1-string-encoding\";\n\n// Objects have a max size of 32kb\n// If each channel and allowed actor takes 32 bytes\n// of space (i.e. they are hashed with 256 bit security)\n// then this means that the combined number of channels\n// and recipients of object has cannot exceed one thousand.\n// This seems like a reasonable limit and on par with\n// signal's group chat limit of 1000\nexport const MAX_OBJECT_SIZE_BYTES = 32 * 1024;\n\nexport class ObjectEncoding {\n constructor(\n protected readonly primitives: {\n readonly stringEncoder: StringEncoder;\n readonly channelAttestations: ChannelAttestations;\n readonly allowedAttestations: AllowedAttestations;\n readonly contentAddresses: ContentAddresses;\n },\n ) {}\n\n async encode<Schema extends JSONSchema>(\n partialObject: GraffitiPostObject<Schema>,\n actor: string,\n ): Promise<{\n object: GraffitiObject<Schema>;\n tags: Uint8Array[];\n objectBytes: Uint8Array;\n allowedTickets: Uint8Array[] | undefined;\n }> {\n // Clean out any undefineds\n partialObject = cleanUndefined(partialObject);\n\n // Create a verifiable attestation that the actor\n // knows the included channels without\n // directly revealing any channel to anyone who doesn't\n // know the channel already\n const channelAttestationAndPublicIds = await Promise.all(\n partialObject.channels.map((channel) =>\n this.primitives.channelAttestations.attest(\n // TODO: get this from the DID document of the actor\n CHANNEL_ATTESTATION_METHOD_SHA256_ED25519,\n actor,\n channel,\n ),\n ),\n );\n const channelAttestations = channelAttestationAndPublicIds.map(\n (c) => c.attestation,\n );\n const channelPublicIds = channelAttestationAndPublicIds.map(\n (c) => c.channelPublicId,\n );\n\n const objectData: infer_<typeof ObjectDataSchema> = {\n [VALUE_PROPERTY]: partialObject.value,\n [CHANNEL_ATTESTATIONS_PROPERTY]: channelAttestations,\n [NONCE_PROPERTY]: randomBytes(32),\n };\n\n let allowedTickets: Uint8Array[] | undefined = undefined;\n\n // If the object is private...\n if (Array.isArray(partialObject.allowed)) {\n // Create an attestation that the object's allowed list\n // includes the given actors, without revealing the\n // presence of an actor on the list to anyone except\n // that actor themselves. Each actor will receive a\n // \"ticket\" that they can use to verify their own membership\n // on the allowed list.\n const allowedAttestations = await Promise.all(\n partialObject.allowed.map(async (allowedActor) =>\n this.primitives.allowedAttestations.attest(\n // TODO: get this from the DID document of the actor\n ALLOWED_ATTESTATION_METHOD_HMAC_SHA256,\n allowedActor,\n ),\n ),\n );\n objectData[ALLOWED_ATTESTATIONS_PROPERTY] = allowedAttestations.map(\n (a) => a.attestation,\n );\n allowedTickets = allowedAttestations.map((a) => a.ticket);\n }\n\n // Encode the mixed JSON/binary data using CBOR\n const objectBytes = dagCborEncode(objectData);\n if (objectBytes.byteLength > MAX_OBJECT_SIZE_BYTES) {\n throw new Error(\"The object is too large\");\n }\n\n // Compute a public identifier (hash) of the object data\n const objectContentAddressBytes =\n await this.primitives.contentAddresses.register(\n // TODO: get this from the DID document of the actor\n CONTENT_ADDRESS_METHOD_SHA256,\n objectBytes,\n );\n const objectContentAddress = await this.primitives.stringEncoder.encode(\n STRING_ENCODER_METHOD_BASE64URL,\n objectContentAddressBytes,\n );\n // Use it to compute the object's URL\n const objectUrl = encodeObjectUrl(actor, objectContentAddress);\n\n const tags = [new TextEncoder().encode(objectUrl), ...channelPublicIds];\n\n const object: GraffitiObject<Schema> = {\n value: partialObject.value,\n channels: partialObject.channels,\n url: objectUrl,\n actor,\n ...(partialObject.allowed\n ? {\n allowed: partialObject.allowed,\n }\n : {}),\n } as GraffitiObject<Schema>;\n\n // Return object URL and allowed secrets\n return {\n object,\n tags,\n objectBytes,\n allowedTickets,\n };\n }\n\n async validate(\n object: GraffitiObjectBase,\n tags: Uint8Array[],\n objectBytes: Uint8Array,\n privateObjectInfo?:\n | {\n recipient: string;\n allowedTicket: Uint8Array;\n allowedIndex: number;\n }\n | {\n allowedTickets: Uint8Array[];\n },\n ): Promise<void> {\n if (objectBytes.byteLength > MAX_OBJECT_SIZE_BYTES) {\n throw new Error(\"Object is too big\");\n }\n const { actor, contentAddress } = decodeObjectUrl(object.url);\n if (actor !== object.actor) {\n throw new Error(\"Object actor does not match URL actor\");\n }\n\n const objectUrlTag = tags.at(0);\n if (!objectUrlTag) {\n throw new Error(\"No object URL tag\");\n }\n if (new TextDecoder().decode(objectUrlTag) !== object.url) {\n throw new Error(\"Object URL tag does not match object URL\");\n }\n const channelPublicIds = tags.slice(1);\n\n // Make sure the object content address matches the object content\n const contentAddressBytes =\n await this.primitives.stringEncoder.decode(contentAddress);\n const contentAddressMethod =\n await this.primitives.contentAddresses.getMethod(contentAddressBytes);\n const expectedContentAddress =\n await this.primitives.contentAddresses.register(\n contentAddressMethod,\n objectBytes,\n );\n if (\n expectedContentAddress.length !== contentAddressBytes.length ||\n !expectedContentAddress.every((b, i) => b === contentAddressBytes[i])\n ) {\n throw new Error(\"Content address is invalid\");\n }\n\n // Convert the raw object data from CBOR\n // back to a javascript object\n const objectDataUnknown = dagCborDecode(objectBytes);\n const objectData = ObjectDataSchema.parse(objectDataUnknown);\n\n // And extract the values\n const value = objectData[VALUE_PROPERTY];\n const channelAttestations = objectData[CHANNEL_ATTESTATIONS_PROPERTY];\n const allowedAttestations = objectData[ALLOWED_ATTESTATIONS_PROPERTY];\n\n // Validate that the object's value matches\n const valueBytes = dagCborEncode(value);\n const expectedValueBytes = dagCborEncode(object.value);\n if (\n valueBytes.length !== expectedValueBytes.length ||\n !valueBytes.every((b, i) => b === expectedValueBytes[i])\n ) {\n throw new Error(\"Object value does not match storage value\");\n }\n\n // Validate the object's channels\n if (channelAttestations.length !== channelPublicIds.length) {\n throw new Error(\"Not as many channel attestations and public ids\");\n }\n for (const [index, attestation] of channelAttestations.entries()) {\n const channelPublicId = channelPublicIds[index];\n const isValid = await this.primitives.channelAttestations.validate(\n attestation,\n actor,\n channelPublicId,\n );\n if (!isValid) {\n throw new Error(\"Invalid channel attestation\");\n }\n }\n if (object.channels.length) {\n // If any channels are included, they all must be included\n if (object.channels.length !== channelPublicIds.length) {\n throw new Error(\n \"Number of claimed channels does not match attestations/public IDs\",\n );\n }\n const channelAttestationMethod =\n await this.primitives.channelAttestations.getMethod(\n channelPublicIds[0],\n );\n const expectedChannelPublicIds = await Promise.all(\n object.channels.map((channel) =>\n this.primitives.channelAttestations.register(\n channelAttestationMethod,\n channel,\n ),\n ),\n );\n for (const [\n index,\n expectedPublicId,\n ] of expectedChannelPublicIds.entries()) {\n const actualPublicId = channelPublicIds[index];\n if (\n expectedPublicId.length !== actualPublicId.length ||\n !expectedPublicId.every((b, i) => b === actualPublicId[i])\n ) {\n throw new Error(\"Channel public id does not match expected\");\n }\n }\n }\n\n // Validate the recipient\n if (privateObjectInfo) {\n if (!allowedAttestations) {\n throw new Error(\"Object is public but thought to be private\");\n }\n\n let recipients: string[];\n let allowedTickets: Uint8Array[];\n let attestations: Uint8Array[];\n if (\"recipient\" in privateObjectInfo) {\n recipients = [privateObjectInfo.recipient];\n allowedTickets = [privateObjectInfo.allowedTicket];\n attestations = allowedAttestations.filter(\n (_, i) => i === privateObjectInfo.allowedIndex,\n );\n } else {\n recipients = [...(object.allowed ?? [])];\n allowedTickets = privateObjectInfo.allowedTickets;\n attestations = allowedAttestations;\n }\n\n // All recipients must be in the allowed list\n if (recipients.length !== object.allowed?.length) {\n throw new Error(\"Recipient count does not match object allowed list\");\n }\n if (!recipients.every((r) => object.allowed?.includes(r))) {\n throw new Error(\"Recipient not in object allowed list\");\n }\n\n for (const [index, recipient] of recipients.entries()) {\n const allowedTicket = allowedTickets.at(index);\n const allowedAttestation = attestations.at(index);\n if (!allowedTicket) {\n throw new Error(\"Missing allowed ticket for recipient\");\n }\n if (!allowedAttestation) {\n throw new Error(\"Missing allowed attestation for recipient\");\n }\n const isValid = await this.primitives.allowedAttestations.validate(\n allowedAttestation,\n recipient,\n allowedTicket,\n );\n\n if (!isValid) {\n throw new Error(\"Invalid allowed attestation for recipient\");\n }\n }\n } else if (allowedAttestations) {\n throw new Error(\"Object is private but no recipient info provided\");\n }\n }\n}\n\n// A compact data representation of the object data\nconst VALUE_PROPERTY = \"v\";\nconst CHANNEL_ATTESTATIONS_PROPERTY = \"c\";\nconst ALLOWED_ATTESTATIONS_PROPERTY = \"a\";\nconst NONCE_PROPERTY = \"n\";\n\nconst Uint8ArraySchema = custom<Uint8Array>(\n (v): v is Uint8Array => v instanceof Uint8Array,\n);\n\nconst ObjectDataSchema = strictObject({\n [VALUE_PROPERTY]: looseObject({}),\n [CHANNEL_ATTESTATIONS_PROPERTY]: array(Uint8ArraySchema),\n [ALLOWED_ATTESTATIONS_PROPERTY]: optional(array(Uint8ArraySchema)),\n [NONCE_PROPERTY]: Uint8ArraySchema,\n});\n\nexport const GRAFFITI_OBJECT_URL_PREFIX = \"graffiti:\";\n\n// Methods to encode and decode object URLs\nexport function encodeObjectUrlComponent(value: string) {\n const replaced = value.replace(/:/g, \"!\").replace(/\\//g, \"~\");\n return encodeURIComponent(replaced);\n}\nexport function decodeObjectUrlComponent(value: string) {\n const decoded = decodeURIComponent(value);\n return decoded.replace(/!/g, \":\").replace(/~/g, \"/\");\n}\nexport function encodeObjectUrl(actor: string, contentAddress: string) {\n return `${GRAFFITI_OBJECT_URL_PREFIX}${encodeObjectUrlComponent(actor)}:${encodeObjectUrlComponent(contentAddress)}`;\n}\nexport function decodeObjectUrl(objectUrl: string) {\n if (!objectUrl.startsWith(GRAFFITI_OBJECT_URL_PREFIX)) {\n throw new Error(\"Invalid object URL\");\n }\n\n const rest = objectUrl.slice(GRAFFITI_OBJECT_URL_PREFIX.length);\n const parts = rest.split(\":\");\n\n if (parts.length !== 2) {\n throw new Error(\"Invalid object URL format\");\n }\n\n const [actor, contentAddress] = parts;\n\n return {\n actor: decodeObjectUrlComponent(actor),\n contentAddress: decodeObjectUrlComponent(contentAddress),\n };\n}\n\nfunction cleanUndefined(value: any): any {\n if (value === undefined) return null;\n\n if (Array.isArray(value)) {\n return value.map(cleanUndefined);\n }\n\n if (typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => [k, cleanUndefined(v)]),\n );\n }\n\n return value;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { JSONSchema } from \"json-schema-to-ts\";\nimport type {\n GraffitiObject,\n GraffitiObjectBase,\n GraffitiPostObject,\n} from \"@graffiti-garden/api\";\nimport type { ChannelAttestations } from \"../2-primitives/3-channel-attestations\";\nimport type { AllowedAttestations } from \"../2-primitives/4-allowed-attestations\";\nimport {\n CONTENT_ADDRESS_METHOD_SHA256,\n type ContentAddresses,\n} from \"../2-primitives/2-content-addresses\";\nimport { randomBytes } from \"@noble/hashes/utils.js\";\nimport {\n encode as dagCborEncode,\n decode as dagCborDecode,\n} from \"@ipld/dag-cbor\";\nimport {\n type infer as infer_,\n array,\n custom,\n looseObject,\n optional,\n strictObject,\n} from \"zod/mini\";\nimport { CHANNEL_ATTESTATION_METHOD_SHA256_ED25519 } from \"../2-primitives/3-channel-attestations\";\nimport { ALLOWED_ATTESTATION_METHOD_HMAC_SHA256 } from \"../2-primitives/4-allowed-attestations\";\nimport {\n STRING_ENCODER_METHOD_BASE64URL,\n type StringEncoder,\n} from \"../2-primitives/1-string-encoding\";\n\n// Objects have a max size of 32kb\n// If each channel and allowed actor takes 32 bytes\n// of space (i.e. they are hashed with 256 bit security)\n// then this means that the combined number of channels\n// and recipients of object has cannot exceed one thousand.\n// This seems like a reasonable limit and on par with\n// signal's group chat limit of 1000\nexport const MAX_OBJECT_SIZE_BYTES = 32 * 1024;\n\nexport class ObjectEncoding {\n constructor(\n protected readonly primitives: {\n readonly stringEncoder: StringEncoder;\n readonly channelAttestations: ChannelAttestations;\n readonly allowedAttestations: AllowedAttestations;\n readonly contentAddresses: ContentAddresses;\n },\n ) {}\n\n async encode<Schema extends JSONSchema>(\n partialObject: GraffitiPostObject<Schema>,\n actor: string,\n ): Promise<{\n object: GraffitiObject<Schema>;\n tags: Uint8Array[];\n objectBytes: Uint8Array;\n allowedTickets: Uint8Array[] | undefined;\n }> {\n // Clean out any undefineds\n partialObject = cleanUndefined(partialObject);\n\n const uniqueChannels = [...new Set(partialObject.channels)];\n\n // Create a verifiable attestation that the actor\n // knows the included channels without\n // directly revealing any channel to anyone who doesn't\n // know the channel already\n const channelAttestationAndPublicIds = await Promise.all(\n uniqueChannels.map((channel) =>\n this.primitives.channelAttestations.attest(\n // TODO: get this from the DID document of the actor\n CHANNEL_ATTESTATION_METHOD_SHA256_ED25519,\n actor,\n channel,\n ),\n ),\n );\n const channelAttestations = channelAttestationAndPublicIds.map(\n (c) => c.attestation,\n );\n const channelPublicIds = channelAttestationAndPublicIds.map(\n (c) => c.channelPublicId,\n );\n\n const objectData: infer_<typeof ObjectDataSchema> = {\n [VALUE_PROPERTY]: partialObject.value,\n [CHANNEL_ATTESTATIONS_PROPERTY]: channelAttestations,\n [NONCE_PROPERTY]: randomBytes(32),\n };\n\n let allowedTickets: Uint8Array[] | undefined = undefined;\n\n // If the object is private...\n if (Array.isArray(partialObject.allowed)) {\n // Create an attestation that the object's allowed list\n // includes the given actors, without revealing the\n // presence of an actor on the list to anyone except\n // that actor themselves. Each actor will receive a\n // \"ticket\" that they can use to verify their own membership\n // on the allowed list.\n const allowedAttestations = await Promise.all(\n partialObject.allowed.map(async (allowedActor) =>\n this.primitives.allowedAttestations.attest(\n // TODO: get this from the DID document of the actor\n ALLOWED_ATTESTATION_METHOD_HMAC_SHA256,\n allowedActor,\n ),\n ),\n );\n objectData[ALLOWED_ATTESTATIONS_PROPERTY] = allowedAttestations.map(\n (a) => a.attestation,\n );\n allowedTickets = allowedAttestations.map((a) => a.ticket);\n }\n\n // Encode the mixed JSON/binary data using CBOR\n const objectBytes = dagCborEncode(objectData);\n if (objectBytes.byteLength > MAX_OBJECT_SIZE_BYTES) {\n throw new Error(\"The object is too large\");\n }\n\n // Compute a public identifier (hash) of the object data\n const objectContentAddressBytes =\n await this.primitives.contentAddresses.register(\n // TODO: get this from the DID document of the actor\n CONTENT_ADDRESS_METHOD_SHA256,\n objectBytes,\n );\n const objectContentAddress = await this.primitives.stringEncoder.encode(\n STRING_ENCODER_METHOD_BASE64URL,\n objectContentAddressBytes,\n );\n // Use it to compute the object's URL\n const objectUrl = encodeObjectUrl(actor, objectContentAddress);\n\n const tags = [new TextEncoder().encode(objectUrl), ...channelPublicIds];\n\n const object: GraffitiObject<Schema> = {\n value: partialObject.value,\n channels: uniqueChannels,\n url: objectUrl,\n actor,\n ...(partialObject.allowed\n ? {\n allowed: partialObject.allowed,\n }\n : {}),\n } as GraffitiObject<Schema>;\n\n // Return object URL and allowed secrets\n return {\n object,\n tags,\n objectBytes,\n allowedTickets,\n };\n }\n\n async validate(\n object: GraffitiObjectBase,\n tags: Uint8Array[],\n objectBytes: Uint8Array,\n privateObjectInfo?:\n | {\n recipient: string;\n allowedTicket: Uint8Array;\n allowedIndex: number;\n }\n | {\n allowedTickets: Uint8Array[];\n },\n ): Promise<void> {\n if (objectBytes.byteLength > MAX_OBJECT_SIZE_BYTES) {\n throw new Error(\"Object is too big\");\n }\n const { actor, contentAddress } = decodeObjectUrl(object.url);\n if (actor !== object.actor) {\n throw new Error(\"Object actor does not match URL actor\");\n }\n\n const objectUrlTag = tags.at(0);\n if (!objectUrlTag) {\n throw new Error(\"No object URL tag\");\n }\n if (new TextDecoder().decode(objectUrlTag) !== object.url) {\n throw new Error(\"Object URL tag does not match object URL\");\n }\n const channelPublicIds = tags.slice(1);\n\n // Make sure the object content address matches the object content\n const contentAddressBytes =\n await this.primitives.stringEncoder.decode(contentAddress);\n const contentAddressMethod =\n await this.primitives.contentAddresses.getMethod(contentAddressBytes);\n const expectedContentAddress =\n await this.primitives.contentAddresses.register(\n contentAddressMethod,\n objectBytes,\n );\n if (\n expectedContentAddress.length !== contentAddressBytes.length ||\n !expectedContentAddress.every((b, i) => b === contentAddressBytes[i])\n ) {\n throw new Error(\"Content address is invalid\");\n }\n\n // Convert the raw object data from CBOR\n // back to a javascript object\n const objectDataUnknown = dagCborDecode(objectBytes);\n const objectData = ObjectDataSchema.parse(objectDataUnknown);\n\n // And extract the values\n const value = objectData[VALUE_PROPERTY];\n const channelAttestations = objectData[CHANNEL_ATTESTATIONS_PROPERTY];\n const allowedAttestations = objectData[ALLOWED_ATTESTATIONS_PROPERTY];\n\n // Validate that the object's value matches\n const valueBytes = dagCborEncode(value);\n const expectedValueBytes = dagCborEncode(object.value);\n if (\n valueBytes.length !== expectedValueBytes.length ||\n !valueBytes.every((b, i) => b === expectedValueBytes[i])\n ) {\n throw new Error(\"Object value does not match storage value\");\n }\n\n // Validate the object's channels\n if (channelAttestations.length !== channelPublicIds.length) {\n throw new Error(\"Not as many channel attestations and public ids\");\n }\n for (const [index, attestation] of channelAttestations.entries()) {\n const channelPublicId = channelPublicIds[index];\n const isValid = await this.primitives.channelAttestations.validate(\n attestation,\n actor,\n channelPublicId,\n );\n if (!isValid) {\n throw new Error(\"Invalid channel attestation\");\n }\n }\n if (object.channels.length) {\n // If any channels are included, they all must be included\n if (object.channels.length !== channelPublicIds.length) {\n throw new Error(\n \"Number of claimed channels does not match attestations/public IDs\",\n );\n }\n const channelAttestationMethod =\n await this.primitives.channelAttestations.getMethod(\n channelPublicIds[0],\n );\n const expectedChannelPublicIds = await Promise.all(\n object.channels.map((channel) =>\n this.primitives.channelAttestations.register(\n channelAttestationMethod,\n channel,\n ),\n ),\n );\n for (const [\n index,\n expectedPublicId,\n ] of expectedChannelPublicIds.entries()) {\n const actualPublicId = channelPublicIds[index];\n if (\n expectedPublicId.length !== actualPublicId.length ||\n !expectedPublicId.every((b, i) => b === actualPublicId[i])\n ) {\n throw new Error(\"Channel public id does not match expected\");\n }\n }\n }\n\n // Validate the recipient\n if (privateObjectInfo) {\n if (!allowedAttestations) {\n throw new Error(\"Object is public but thought to be private\");\n }\n\n let recipients: string[];\n let allowedTickets: Uint8Array[];\n let attestations: Uint8Array[];\n if (\"recipient\" in privateObjectInfo) {\n recipients = [privateObjectInfo.recipient];\n allowedTickets = [privateObjectInfo.allowedTicket];\n attestations = allowedAttestations.filter(\n (_, i) => i === privateObjectInfo.allowedIndex,\n );\n } else {\n recipients = [...(object.allowed ?? [])];\n allowedTickets = privateObjectInfo.allowedTickets;\n attestations = allowedAttestations;\n }\n\n // All recipients must be in the allowed list\n if (recipients.length !== object.allowed?.length) {\n throw new Error(\"Recipient count does not match object allowed list\");\n }\n if (!recipients.every((r) => object.allowed?.includes(r))) {\n throw new Error(\"Recipient not in object allowed list\");\n }\n\n for (const [index, recipient] of recipients.entries()) {\n const allowedTicket = allowedTickets.at(index);\n const allowedAttestation = attestations.at(index);\n if (!allowedTicket) {\n throw new Error(\"Missing allowed ticket for recipient\");\n }\n if (!allowedAttestation) {\n throw new Error(\"Missing allowed attestation for recipient\");\n }\n const isValid = await this.primitives.allowedAttestations.validate(\n allowedAttestation,\n recipient,\n allowedTicket,\n );\n\n if (!isValid) {\n throw new Error(\"Invalid allowed attestation for recipient\");\n }\n }\n } else if (allowedAttestations) {\n throw new Error(\"Object is private but no recipient info provided\");\n }\n }\n}\n\n// A compact data representation of the object data\nconst VALUE_PROPERTY = \"v\";\nconst CHANNEL_ATTESTATIONS_PROPERTY = \"c\";\nconst ALLOWED_ATTESTATIONS_PROPERTY = \"a\";\nconst NONCE_PROPERTY = \"n\";\n\nconst Uint8ArraySchema = custom<Uint8Array>(\n (v): v is Uint8Array => v instanceof Uint8Array,\n);\n\nconst ObjectDataSchema = strictObject({\n [VALUE_PROPERTY]: looseObject({}),\n [CHANNEL_ATTESTATIONS_PROPERTY]: array(Uint8ArraySchema),\n [ALLOWED_ATTESTATIONS_PROPERTY]: optional(array(Uint8ArraySchema)),\n [NONCE_PROPERTY]: Uint8ArraySchema,\n});\n\nexport const GRAFFITI_OBJECT_URL_PREFIX = \"graffiti:\";\n\n// Methods to encode and decode object URLs\nexport function encodeObjectUrlComponent(value: string) {\n const replaced = value.replace(/:/g, \"!\").replace(/\\//g, \"~\");\n return encodeURIComponent(replaced);\n}\nexport function decodeObjectUrlComponent(value: string) {\n const decoded = decodeURIComponent(value);\n return decoded.replace(/!/g, \":\").replace(/~/g, \"/\");\n}\nexport function encodeObjectUrl(actor: string, contentAddress: string) {\n return `${GRAFFITI_OBJECT_URL_PREFIX}${encodeObjectUrlComponent(actor)}:${encodeObjectUrlComponent(contentAddress)}`;\n}\nexport function decodeObjectUrl(objectUrl: string) {\n if (!objectUrl.startsWith(GRAFFITI_OBJECT_URL_PREFIX)) {\n throw new Error(\"Invalid object URL\");\n }\n\n const rest = objectUrl.slice(GRAFFITI_OBJECT_URL_PREFIX.length);\n const parts = rest.split(\":\");\n\n if (parts.length !== 2) {\n throw new Error(\"Invalid object URL format\");\n }\n\n const [actor, contentAddress] = parts;\n\n return {\n actor: decodeObjectUrlComponent(actor),\n contentAddress: decodeObjectUrlComponent(contentAddress),\n };\n}\n\nfunction cleanUndefined(value: any): any {\n if (value === undefined) return null;\n\n if (Array.isArray(value)) {\n return value.map(cleanUndefined);\n }\n\n if (typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => [k, cleanUndefined(v)]),\n );\n }\n\n return value;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }