@fitzzero/quickdraw-core 5.1.2 → 5.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +793 -57
- package/dist/{admin-DWWrT1h6.d.ts → admin-CS7JB_UH.d.ts} +2 -2
- package/dist/{adminSchemas-D4bFKsrb.d.ts → adminSchemas-29cGgrg5.d.ts} +1 -1
- package/dist/{auth-DXiSJZaf.d.ts → auth-DtdYjrZ9.d.ts} +11 -7
- package/dist/{caller-BT-Mv4bD.d.ts → caller-BTwGl7vm.d.ts} +183 -15
- package/dist/chunk-2FDSKLBO.js +82 -0
- package/dist/chunk-2FDSKLBO.js.map +1 -0
- package/dist/{chunk-GSIPUNJL.js → chunk-64I6MYBC.js} +139 -792
- package/dist/chunk-64I6MYBC.js.map +1 -0
- package/dist/chunk-7473HK24.js +17 -0
- package/dist/chunk-7473HK24.js.map +1 -0
- package/dist/chunk-7Y5ROPXL.js +33 -0
- package/dist/chunk-7Y5ROPXL.js.map +1 -0
- package/dist/chunk-A2RBQD66.js +7 -0
- package/dist/chunk-A2RBQD66.js.map +1 -0
- package/dist/chunk-ASAMUGHG.js +651 -0
- package/dist/chunk-ASAMUGHG.js.map +1 -0
- package/dist/{chunk-B2P7AZ25.js → chunk-CYE4UBOB.js} +6 -4
- package/dist/chunk-CYE4UBOB.js.map +1 -0
- package/dist/chunk-DNP65TD5.js +832 -0
- package/dist/chunk-DNP65TD5.js.map +1 -0
- package/dist/{chunk-5CGO426C.js → chunk-GYLWEJHW.js} +5 -1
- package/dist/chunk-GYLWEJHW.js.map +1 -0
- package/dist/{chunk-AONAFOYU.js → chunk-I3JCFS7H.js} +25 -13
- package/dist/chunk-I3JCFS7H.js.map +1 -0
- package/dist/{chunk-X5CWPX7H.js → chunk-IIQ3WMJK.js} +13 -2
- package/dist/chunk-IIQ3WMJK.js.map +1 -0
- package/dist/{chunk-6K5BY47X.js → chunk-JVTONQFI.js} +1277 -277
- package/dist/chunk-JVTONQFI.js.map +1 -0
- package/dist/{chunk-RL4VKLBX.js → chunk-KKQANRLG.js} +2 -2
- package/dist/{chunk-RL4VKLBX.js.map → chunk-KKQANRLG.js.map} +1 -1
- package/dist/{chunk-GKH3LJ7N.js → chunk-MPQAT55F.js} +38 -205
- package/dist/chunk-MPQAT55F.js.map +1 -0
- package/dist/chunk-NX2V6SIL.js +202 -0
- package/dist/chunk-NX2V6SIL.js.map +1 -0
- package/dist/{chunk-7QCUX4TK.js → chunk-NZHA4TZD.js} +72 -175
- package/dist/chunk-NZHA4TZD.js.map +1 -0
- package/dist/{chunk-MBH6IDSM.js → chunk-OCZJ4EYV.js} +40 -17
- package/dist/chunk-OCZJ4EYV.js.map +1 -0
- package/dist/{chunk-RJ5I2VO3.js → chunk-PHCURHC7.js} +6 -6
- package/dist/chunk-PHCURHC7.js.map +1 -0
- package/dist/{chunk-4YO5H5ON.js → chunk-Q4OGQM5I.js} +4 -10
- package/dist/chunk-Q4OGQM5I.js.map +1 -0
- package/dist/chunk-QSU47XEX.js +13 -0
- package/dist/chunk-QSU47XEX.js.map +1 -0
- package/dist/{chunk-LCC7HNFP.js → chunk-RZXJTDH5.js} +2 -2
- package/dist/{chunk-Z2LZ7IZ7.js → chunk-SN3OZVEQ.js} +5 -31
- package/dist/chunk-SN3OZVEQ.js.map +1 -0
- package/dist/{chunk-Z5ILULF2.js → chunk-SQ5VUG77.js} +4 -3
- package/dist/{chunk-Z5ILULF2.js.map → chunk-SQ5VUG77.js.map} +1 -1
- package/dist/chunk-STH5DLQP.js +1192 -0
- package/dist/chunk-STH5DLQP.js.map +1 -0
- package/dist/{chunk-NMXYENH4.js → chunk-VZFJTKIW.js} +6 -6
- package/dist/{chunk-753VJIUP.js → chunk-X4ZU4QH5.js} +43 -9
- package/dist/chunk-X4ZU4QH5.js.map +1 -0
- package/dist/chunk-XD7SRDUY.js +188 -0
- package/dist/chunk-XD7SRDUY.js.map +1 -0
- package/dist/chunk-Z7I6SONO.js +825 -0
- package/dist/chunk-Z7I6SONO.js.map +1 -0
- package/dist/cli/quickdraw-docs.js +131 -113
- package/dist/cli/quickdraw-docs.js.map +1 -1
- package/dist/client/core.d.ts +387 -0
- package/dist/client/core.js +109 -0
- package/dist/client/core.js.map +1 -0
- package/dist/client/index.d.ts +30 -193
- package/dist/client/index.js +56 -217
- package/dist/client/index.js.map +1 -1
- package/dist/{clientTypes-hWcPUVS9.d.ts → clientTypes-CUUNJwWs.d.ts} +3 -3
- package/dist/{connection-CgijdGpT.d.ts → connection-2EeQ_Et0.d.ts} +16 -9
- package/dist/{createServer-Cp-Wk500.d.ts → createServer-Bx5HOcYK.d.ts} +37 -9
- package/dist/{createTestApp-BB_PkqQ7.d.ts → createTestApp-DAMANT79.d.ts} +36 -9
- package/dist/{envelope-DzWyIXjj.d.ts → envelope-BMBaxppV.d.ts} +200 -8
- package/dist/{http-DotQ5bj8.d.ts → http-YzLAar3w.d.ts} +15 -50
- package/dist/index.d.ts +7 -6
- package/dist/index.js +61 -28
- package/dist/index.js.map +1 -1
- package/dist/{infer-BmoZsL9a.d.ts → infer-Br62cnyW.d.ts} +39 -1
- package/dist/{metrics-DW80Exve.d.ts → metrics-CTVpw0gu.d.ts} +2 -2
- package/dist/parser.js +2 -1
- package/dist/prisma/index.d.ts +13 -6
- package/dist/prisma/index.js +155 -9
- package/dist/prisma/index.js.map +1 -1
- package/dist/remote-N7OUAGOW.js +15 -0
- package/dist/remote-N7OUAGOW.js.map +1 -0
- package/dist/server/auth/index.d.ts +3 -3
- package/dist/server/auth/index.js +5 -3
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/index.d.ts +140 -36
- package/dist/server/index.js +162 -55
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/index.d.ts +34 -18
- package/dist/server/mcp/index.js +29 -12
- package/dist/server/mcp/index.js.map +1 -1
- package/dist/server/otel.d.ts +3 -3
- package/dist/{sharing-UDLn1049.d.ts → sharing-BrsmlDv_.d.ts} +3 -3
- package/dist/testing/client.d.ts +13 -13
- package/dist/testing/client.js +17 -10
- package/dist/testing/client.js.map +1 -1
- package/dist/testing/index.d.ts +49 -12
- package/dist/testing/index.js +102 -19
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mock.d.ts +5 -5
- package/dist/testing/mock.js +15 -9
- package/dist/tracked-BR5b2O-U.d.ts +43 -0
- package/dist/{types-CYliTP65.d.ts → types-CLRCQGnO.d.ts} +4 -3
- package/dist/types-tMagHbjy.d.ts +151 -0
- package/dist/utils/index.d.ts +16 -4
- package/dist/utils/index.js +6 -3
- package/package.json +5 -1
- package/dist/chunk-4YO5H5ON.js.map +0 -1
- package/dist/chunk-5CGO426C.js.map +0 -1
- package/dist/chunk-6K5BY47X.js.map +0 -1
- package/dist/chunk-753VJIUP.js.map +0 -1
- package/dist/chunk-7QCUX4TK.js.map +0 -1
- package/dist/chunk-AONAFOYU.js.map +0 -1
- package/dist/chunk-B2P7AZ25.js.map +0 -1
- package/dist/chunk-GKH3LJ7N.js.map +0 -1
- package/dist/chunk-GSIPUNJL.js.map +0 -1
- package/dist/chunk-MBH6IDSM.js.map +0 -1
- package/dist/chunk-RJ5I2VO3.js.map +0 -1
- package/dist/chunk-X5CWPX7H.js.map +0 -1
- package/dist/chunk-Z2LZ7IZ7.js.map +0 -1
- /package/dist/{chunk-LCC7HNFP.js.map → chunk-RZXJTDH5.js.map} +0 -0
- /package/dist/{chunk-NMXYENH4.js.map → chunk-VZFJTKIW.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client/backoff.ts","../src/client/watch.ts","../src/client/lane.ts","../src/client/reload.ts","../src/client/retry.ts","../src/client/socket.ts","../src/client/connection.ts","../src/client/host/hosts.ts","../src/client/host/links.ts"],"sourcesContent":["// Rate-limit backoff per kind of work (RFC 0003 section 11.1). A `RATE_LIMITED`\n// answer pauses that kind of work for its `retryAfterMs` plus random jitter,\n// so clients told to wait the same time do not all retry at the same instant.\n// Ported from 4.1's provider (4.1 `src/client/QuickdrawProvider.tsx:269-287`),\n// which paused every read for one shared window. Here each kind has its own:\n// a full query queue says nothing about mutations, and subscription events run\n// in their own per-socket lane on the server (section 8.2).\n//\n// React-free: the connection owns one and puts its windows in its state.\n\nimport type { MethodKind } from \"../contract/methods\";\nimport type { QuickdrawError } from \"../protocol/errors\";\nimport { isRecord } from \"../protocol/guards\";\n\n/** The kinds of work that back off separately: method calls by kind, and subscription events. */\nexport type BackoffKind = MethodKind | \"subscription\";\n\n/** When each kind's backoff ends, in epoch milliseconds; a kind that is not backing off is absent. */\nexport type BackoffWindows = Readonly<Partial<Record<BackoffKind, number>>>;\n\n/** The wait when a `RATE_LIMITED` answer carries no `retryAfterMs`, as in 4.1. */\nexport const DEFAULT_BACKOFF_MS = 5000;\n\n/** The shortest and longest wait, before jitter. */\nconst MIN_BACKOFF_MS = 250;\nconst MAX_BACKOFF_MS = 5 * 60 * 1000;\n\n/** Jitter adds up to this share of the wait. */\nconst JITTER_RATIO = 0.5;\n\n/** The backoff windows of one connection. */\nexport interface Backoff {\n /** Starts or extends `kind`'s window: `retryAfterMs` (or the default) plus up to half again at random. */\n report(kind: BackoffKind, retryAfterMs?: number): void;\n /** How long `kind` still backs off, in milliseconds; 0 when it does not. */\n remaining(kind: BackoffKind): number;\n /** The windows in effect. */\n windows(): BackoffWindows;\n /** Ends every window and its timer. */\n clear(): void;\n}\n\n/** The `retryAfterMs` a `RATE_LIMITED` error carries in its data, if it carries one. */\nexport function retryAfterOf(error: QuickdrawError): number | undefined {\n const retryAfterMs: unknown = isRecord(error.data) ? error.data.retryAfterMs : undefined;\n return typeof retryAfterMs === \"number\" ? retryAfterMs : undefined;\n}\n\nfunction waitFor(retryAfterMs: number | undefined): number {\n const wait =\n typeof retryAfterMs === \"number\" && Number.isFinite(retryAfterMs)\n ? retryAfterMs\n : DEFAULT_BACKOFF_MS;\n return Math.min(Math.max(wait, MIN_BACKOFF_MS), MAX_BACKOFF_MS);\n}\n\n/**\n * Creates the backoff windows of one connection. `onChange` is called with\n * the windows whenever one starts, is extended or ends.\n */\nexport function createBackoff(onChange: (windows: BackoffWindows) => void): Backoff {\n const until = new Map<BackoffKind, number>();\n const timers = new Map<BackoffKind, ReturnType<typeof setTimeout>>();\n\n const windows = (): BackoffWindows => Object.freeze(Object.fromEntries(until));\n\n function end(kind: BackoffKind): void {\n timers.delete(kind);\n until.delete(kind);\n onChange(windows());\n }\n\n return Object.freeze({\n report(kind: BackoffKind, retryAfterMs?: number): void {\n const wait = waitFor(retryAfterMs);\n const delay = wait + Math.random() * wait * JITTER_RATIO;\n const ends = Date.now() + delay;\n if (ends <= (until.get(kind) ?? 0)) {\n return;\n }\n clearTimeout(timers.get(kind));\n until.set(kind, ends);\n timers.set(\n kind,\n setTimeout(() => {\n end(kind);\n }, delay),\n );\n onChange(windows());\n },\n remaining(kind: BackoffKind): number {\n const ends = until.get(kind);\n return ends === undefined ? 0 : Math.max(0, ends - Date.now());\n },\n windows,\n clear(): void {\n for (const timer of timers.values()) {\n clearTimeout(timer);\n }\n timers.clear();\n if (until.size > 0) {\n until.clear();\n onChange(windows());\n }\n },\n });\n}\n","// The change topics a client connection watches (RFC 0003 sections 8.2 and\n// 11.3). A query whose contract declares `watch` joins the topic of the\n// collection scope it reads; the server then sends `qd:changed { s, topic,\n// rev }` once per flush that changed the topic, with no data, and the query\n// is invalidated (`hooks.ts`, through the coordinator). This replaces 4.1's\n// `invalidateOn`, which listened for hand-written event names on every hook\n// (4.1 `src/client/useServiceQuery.ts:139-166`).\n//\n// - Watches are counted per topic across everything that watches it: the\n// first sends `qd:watch`, and the topic is left (`qd:unwatch`) a tick\n// after the last ends unless another starts first, so two components\n// reading one topic join it once, and a remount joins it once too.\n// - The server forgets a socket's topics when it disconnects, so every\n// connect sends `qd:watch` again for each topic still watched.\n// - `qd:watch` runs in the socket's subscription lane on the server, and goes\n// through the connection's lane here (`lane.ts`). A `RATE_LIMITED` answer\n// starts the connection's `subscription` backoff, and the topic is joined\n// again once it ends; no topic is joined while it lasts. Any other refusal\n// (`FORBIDDEN`, `NOT_FOUND`, ...) stands until the next connect: the query\n// still works, it is just not told of changes.\n// - One `qd:changed` listener serves every topic and routes by topic.\n// Watches that share a `key` are told once per frame between them.\n// - A read can wait for its topic's join (`waitForJoin`): while the socket\n// is connected and the topic's `qd:watch` is unanswered, the query hooks\n// hold a read until the server answers it, joined or refused, so the read\n// sees every change made before the join and a watched query is read once\n// on its first mount (RFC 0003 section 17).\n// - A watch is told once (`onJoined`) when its topic is first joined after\n// the watch started, unless its key's read waited for that join: a read\n// sent before then (on a socket that was not connected yet, or a result\n// prefetched before the watch) may have missed a change made before the\n// join, so the query hooks read once more. A topic is not joined from the\n// moment the socket drops: a read of a key sent while it is not joined (it\n// waited in Socket.IO's buffer through an outage, or went out while the\n// join waited out a backoff) reaches the server before the join, so the\n// watches of that key are told again when the join is acknowledged.\n// Otherwise joining again after a reconnect tells no one: the\n// coordinator's reconnect refetch covers the queries read before it.\n//\n// React-free: the connection owns one (`connection.watch`).\n\nimport { CLIENT_EVENTS, SERVER_EVENTS } from \"../contract/names\";\nimport type { ChangedFrame } from \"../protocol/envelope\";\nimport { fromWire } from \"../protocol/errors\";\nimport { isRecord } from \"../protocol/guards\";\nimport { DEFAULT_BACKOFF_MS, retryAfterOf, type BackoffKind } from \"./backoff\";\nimport type { QuickdrawSocket } from \"./connection\";\nimport type { SubscriptionLane } from \"./lane\";\n\n/** One watch of a change topic, as `connection.watch` takes it. */\nexport interface TopicWatch {\n /** The service's name on the wire: the contract's `name`. */\n readonly service: string;\n /**\n * The topic: `{collection}:{scope}` for one scope of a collection\n * (`collectionTopic`), or `\"service\"` for the service-wide topic, which the\n * server opens only to services that declare `watchAccess`.\n */\n readonly topic: string;\n /** Called with each `qd:changed` frame of the topic while the watch lasts. */\n readonly onChanged: (frame: ChangedFrame) => void;\n /**\n * On the `\"service\"` topic, the models this watch is about (`watch: {\n * service: [models] }`): a frame whose `models` names none of them is not\n * passed to `onChanged`. A frame without `models` (an older server, or\n * the last frame of a watch the socket lost) is always passed.\n */\n readonly models?: readonly string[];\n /**\n * Called when the server first acknowledges `qd:watch` for the topic after\n * this watch started, and again when a later join (after a reconnect) is\n * acknowledged after a read of this watch's `key` went out while the topic\n * was not joined: that read may have missed a change made before the join.\n * Not called when the topic was joined already, nor when a read of the\n * `key` waited for that acknowledgement (`waitForJoin`).\n */\n readonly onJoined?: () => void;\n /**\n * Watches of one topic that share a key are told of each frame once\n * between them. The query hooks pass their query key's hash, so two\n * components reading one query cause one invalidation.\n */\n readonly key?: string;\n}\n\n/** What the topics need of their connection. */\nexport interface TopicHost {\n readonly socket: QuickdrawSocket;\n /** The connection's lane, which `qd:watch` goes through. */\n readonly lane: SubscriptionLane;\n backoffRemaining(kind: BackoffKind): number;\n reportRateLimited(kind: BackoffKind, retryAfterMs?: number): void;\n}\n\n/** A topic a read waits for, as `waitForJoin` takes it. */\nexport type JoinWait = Pick<TopicWatch, \"service\" | \"topic\" | \"key\">;\n\n/** The topics of one connection. */\nexport interface Topics {\n /** Starts a watch; returns the function that ends it. Ending it twice does nothing. */\n watch(watch: TopicWatch): () => void;\n /**\n * While the socket is connected and the topic's `qd:watch` is unanswered,\n * a promise that resolves once it is answered (joined or refused), fails,\n * or the topic is left or the connection closes; `undefined` otherwise,\n * when there is nothing to wait for. The watches with `key` are not told\n * `onJoined` for that join: the read that waited is sent after it.\n */\n waitForJoin(wait: JoinWait): Promise<void> | undefined;\n /** Joins every watched topic again: the socket has just connected. */\n rejoin(): void;\n /** Stops waiting to retry joins: the connection closed. The next connect joins again. */\n stop(): void;\n}\n\n/**\n * Where a topic stands with the server: `waiting` to be sent (not connected,\n * or backing off), `joining` until `qd:watch` is answered, `joined`, or\n * `refused` until the next connect.\n */\ntype JoinState = \"waiting\" | \"joining\" | \"joined\" | \"refused\";\n\n/** One `watch` call: the watch, and whether it was told its topic was joined. */\ninterface WatchRecord {\n readonly watch: TopicWatch;\n told: boolean;\n}\n\n/** One read waiting for a topic's join (`waitForJoin`): its key, and what lets it go. */\ninterface Hold {\n readonly key: string | undefined;\n readonly release: () => void;\n}\n\ninterface Topic {\n readonly id: string;\n readonly frame: { readonly s: string; readonly topic: string };\n /** One record per `watch` call, so the same watch object may be started twice. */\n readonly watches: Set<WatchRecord>;\n /** The reads waiting for the `qd:watch` in flight to be answered. */\n readonly holds: Set<Hold>;\n /** The keys whose reads went out while the topic was not joined, owed a telling at the next join. */\n readonly unjoinedReads: Set<string>;\n state: JoinState;\n retry: ReturnType<typeof setTimeout> | undefined;\n /**\n * Set when the last watch ended: the topic is left on the next tick\n * unless a watch starts first, so a component that remounts (React's\n * strict mode) or changes its input within one scope keeps its topic\n * instead of leaving and joining it again.\n */\n leaving: ReturnType<typeof setTimeout> | undefined;\n /** Raised by every join and by leaving, so the answer to an earlier `qd:watch` is ignored. */\n attempt: number;\n}\n\n/** The topics of one connection, by `topicId`. */\ninterface Registry {\n readonly host: TopicHost;\n readonly topics: Map<string, Topic>;\n}\n\nfunction topicId(service: string, topic: string): string {\n return `${service}\\u0000${topic}`;\n}\n\n/** Throws `error` from a microtask of its own, where it is reported without unwinding the caller. */\nfunction rethrowLater(error: unknown): void {\n queueMicrotask(() => {\n throw error;\n });\n}\n\n/** Runs `notify` for each listener, so one that throws neither stops the others nor the socket. */\nexport function notifyEach<T>(listeners: Iterable<T>, notify: (listener: T) => void): void {\n for (const listener of [...listeners]) {\n try {\n notify(listener);\n } catch (error) {\n rethrowLater(error);\n }\n }\n}\n\n/** `records` with one per key: a watch without a key is always kept. */\nfunction oncePerKey(records: Iterable<WatchRecord>): WatchRecord[] {\n const keys = new Set<string>();\n return [...records].filter(({ watch }) => {\n if (watch.key === undefined) {\n return true;\n }\n const first = !keys.has(watch.key);\n keys.add(watch.key);\n return first;\n });\n}\n\n/** The first letter lowercased, as the server names models (`modelKey`). */\nfunction modelName(model: string): string {\n return `${model.charAt(0).toLowerCase()}${model.slice(1)}`;\n}\n\n/** Whether `frame` is about a model `watch` watches: always, unless both name models and share none. */\nfunction concerns(watch: TopicWatch, frame: ChangedFrame): boolean {\n const { models } = watch;\n if (models === undefined || !Array.isArray(frame.models)) {\n return true;\n }\n const changed = new Set(frame.models.map((model) => modelName(String(model))));\n return models.some((model) => changed.has(modelName(model)));\n}\n\n/** Tells each watch of `topic` that `frame` concerns, once per key. */\nfunction tell(topic: Topic, frame: ChangedFrame): void {\n const concerned = [...topic.watches].filter(({ watch }) => concerns(watch, frame));\n notifyEach(oncePerKey(concerned), ({ watch }) => {\n watch.onChanged(frame);\n });\n}\n\n/**\n * Tells the watches of `topic` that it is joined, once per key: those not\n * told yet, and those whose key's read went out while it was not joined;\n * except those whose key's read waited for this join, which is sent after\n * it.\n */\nfunction tellJoined(topic: Topic): void {\n const waited = new Set([...topic.holds].map((hold) => hold.key));\n const unjoined = new Set(topic.unjoinedReads);\n topic.unjoinedReads.clear();\n const owed = [...topic.watches].filter(\n ({ watch, told }) => !told || (watch.key !== undefined && unjoined.has(watch.key)),\n );\n for (const record of owed) {\n record.told = true;\n }\n const toTell = owed.filter(({ watch }) => watch.key === undefined || !waited.has(watch.key));\n notifyEach(oncePerKey(toTell), ({ watch }) => {\n watch.onJoined?.();\n });\n}\n\n/**\n * Lets every read waiting for `topic`'s join go: it was answered, failed, or\n * will not be sent. Unless the topic is joined now, those reads go out\n * before its join.\n */\nfunction releaseHolds(topic: Topic): void {\n const holds = [...topic.holds];\n topic.holds.clear();\n for (const hold of holds) {\n if (topic.state !== \"joined\" && hold.key !== undefined) {\n topic.unjoinedReads.add(hold.key);\n }\n hold.release();\n }\n}\n\nfunction isChangedFrame(value: unknown): value is ChangedFrame {\n return isRecord(value) && typeof value.s === \"string\" && typeof value.topic === \"string\";\n}\n\n/** Joins `topic` again after `delayMs`, unless it is left first. */\nfunction retryIn(registry: Registry, topic: Topic, delayMs: number): void {\n clearTimeout(topic.retry);\n topic.state = \"waiting\";\n topic.retry = setTimeout(() => {\n topic.retry = undefined;\n if (registry.topics.get(topic.id) === topic) {\n join(registry, topic);\n }\n }, delayMs);\n}\n\n/** Settles a topic by a refusal of its `qd:watch`: waits out `RATE_LIMITED`, else stands refused. */\nfunction refused(registry: Registry, topic: Topic, reply: unknown): void {\n const { host } = registry;\n const failure = fromWire(isRecord(reply) ? reply.e : undefined);\n if (failure.code === \"RATE_LIMITED\") {\n host.reportRateLimited(\"subscription\", retryAfterOf(failure));\n retryIn(registry, topic, host.backoffRemaining(\"subscription\"));\n } else {\n topic.state = \"refused\";\n }\n}\n\n/**\n * Settles a topic by the answer to its `qd:watch`, or by the error that came\n * instead, and lets the reads that waited for it go: joined, they see every\n * change made before the join; otherwise they read anyway.\n */\nfunction answered(registry: Registry, topic: Topic, error: Error | null, reply: unknown): void {\n if (error !== null) {\n // No answer: a dropped connection joins again on its next connect.\n if (registry.host.socket.connected) {\n retryIn(registry, topic, DEFAULT_BACKOFF_MS);\n } else {\n topic.state = \"waiting\";\n }\n } else if (isRecord(reply) && reply.ok === true) {\n topic.state = \"joined\";\n tellJoined(topic);\n } else {\n refused(registry, topic, reply);\n }\n releaseHolds(topic);\n}\n\n/**\n * Sends `qd:watch` for `topic` when the socket is connected and\n * subscriptions are not backing off. Reads waiting for an earlier attempt\n * wait for this one; when none is sent, they go.\n */\nfunction join(registry: Registry, topic: Topic): void {\n const { host } = registry;\n clearTimeout(topic.retry);\n topic.retry = undefined;\n topic.attempt += 1;\n const wait = host.backoffRemaining(\"subscription\");\n if (!host.socket.connected) {\n topic.state = \"waiting\";\n releaseHolds(topic);\n } else if (wait > 0) {\n retryIn(registry, topic, wait);\n releaseHolds(topic);\n } else {\n topic.state = \"joining\";\n const { attempt } = topic;\n host.lane.send(CLIENT_EVENTS.watch, topic.frame, (error, reply) => {\n if (registry.topics.get(topic.id) === topic && topic.attempt === attempt) {\n answered(registry, topic, error, reply);\n }\n });\n }\n}\n\n/** Forgets `topic`, and sends `qd:unwatch` when the server may have joined it. */\nfunction leave(registry: Registry, topic: Topic): void {\n registry.topics.delete(topic.id);\n clearTimeout(topic.retry);\n clearTimeout(topic.leaving);\n topic.leaving = undefined;\n topic.attempt += 1;\n releaseHolds(topic);\n const sent = topic.state === \"joining\" || topic.state === \"joined\";\n if (sent && registry.host.socket.connected) {\n registry.host.socket.emit(CLIENT_EVENTS.unwatch, topic.frame);\n }\n}\n\n/**\n * A promise that resolves once `topic`'s `qd:watch` in flight is answered;\n * see `Topics.waitForJoin`. A read that does not wait while the topic is not\n * joined goes out before its join, and is remembered for that join.\n */\nfunction waitForJoin(registry: Registry, wait: JoinWait): Promise<void> | undefined {\n const topic = registry.topics.get(topicId(wait.service, wait.topic));\n if (topic === undefined) {\n return undefined;\n }\n if (topic.state === \"joining\" && registry.host.socket.connected) {\n return new Promise<void>((resolve) => {\n topic.holds.add({ key: wait.key, release: resolve });\n });\n }\n if (topic.state !== \"joined\" && wait.key !== undefined) {\n topic.unjoinedReads.add(wait.key);\n }\n return undefined;\n}\n\nfunction startWatch(registry: Registry, watch: TopicWatch): () => void {\n const id = topicId(watch.service, watch.topic);\n const topic = registry.topics.get(id) ?? {\n id,\n frame: Object.freeze({ s: watch.service, topic: watch.topic }),\n watches: new Set(),\n holds: new Set(),\n unjoinedReads: new Set(),\n state: \"waiting\",\n retry: undefined,\n leaving: undefined,\n attempt: 0,\n };\n registry.topics.set(id, topic);\n const kept = topic.leaving !== undefined;\n clearTimeout(topic.leaving);\n topic.leaving = undefined;\n // A topic joined already needs no telling: this watch's reads come after the join.\n const record: WatchRecord = { watch, told: topic.state === \"joined\" };\n topic.watches.add(record);\n if (topic.watches.size === 1 && !kept) {\n join(registry, topic);\n }\n return () => {\n if (topic.watches.delete(record) && topic.watches.size === 0) {\n topic.leaving = setTimeout(() => {\n if (topic.watches.size === 0 && registry.topics.get(topic.id) === topic) {\n leave(registry, topic);\n }\n }, 0);\n }\n };\n}\n\n/** Creates the topics of a connection and listens for `qd:changed` on its socket. */\nexport function createTopics(host: TopicHost): Topics {\n const registry: Registry = { host, topics: new Map() };\n host.socket.on(SERVER_EVENTS.changed, (frame: unknown) => {\n const topic = isChangedFrame(frame)\n ? registry.topics.get(topicId(frame.s, frame.topic))\n : undefined;\n if (topic !== undefined) {\n tell(topic, frame as ChangedFrame);\n }\n });\n host.socket.on(\"disconnect\", () => {\n // The server forgot the socket's topics with it.\n for (const topic of registry.topics.values()) {\n if (topic.state === \"joined\") {\n topic.state = \"waiting\";\n }\n }\n });\n return Object.freeze({\n watch: (watch: TopicWatch) => startWatch(registry, watch),\n waitForJoin: (wait: JoinWait) => waitForJoin(registry, wait),\n rejoin(): void {\n for (const topic of [...registry.topics.values()]) {\n if (topic.watches.size === 0) {\n leave(registry, topic);\n } else {\n join(registry, topic);\n }\n }\n },\n stop(): void {\n for (const topic of [...registry.topics.values()]) {\n if (topic.watches.size === 0) {\n leave(registry, topic);\n } else {\n clearTimeout(topic.retry);\n topic.retry = undefined;\n topic.attempt += 1;\n topic.state = \"waiting\";\n releaseHolds(topic);\n }\n }\n },\n });\n}\n","// The client's lane of subscription events (RFC 0003 section 8.2). The server\n// runs `qd:sub`, `qd:col:sub`, `qd:col:items`, `qd:watch` and\n// `qd:stream:sub` in a lane per socket:\n// `limits.subscriptions.maxInFlight` (8) at once and `maxQueued` (64)\n// waiting, then `RATE_LIMITED`. A page that mounts a board can ask for more\n// than that in one tick, so the client keeps a lane of its own in front of it:\n//\n// - at most `maxInFlight` events (from the server's `qd:hello`, 8 before it\n// arrives) await their acknowledgement; the others wait here, in order, so\n// the server's queue never fills;\n// - nothing is sent while the connection's `subscription` backoff lasts\n// (after a `RATE_LIMITED` answer): the lane waits it out, then goes on;\n// - nothing is sent while the socket is down: an event whose turn comes then\n// is answered at once with an error, and its sender subscribes again when\n// the socket connects (the server forgets a socket's subscriptions anyway);\n// closing the connection answers every waiting event that way at once, and\n// leaves no timer behind.\n//\n// Unsubscribe events (`qd:unsub`, `qd:col:unsub`, `qd:unwatch`,\n// `qd:stream:unsub`) are not in the server's lane, and are sent directly.\n//\n// React-free: the connection owns one; the change topics (`watch.ts`) and the\n// live data (`live/`) send through it.\n\nimport type { CLIENT_EVENTS } from \"../contract/names\";\nimport { isRecord } from \"../protocol/guards\";\nimport { MAX_SUBSCRIBE_IDS, type HelloFrame } from \"../protocol/version\";\nimport type { BackoffKind } from \"./backoff\";\nimport type { QuickdrawSocket } from \"./connection\";\nimport { notifyEach } from \"./watch\";\n\n/** How many subscription events a lane sends at once before the server's `qd:hello` says. */\nexport const DEFAULT_SUBSCRIPTION_LANE = 8;\n\n/** The events that run in the server's subscription lane. */\nexport type SubscriptionEvent =\n | typeof CLIENT_EVENTS.sub\n | typeof CLIENT_EVENTS.collectionSub\n | typeof CLIENT_EVENTS.collectionItems\n | typeof CLIENT_EVENTS.watch\n | typeof CLIENT_EVENTS.streamSub;\n\n/**\n * Receives what came back for one subscription event: an error (no answer\n * in time, the socket dropped, or it was down when the event's turn came) or\n * the server's reply.\n */\nexport type LaneCallback = (error: Error | null, reply: unknown) => void;\n\n/** What a lane needs of its connection. */\nexport interface LaneHost {\n readonly socket: QuickdrawSocket;\n /** How long to wait for an acknowledgement. */\n timeoutMs(): number;\n /** The server's `qd:hello` on the current credentials, or `null` before it arrives. */\n hello(): HelloFrame | null;\n backoffRemaining(kind: BackoffKind): number;\n}\n\n/** A connection's lane of subscription events. */\nexport interface SubscriptionLane {\n /**\n * Sends `event` with `frame` once a slot is free, and calls `done` with\n * what comes back. `done` is called exactly once, and never synchronously\n * from `send` unless the socket is down.\n */\n send(event: SubscriptionEvent, frame: object, done: LaneCallback): void;\n /** Events sent and not answered yet. */\n inFlight(): number;\n /** Events waiting for a slot. */\n waiting(): number;\n /**\n * The connection closed: answers every event waiting for a slot as if the\n * socket were down (their senders subscribe again on the next connect),\n * and stops waiting out a backoff.\n */\n stop(): void;\n}\n\n/** The subscription limits a server announced, checked: they came over the network. */\nexport interface SubscriptionLimits {\n /** Subscription events one socket may have running at once. */\n readonly maxInFlight: number;\n /** Ids one `qd:sub` may name. */\n readonly maxSubscribeIds: number;\n}\n\nfunction positiveInteger(value: unknown, fallback: number): number {\n return typeof value === \"number\" && Number.isSafeInteger(value) && value > 0 ? value : fallback;\n}\n\n/** The limits `hello` announces, or the defaults where it says nothing usable. */\nexport function subscriptionLimits(hello: HelloFrame | null): SubscriptionLimits {\n const limits: unknown = hello?.limits;\n const record = isRecord(limits) ? limits : {};\n const lane = isRecord(record.subscriptions) ? record.subscriptions : {};\n return {\n maxInFlight: positiveInteger(lane.maxInFlight, DEFAULT_SUBSCRIPTION_LANE),\n maxSubscribeIds: positiveInteger(record.maxSubscribeIds, MAX_SUBSCRIBE_IDS),\n };\n}\n\ninterface Pending {\n readonly event: SubscriptionEvent;\n readonly frame: object;\n readonly done: LaneCallback;\n}\n\n/** The untyped face of a socket's `timeout(ms)`: the lane sends four event types through one call. */\ninterface TimedEmitter {\n emit(event: string, frame: object, ack: LaneCallback): void;\n}\n\n/** The error a sender gets for an event the lane did not send because the socket was down. */\nfunction notConnected(): Error {\n return new Error(\"The socket is not connected; the event was not sent\");\n}\n\n/** Creates the subscription lane of one connection. */\nexport function createSubscriptionLane(host: LaneHost): SubscriptionLane {\n const queue: Pending[] = [];\n let inFlight = 0;\n let backoffTimer: ReturnType<typeof setTimeout> | undefined;\n\n const answer = (pending: Pending, error: Error | null, reply: unknown): void => {\n notifyEach([pending.done], (done) => {\n done(error, reply);\n });\n };\n\n function sendOne(pending: Pending): void {\n inFlight += 1;\n const emitter = host.socket.timeout(host.timeoutMs()) as unknown as TimedEmitter;\n emitter.emit(pending.event, pending.frame, (error, reply) => {\n inFlight -= 1;\n answer(pending, error, reply);\n pump();\n });\n }\n\n function waitForBackoff(wait: number): void {\n backoffTimer ??= setTimeout(() => {\n backoffTimer = undefined;\n pump();\n }, wait);\n }\n\n function dropAll(): void {\n for (const dropped of queue.splice(0)) {\n answer(dropped, notConnected(), undefined);\n }\n }\n\n function pump(): void {\n const { maxInFlight } = subscriptionLimits(host.hello());\n while (queue.length > 0 && inFlight < maxInFlight) {\n if (!host.socket.connected) {\n dropAll();\n return;\n }\n const wait = host.backoffRemaining(\"subscription\");\n if (wait > 0) {\n waitForBackoff(wait);\n return;\n }\n sendOne(queue.shift() as Pending);\n }\n }\n\n return Object.freeze({\n send(event: SubscriptionEvent, frame: object, done: LaneCallback): void {\n queue.push({ event, frame, done });\n pump();\n },\n inFlight: () => inFlight,\n waiting: () => queue.length,\n stop(): void {\n clearTimeout(backoffTimer);\n backoffTimer = undefined;\n dropAll();\n },\n });\n}\n","// What a client does by default when the server speaks another protocol\n// (RFC 0003 section 8.1): reload the page once per browser session, so a tab\n// left open across a deploy picks up the client that matches the server. A\n// second mismatch in the same session (the new bundle still speaks the old\n// protocol) does not reload again, which would loop.\n//\n// Without a DOM (React Native, Node) there is nothing to reload and this does\n// nothing; such apps pass their own `onProtocolMismatch`. Without\n// `sessionStorage` it does nothing either, since it could not stop a loop.\n\nimport type { ProtocolMismatch } from \"../protocol/version\";\n\n/** The `sessionStorage` key that records a reload for one server protocol. */\nexport const RELOAD_KEY_PREFIX = \"quickdraw:protocol-reload:\";\n\ninterface BrowserScope {\n readonly location?: { reload?: unknown };\n readonly sessionStorage?: Pick<Storage, \"getItem\" | \"setItem\">;\n}\n\n/** Records the reload in `sessionStorage`; false when one was recorded already or storage fails. */\nfunction firstReload(scope: BrowserScope, key: string): boolean {\n try {\n const storage = scope.sessionStorage;\n if (storage === undefined || storage.getItem(key) !== null) {\n return false;\n }\n storage.setItem(key, String(Date.now()));\n return true;\n } catch {\n return false;\n }\n}\n\n/** The default `onProtocolMismatch`: reloads the page once per session and server protocol. */\nexport function reloadOncePerSession(mismatch: ProtocolMismatch): void {\n const scope = globalThis as BrowserScope;\n const location = scope.location;\n if (typeof location?.reload !== \"function\") {\n return;\n }\n if (firstReload(scope, `${RELOAD_KEY_PREFIX}${mismatch.expected}`)) {\n (location as { reload(): void }).reload();\n }\n}\n","// Connecting again after the server could not authenticate the connection\n// (RFC 0003 section 8.1): a handshake refused `INTERNAL` (its `authenticate`\n// failed with anything but a refusal: its session store down, say). Socket.IO\n// does not retry a handshake its middleware refused (the socket is left\n// inactive), so the connection does, waiting what Socket.IO's manager waits\n// between reconnection attempts: its `reconnectionDelay`, doubling with each\n// attempt in a row, with `randomizationFactor`'s jitter (Socket.IO's\n// `backo2` formula), at most `reconnectionDelayMax`, as often as\n// `reconnectionAttempts` allows. An app tunes them with `socketOptions`.\n//\n// React-free: each connection owns one (`connection.ts`).\n\nimport type { QuickdrawSocket } from \"./socket\";\n\n/** The most doublings a wait takes; `reconnectionDelayMax` caps it long before. */\nconst MAX_DOUBLINGS = 20;\n\n/** The wait before attempt `attempt` (from 0) to connect again, in milliseconds. */\nexport function retryDelay(socket: QuickdrawSocket, attempt: number): number {\n const manager = socket.io;\n const wait = manager.reconnectionDelay() * 2 ** Math.min(attempt, MAX_DOUBLINGS);\n const deviation = Math.floor(Math.random() * manager.randomizationFactor() * wait);\n const jittered = Math.random() < 0.5 ? wait - deviation : wait + deviation;\n return Math.min(jittered, manager.reconnectionDelayMax());\n}\n\n/** The retries of one connection's socket after the server could not authenticate it. */\nexport interface Retry {\n /**\n * Connects the socket again after the next wait. False, and nothing\n * scheduled, when the connection is not trying to connect, Socket.IO's\n * reconnection is off, or its attempts are spent.\n */\n later(): boolean;\n /** Cancels a scheduled attempt, and the next refusal waits from the start again. */\n stop(): void;\n /** The socket connected: the next refusal waits from the start again. */\n connected(): void;\n}\n\n/** Creates the retries of `socket`, which run only while `wanted()`: the connection is trying to connect. */\nexport function createRetry(socket: QuickdrawSocket, wanted: () => boolean): Retry {\n let timer: ReturnType<typeof setTimeout> | undefined;\n let attempts = 0;\n return Object.freeze({\n later(): boolean {\n const manager = socket.io;\n if (!wanted() || !manager.reconnection() || attempts >= manager.reconnectionAttempts()) {\n return false;\n }\n clearTimeout(timer);\n timer = setTimeout(\n () => {\n timer = undefined;\n if (wanted() && !socket.connected && !socket.active) {\n socket.connect();\n }\n },\n retryDelay(socket, attempts),\n );\n attempts += 1;\n return true;\n },\n stop(): void {\n clearTimeout(timer);\n timer = undefined;\n attempts = 0;\n },\n connected(): void {\n attempts = 0;\n },\n });\n}\n","// The Socket.IO socket of a client connection (RFC 0003 sections 8.1 and\n// 8.4): created unconnected with the connection's options, typed with the v5\n// frames, encoding with the JSON-only parser unless the server sends binary,\n// and sending the v5 handshake beside the app's credentials on every\n// connect. Split from `connection.ts`, which owns the socket's state.\n//\n// React-free.\n\nimport { io, type ManagerOptions, type Socket, type SocketOptions } from \"socket.io-client\";\nimport type { ClientToServerEvents, ServerToClientEvents } from \"../protocol/envelope\";\nimport { createJsonParser } from \"../protocol/parser\";\nimport { PROTOCOL_VERSION } from \"../protocol/version\";\nimport { QUICKDRAW_VERSION } from \"../version\";\n\n/** The client's Socket.IO socket, typed with the v5 frames. */\nexport type QuickdrawSocket = Socket<ServerToClientEvents, ClientToServerEvents>;\n\n/** Socket.IO client options: `io(url, options)`. */\nexport type SocketClientOptions = Partial<ManagerOptions & SocketOptions>;\n\n/** The connection options the socket is made from. */\nexport interface SocketSettings {\n readonly url: string;\n readonly socketOptions?: SocketClientOptions;\n readonly transports?: SocketClientOptions[\"transports\"];\n readonly binary?: boolean;\n}\n\n/**\n * Creates the socket, unconnected. `credentials` is read on every connect,\n * so new credentials take effect on the next one; the handshake adds\n * `qd: { protocol: 5, client }` to them.\n */\nexport function createSocket(\n settings: SocketSettings,\n credentials: () => Readonly<Record<string, unknown>>,\n): QuickdrawSocket {\n return io(settings.url, {\n forceNew: true,\n withCredentials: true,\n transports: [\"websocket\", \"polling\"],\n ...(settings.binary === true ? {} : { parser: createJsonParser() }),\n ...settings.socketOptions,\n ...(settings.transports === undefined ? {} : { transports: settings.transports }),\n autoConnect: false,\n auth: (send: (data: object) => void) => {\n send({ ...credentials(), qd: { protocol: PROTOCOL_VERSION, client: QUICKDRAW_VERSION } });\n },\n });\n}\n","// The connection of a v5 client (RFC 0003 sections 8.1, 11.1 and 11.3): one\n// Socket.IO socket with the v5 handshake, its state as a snapshot to\n// subscribe to, the pushes a server sends a whole connection, and the change\n// topics it watches. Ported from 4.1's provider\n// (4.1 `src/client/QuickdrawProvider.tsx:295-403`), without React, so a\n// React Native or Node client uses it as it is: nothing here touches\n// `window` or `document`.\n//\n// - The handshake sends `auth.qd = { protocol: 5, client }` beside the app's\n// credentials, and keeps the server's `qd:hello`: who the socket acts for,\n// its grants, and the limits. Once it is known, a call waits the server's\n// `callTimeoutMs` plus 2 s by default, so a slow call ends with the\n// server's `TIMEOUT` rather than the client's.\n// - A refused handshake says why in its `connect_error` data: another\n// protocol (`PROTOCOL_MISMATCH`, which calls `onProtocolMismatch`), refused\n// credentials (`UNAUTHENTICATED`), or credentials the server could not\n// check (`INTERNAL`: its `authenticate` failed with anything but a\n// refusal, its session store down, say). Socket.IO retries none of them;\n// the connection retries the last itself, with backoff (below).\n// - A session the server ends is signed out (the 5.1 audit's CLIENT-1): the\n// server's disconnect (`io server disconnect`: signed out everywhere,\n// revoked, expired, or a flooded channel) and a connect or reconnect\n// refused with `UNAUTHENTICATED` or `FORBIDDEN` (its `authenticate`\n// refused the credentials, or the handshake was forbidden) forget the\n// hello and the grants, and such a refusal fails the calls waiting in the\n// send buffer with `CANCELLED`; the cache session empties every cache,\n// data a server render prefetched included (`session.ts`). A dropped\n// transport, a `qd:rotate`, another protocol or an `INTERNAL` refusal\n// keeps everything.\n// - A connect or reconnect refused with `INTERNAL` stays `connecting` (and\n// `reconnecting`, after a connect on these credentials) and connects again\n// after Socket.IO's own reconnection delays (its manager's\n// `reconnectionDelay`, doubling to `reconnectionDelayMax`, with\n// `randomizationFactor`'s jitter), as often as its `reconnectionAttempts`\n// allow: a short outage of the server's session store signs nobody out.\n// With Socket.IO's reconnection off, or its attempts spent, it is\n// `refused` with that refusal, and still keeps the user.\n// - `qd:rotate` reconnects at a random moment within its window; `qd:access`\n// updates the grants in the state.\n// - `RATE_LIMITED` answers start a backoff window per kind (`backoff.ts`).\n// - A connection that drops and comes back with the same credentials (or\n// rotates) is `reconnecting` meanwhile, joins its watched topics again\n// (`watch.ts`) and tells `onReconnect` listeners, which refetch what may\n// have changed (the provider, through the coordinator).\n// - Every `qd:hello` stands for a new socket, which is in no app room until\n// a call joins it: `onHello` listeners hear each one (and the current one\n// when they start listening), to run their joining calls again\n// (`useJoin`).\n// - New credentials (`setAuth`) fail the calls waiting in Socket.IO's send\n// buffer with `CANCELLED` before reconnecting: Socket.IO would send them\n// on the next connect, as the new user.\n// - Subscription events (`qd:sub`, `qd:col:sub`, `qd:col:items`,\n// `qd:watch`) go through the connection's lane (`lane.ts`), paced by the\n// server's `limits.subscriptions`.\n//\n// 4.1 created a socket per token and hard-coded its options\n// (4.1 `src/client/QuickdrawProvider.tsx:306-312`), and cleared every\n// subscription on a disconnect (`:326-330`). Here one socket lives as long as\n// the connection (`socket.ts`): new credentials reconnect it, and\n// `socketOptions` pass through to `io()`. Caches are kept across a disconnect\n// the client did not cause the session's end with: a dropped transport or a\n// rotation; a session the server ended empties them.\n\nimport { SERVER_EVENTS } from \"../contract/names\";\nimport { QuickdrawError } from \"../protocol/errors\";\nimport { isRecord } from \"../protocol/guards\";\nimport {\n isAuthenticationRefused,\n isAuthenticationUnavailable,\n isProtocolMismatch,\n type AuthenticationRefused,\n type AuthenticationUnavailable,\n type HelloFrame,\n type ProtocolMismatch,\n} from \"../protocol/version\";\nimport { createBackoff, type Backoff, type BackoffKind, type BackoffWindows } from \"./backoff\";\nimport { createSubscriptionLane, type SubscriptionLane } from \"./lane\";\nimport { reloadOncePerSession } from \"./reload\";\nimport { createRetry, type Retry } from \"./retry\";\nimport { createSocket, type QuickdrawSocket, type SocketClientOptions } from \"./socket\";\nimport { createTopics, notifyEach, type JoinWait, type Topics, type TopicWatch } from \"./watch\";\n\nexport type { QuickdrawSocket, SocketClientOptions };\n\n/**\n * The credentials a connection authenticates with: a token, sent as\n * `auth.token`; an object of handshake fields, sent as they are; or nothing,\n * for an anonymous connection (or one a session cookie authenticates).\n */\nexport type ConnectionAuth = string | Readonly<Record<string, unknown>> | null | undefined;\n\n/** Options of {@link createQuickdrawConnection}. */\nexport interface QuickdrawConnectionOptions {\n /** The server's URL: `\"https://api.example.com\"`. */\n readonly url: string;\n /** The credentials to connect with. `setAuth` changes them. */\n readonly auth?: ConnectionAuth;\n /**\n * Socket.IO manager and socket options, passed to `io()` over the defaults\n * (`withCredentials: true`, `forceNew: true`). The connection sets `auth`\n * and `autoConnect` itself.\n */\n readonly socketOptions?: SocketClientOptions;\n /**\n * The transports to try, in order. Default `[\"websocket\", \"polling\"]`;\n * React Native clients pass `[\"websocket\"]`.\n */\n readonly transports?: SocketClientOptions[\"transports\"];\n /**\n * `true` when the server was created with `binary: true`. By default the\n * client encodes with the JSON-only parser, as the server does (section 8.4).\n */\n readonly binary?: boolean;\n /**\n * How long a call waits for its answer before it fails with `TIMEOUT`.\n * Default: the server's `callTimeoutMs` plus 2,000 ms once its hello has\n * arrived, and 10,000 ms before.\n */\n readonly timeoutMs?: number;\n /**\n * Called when the server refuses the connection because it speaks another\n * protocol. Default: reload the page once per browser session; nothing\n * without a DOM.\n */\n readonly onProtocolMismatch?: (mismatch: ProtocolMismatch) => void;\n}\n\n/**\n * Where a connection is:\n *\n * - `idle`: not opened, or closed by the app;\n * - `connecting`: opening, or reopening after it was lost (Socket.IO\n * retries) or after the server could not authenticate it (`INTERNAL`:\n * the connection retries, with backoff);\n * - `connected`;\n * - `disconnected`: the server ended it, and it does not retry;\n * - `refused`: the server refused the handshake (see `refusal`).\n */\nexport type ConnectionStatus = \"idle\" | \"connecting\" | \"connected\" | \"disconnected\" | \"refused\";\n\n/**\n * Why the server refused a handshake: another protocol, refused\n * credentials, credentials it could not check (`INTERNAL`, only once the\n * connection stopped retrying), or something else.\n */\nexport type ConnectionRefusal =\n | ProtocolMismatch\n | AuthenticationRefused\n | AuthenticationUnavailable\n | { readonly code: \"REFUSED\"; readonly message: string };\n\n/** A snapshot of a connection's state. A new object whenever anything in it changes. */\nexport interface ConnectionState {\n readonly status: ConnectionStatus;\n /**\n * True while the connection is `connecting` again after it was connected\n * with its current credentials: the socket dropped, the server asked it to\n * rotate, or the server could not authenticate a reconnect (`INTERNAL`).\n * Queries keep running meanwhile (their calls wait in the send buffer);\n * `setAuth` and `close` end it.\n */\n readonly reconnecting: boolean;\n /**\n * The server's `qd:hello` on the current credentials, or `null` before it\n * arrives, and again once the server ended the session (its disconnect, or\n * a connect or reconnect refused with `UNAUTHENTICATED` or `FORBIDDEN`).\n */\n readonly hello: HelloFrame | null;\n /**\n * The principal's service grants: from the server's `qd:hello`, then from\n * every `qd:access` push; `null` before the hello on the current\n * credentials arrives, and once the server ended the session.\n */\n readonly serviceAccess: HelloFrame[\"serviceAccess\"] | null;\n /** Why the last handshake was refused, while `status` is `refused`. */\n readonly refusal: ConnectionRefusal | null;\n /** When each kind's rate-limit backoff ends; a kind that is not backing off is absent. */\n readonly backoff: BackoffWindows;\n}\n\n/** A v5 client connection. */\nexport interface QuickdrawConnection {\n readonly url: string;\n /**\n * The default time limit of a call, in milliseconds: the `timeoutMs`\n * option when it was given; otherwise the server's `callTimeoutMs` plus\n * 2,000 once its hello has arrived, and 10,000 before.\n */\n readonly timeoutMs: number;\n /** The socket, created unconnected with the connection and kept for its lifetime. */\n readonly socket: QuickdrawSocket;\n /**\n * The lane every subscription event goes through (`qd:sub`, `qd:col:sub`,\n * `qd:col:items`, `qd:watch`): at most the server's\n * `limits.subscriptions.maxInFlight` of them unanswered at once, none while\n * the `subscription` backoff lasts.\n */\n readonly subscriptionLane: SubscriptionLane;\n /** The current state. The same object until something changes. */\n getState(): ConnectionState;\n /** Calls `listener` after every state change; returns the unsubscribe function. */\n subscribe(listener: () => void): () => void;\n /** Connects, unless the connection is connected or connecting. */\n open(): void;\n /**\n * Disconnects and stays idle until `open`. Calls in flight fail; calls\n * still waiting in Socket.IO's send buffer fail with `CANCELLED`. No timer\n * of the connection's is left running.\n */\n close(): void;\n /**\n * Opens the connection for one user of it (a mounted provider) and returns\n * the release. The last release closes it on the next tick, unless the\n * connection is retained again first, so React's strict mode, which mounts\n * effects twice, does not reconnect.\n */\n retain(): () => void;\n /**\n * Changes the credentials. When they differ from the current ones\n * (compared by value as they are sent: a token string is the same as\n * `{ token }` with that value), the calls still waiting in Socket.IO's send\n * buffer fail with `CANCELLED` (they were made as the last credentials,\n * and would be sent as the new ones), an open connection reconnects with\n * the new credentials, and this returns true.\n */\n setAuth(auth: ConnectionAuth): boolean;\n /** A call id, unique among this connection's calls in flight. */\n nextCallId(): number;\n /** Starts or extends `kind`'s backoff after a `RATE_LIMITED` answer. */\n reportRateLimited(kind: BackoffKind, retryAfterMs?: number): void;\n /** How long `kind` still backs off, in milliseconds; 0 when it does not. */\n backoffRemaining(kind: BackoffKind): number;\n /**\n * Calls `listener` each time the socket connects again with the same\n * credentials: after it dropped, after a `qd:rotate`, or on `open` after\n * the server ended it. Not on the first connect, nor after `setAuth` or\n * `close`. Watched topics are joined again first. Returns the unsubscribe\n * function.\n */\n onReconnect(listener: () => void): () => void;\n /**\n * Calls `listener` with every `qd:hello` the server sends: on the first\n * connect, after every reconnect (a dropped socket, a `qd:rotate`, `open`\n * after the server ended it) and after new credentials. Each hello is a\n * new socket, which is in no app room until a call joins it, so this is\n * where a client joins its rooms again (`useJoin` does it for React).\n * When the connection is connected and already has its hello, `listener`\n * is also called with it, in a microtask. Returns the unsubscribe\n * function.\n *\n * @example\n * const stop = connection.onHello(() => {\n * void call(connection, { service: \"gameService\", method: \"watchWorld\", input: { worldId } });\n * });\n */\n onHello(listener: (hello: HelloFrame) => void): () => void;\n /**\n * Watches a change topic (RFC 0003 section 11.3): the first watch of a\n * topic sends `qd:watch` (again on every connect), the last one to end\n * sends `qd:unwatch`, and `onChanged` receives the topic's `qd:changed`\n * frames meanwhile. Returns the function that ends the watch.\n *\n * @example\n * const stop = connection.watch({\n * service: \"taskService\",\n * topic: collectionTopic(\"byProject\", projectId),\n * onChanged: () => void refresh(),\n * });\n */\n watch(watch: TopicWatch): () => void;\n /**\n * While the socket is connected and a watched topic's `qd:watch` is\n * unanswered, a promise that resolves once the server answers it (joined\n * or refused), the attempt fails, the topic is left or the connection\n * closes; `undefined` when there is nothing to wait for. A read sent after\n * it sees every change made before the join, so the watches with `key`\n * are not told `onJoined` for that join. The query hooks hold a watched\n * query's read with it.\n */\n waitForJoin(wait: JoinWait): Promise<void> | undefined;\n}\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\n/** How much longer than the server's time limit a call waits, so the server's `TIMEOUT` arrives first. */\nconst HELLO_TIMEOUT_MARGIN_MS = 2_000;\n\n/** The state every connection starts in, and the one a server renders (no socket opens there). */\nexport const INITIAL_CONNECTION_STATE: ConnectionState = Object.freeze({\n status: \"idle\",\n reconnecting: false,\n hello: null,\n serviceAccess: null,\n refusal: null,\n backoff: Object.freeze({}),\n});\n\ninterface StateStore {\n get(): ConnectionState;\n set(patch: Partial<ConnectionState>): void;\n subscribe(listener: () => void): () => void;\n}\n\nfunction createStore(): StateStore {\n let state = INITIAL_CONNECTION_STATE;\n const listeners = new Set<() => void>();\n return {\n get: () => state,\n set(patch) {\n state = Object.freeze({ ...state, ...patch });\n for (const listener of [...listeners]) {\n listener();\n }\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\nfunction credentialsOf(auth: ConnectionAuth): Readonly<Record<string, unknown>> {\n if (typeof auth === \"string\") {\n return auth === \"\" ? {} : { token: auth };\n }\n return auth ?? {};\n}\n\n/**\n * Equal credentials give equal keys, whatever the order of their fields at\n * any depth. They are compared as they are sent: a token string and\n * `{ token }` with the same value are the same credentials.\n */\nfunction authKey(auth: ConnectionAuth): string {\n const sorted = (_key: string, value: unknown): unknown =>\n isRecord(value)\n ? Object.fromEntries(Object.entries(value).sort(([a], [b]) => (a < b ? -1 : 1)))\n : value;\n return JSON.stringify(credentialsOf(auth), sorted);\n}\n\n/** An acknowledgement as Socket.IO keeps it: `withError` when it takes an error first (`timeout`). */\ntype Ack = ((...args: unknown[]) => void) & { readonly withError?: boolean };\n\n/**\n * Fails every packet waiting in Socket.IO's send buffer with `error` and\n * empties the buffer. Socket.IO sends that buffer on the next connect,\n * whatever credentials the socket then carries, so a call made as one user\n * would run as the next.\n */\nfunction failBuffered(socket: QuickdrawSocket, error: Error): void {\n const buffered = socket.sendBuffer.splice(0);\n const { acks } = socket as unknown as { readonly acks: Record<string, Ack | undefined> };\n for (const packet of buffered) {\n const id = packet.id === undefined ? undefined : String(packet.id);\n const ack = id === undefined ? undefined : acks[id];\n if (id !== undefined && ack !== undefined) {\n Reflect.deleteProperty(acks, id);\n if (ack.withError === true) {\n ack.call(socket, error);\n }\n }\n }\n}\n\n/**\n * True for the reason Socket.IO gives a disconnect the server made\n * (`socket.disconnect(true)`: signed out everywhere, revoked, expired, a\n * flooded channel). A dropped transport, a ping timeout and the client's own\n * disconnect give other reasons.\n */\nexport function isServerDisconnect(reason: unknown): boolean {\n return reason === \"io server disconnect\";\n}\n\n/**\n * True while `connection`'s socket will connect unless the app closes it:\n * Socket.IO reconnects it (`socket.active`), or the connection waits to\n * connect it again because the server could not authenticate it (its\n * socket inactive, its status `connecting`). Calls made meanwhile wait in\n * the send buffer, and reads wait for the hello.\n */\nexport function mayConnect(connection: Pick<QuickdrawConnection, \"socket\" | \"getState\">): boolean {\n return connection.socket.active || connection.getState().status === \"connecting\";\n}\n\n/**\n * True for the `connect_error` data of a handshake refused for its session:\n * `UNAUTHENTICATED` or `FORBIDDEN`. `INTERNAL` (the server could not check\n * the credentials) is not one: the session stands.\n */\nexport function isSessionRefusal(data: unknown): boolean {\n return isRecord(data) && (data.code === \"UNAUTHENTICATED\" || data.code === \"FORBIDDEN\");\n}\n\nfunction refusalOf(error: Error & { readonly data?: unknown }): ConnectionRefusal {\n if (isProtocolMismatch(error.data)) {\n return { code: error.data.code, expected: error.data.expected };\n }\n if (isAuthenticationRefused(error.data)) {\n return { code: \"UNAUTHENTICATED\" };\n }\n if (isAuthenticationUnavailable(error.data)) {\n return { code: \"INTERNAL\" };\n }\n return { code: \"REFUSED\", message: error.message };\n}\n\n/** The longest delay a timer honors (2^31 - 1 ms); a longer one fires at once. */\nconst MAX_TIMEOUT_MS = 2_147_483_647;\n\n/** True when `value` can be a call's time limit: more than 0 and at most 2^31 - 1 milliseconds. */\nexport function isTimeLimit(value: unknown): value is number {\n return typeof value === \"number\" && value > 0 && value <= MAX_TIMEOUT_MS;\n}\n\n/** The `timeoutMs` option, checked; `undefined` when it was left out. */\nfunction checkOptions(options: QuickdrawConnectionOptions): number | undefined {\n if (typeof options.url !== \"string\" || options.url === \"\") {\n throw new TypeError(\"createQuickdrawConnection: url must be the server's URL\");\n }\n const { timeoutMs } = options;\n if (timeoutMs !== undefined && !isTimeLimit(timeoutMs)) {\n throw new TypeError(\n \"createQuickdrawConnection: timeoutMs must be a number of milliseconds, above 0 and at most 2^31 - 1\",\n );\n }\n return timeoutMs;\n}\n\n/** A call's default time limit: the option, else the server's limit plus the margin, else 10 s. */\nfunction defaultTimeout(option: number | undefined, hello: HelloFrame | null): number {\n if (option !== undefined) {\n return option;\n }\n // The frame came over the network: its shape is checked, not trusted.\n const limits: unknown = hello?.limits;\n const serverMs: unknown = isRecord(limits) ? limits.callTimeoutMs : undefined;\n const fromHello = typeof serverMs === \"number\" ? serverMs + HELLO_TIMEOUT_MARGIN_MS : undefined;\n return isTimeLimit(fromHello) ? fromHello : DEFAULT_TIMEOUT_MS;\n}\n\n/**\n * Whether the socket has connected with the current credentials since the\n * connection last opened (the next connect is then a reconnect), and the\n * topics, the subscription lane and the retries after a refusal the server\n * could not authenticate (`retry.ts`), which stop retrying and waiting when\n * the connection closes.\n */\ninterface Session {\n established: boolean;\n readonly topics: Topics;\n readonly lane: SubscriptionLane;\n readonly retry: Retry;\n}\n\n/** When the socket opens and closes: the app's `open`, `close` and `retain`, new credentials, `qd:rotate`. */\ninterface Lifecycle {\n open(): void;\n close(): void;\n retain(): () => void;\n /** Reconnects an open connection, for new credentials. */\n reconnect(): void;\n /** Reconnects at a random moment within `withinMs` (`qd:rotate`). */\n rotateWithin(withinMs: number): void;\n /** The status a socket that will not retry is in: closed by the app, or ended by the server. */\n stoppedStatus(): ConnectionStatus;\n}\n\nfunction createLifecycle(\n socket: QuickdrawSocket,\n store: StateStore,\n backoff: Backoff,\n session: Session,\n): Lifecycle {\n let opened = false;\n let switching = false;\n let users = 0;\n let closeTimer: ReturnType<typeof setTimeout> | undefined;\n let rotateTimer: ReturnType<typeof setTimeout> | undefined;\n\n function connect(): void {\n socket.connect();\n store.set({ status: \"connecting\", refusal: null });\n }\n\n function reconnect(): void {\n if (!opened) {\n return;\n }\n switching = true;\n socket.disconnect();\n switching = false;\n connect();\n }\n\n function open(): void {\n clearTimeout(closeTimer);\n closeTimer = undefined;\n opened = true;\n if (!socket.connected && !socket.active) {\n connect();\n }\n }\n\n function close(): void {\n opened = false;\n session.established = false;\n session.topics.stop();\n session.lane.stop();\n session.retry.stop();\n clearTimeout(closeTimer);\n closeTimer = undefined;\n clearTimeout(rotateTimer);\n rotateTimer = undefined;\n backoff.clear();\n // A call waiting to be sent would go out on the next `open`, long after the app closed.\n failBuffered(\n socket,\n new QuickdrawError(\"CANCELLED\", \"The connection closed before the call was sent\"),\n );\n socket.disconnect();\n store.set({ status: \"idle\", reconnecting: false });\n }\n\n function retain(): () => void {\n users += 1;\n open();\n let released = false;\n return () => {\n if (!released) {\n released = true;\n users -= 1;\n if (users === 0) {\n closeTimer = setTimeout(close, 0);\n }\n }\n };\n }\n\n function rotateWithin(withinMs: number): void {\n if (rotateTimer === undefined) {\n rotateTimer = setTimeout(() => {\n rotateTimer = undefined;\n reconnect();\n }, Math.random() * withinMs);\n }\n }\n\n function stoppedStatus(): ConnectionStatus {\n if (switching) {\n return \"connecting\";\n }\n return opened ? \"disconnected\" : \"idle\";\n }\n\n return { open, close, retain, reconnect, rotateWithin, stoppedStatus };\n}\n\n/** One `onHello` listener, and the last hello it was given, so no hello reaches it twice. */\ninterface HelloListener {\n readonly listener: (hello: HelloFrame) => void;\n last: HelloFrame | undefined;\n}\n\n/** What the socket's own events update: the state, the session (and its topics) and the listeners. */\ninterface Wiring {\n readonly socket: QuickdrawSocket;\n readonly store: StateStore;\n readonly lifecycle: Lifecycle;\n readonly session: Session;\n readonly reconnected: Set<() => void>;\n readonly hellos: Set<HelloListener>;\n readonly onProtocolMismatch: (mismatch: ProtocolMismatch) => void;\n}\n\n/** Gives `entry` the hello, unless it was given that one already or stopped listening. */\nfunction sayHello(\n hellos: ReadonlySet<HelloListener>,\n entry: HelloListener,\n hello: HelloFrame,\n): void {\n if (hellos.has(entry) && entry.last !== hello) {\n entry.last = hello;\n entry.listener(hello);\n }\n}\n\n/** Adds `listener` to `set`; returns the function that takes it out again. */\nfunction addListener<T>(set: Set<T>, listener: T): () => void {\n set.add(listener);\n return () => {\n set.delete(listener);\n };\n}\n\n/** `connection.onHello`: every hello from now on, and the current one in a microtask. */\nfunction listenForHellos(\n wiring: Pick<Wiring, \"hellos\" | \"store\">,\n listener: (hello: HelloFrame) => void,\n): () => void {\n const { hellos, store } = wiring;\n const entry: HelloListener = { listener, last: undefined };\n hellos.add(entry);\n const { status, hello } = store.get();\n if (status === \"connected\" && hello !== null) {\n queueMicrotask(() => {\n notifyEach([entry], (current) => {\n sayHello(hellos, current, hello);\n });\n });\n }\n return () => {\n hellos.delete(entry);\n };\n}\n\n/** The socket's connects, disconnects and refusals. */\nfunction listenToSocket(wiring: Wiring): void {\n const { socket, store, lifecycle, session } = wiring;\n socket.on(\"connect\", () => {\n const again = session.established;\n session.established = true;\n session.retry.connected();\n store.set({ status: \"connected\", reconnecting: false, refusal: null });\n session.topics.rejoin();\n if (again) {\n notifyEach(wiring.reconnected, (listener) => {\n listener();\n });\n }\n });\n socket.on(\"disconnect\", (reason: unknown) => {\n const status = socket.active ? \"connecting\" : lifecycle.stoppedStatus();\n // The server ended the session: who it was is no longer known.\n const ended = isServerDisconnect(reason) ? { hello: null, serviceAccess: null } : {};\n store.set({ status, reconnecting: status === \"connecting\" && session.established, ...ended });\n });\n socket.on(\"connect_error\", (error: Error & { readonly data?: unknown }) => {\n if (socket.active) {\n return;\n }\n if (isAuthenticationUnavailable(error.data) && session.retry.later()) {\n // The server could not check the credentials (its session store is down, say): they may\n // well be good, so who the connection acts for stands, and it connects again by itself.\n store.set({ status: \"connecting\", reconnecting: session.established, refusal: null });\n return;\n }\n const refusal = refusalOf(error);\n session.established = false;\n const ended = isSessionRefusal(error.data);\n if (ended) {\n // Socket.IO would send them on the next connect, as whoever the credentials then name.\n failBuffered(\n socket,\n new QuickdrawError(\"CANCELLED\", \"The session ended before the call was sent\"),\n );\n }\n store.set({\n status: \"refused\",\n reconnecting: false,\n refusal,\n ...(ended ? { hello: null, serviceAccess: null } : {}),\n });\n if (refusal.code === \"PROTOCOL_MISMATCH\") {\n wiring.onProtocolMismatch(refusal);\n }\n });\n}\n\n/** The pushes a server sends the whole connection. */\nfunction listenToPushes(wiring: Wiring): void {\n const { socket, store, lifecycle } = wiring;\n socket.on(SERVER_EVENTS.hello, (frame) => {\n const grants = isRecord(frame) && isRecord(frame.serviceAccess) ? frame.serviceAccess : null;\n store.set({ hello: frame, serviceAccess: grants as HelloFrame[\"serviceAccess\"] | null });\n notifyEach(wiring.hellos, (entry) => {\n sayHello(wiring.hellos, entry, frame);\n });\n });\n socket.on(SERVER_EVENTS.access, (frame) => {\n if (isRecord(frame) && isRecord(frame.serviceAccess)) {\n store.set({ serviceAccess: frame.serviceAccess as HelloFrame[\"serviceAccess\"] });\n }\n });\n socket.on(SERVER_EVENTS.rotate, (frame) => {\n const withinMs: unknown = isRecord(frame) ? frame.withinMs : undefined;\n if (typeof withinMs === \"number\" && Number.isFinite(withinMs) && withinMs >= 0) {\n lifecycle.rotateWithin(withinMs);\n }\n });\n}\n\n/**\n * Creates a v5 client connection to `url`. It does not connect until `open`\n * or `retain`. React apps use `QuickdrawProvider`, which owns one; other\n * clients (React Native without the provider, Node scripts) use this with\n * `call`.\n *\n * @example\n * const connection = createQuickdrawConnection({ url: \"http://localhost:4000\", auth: token });\n * connection.open();\n * const reply = await call(connection, { service: \"taskService\", method: \"get\", input: { id } });\n */\nexport function createQuickdrawConnection(\n options: QuickdrawConnectionOptions,\n): QuickdrawConnection {\n const timeoutOption = checkOptions(options);\n const store = createStore();\n const backoff = createBackoff((windows) => {\n store.set({ backoff: windows });\n });\n let auth = options.auth;\n let nextId = 0;\n const socket = createSocket(options, () => credentialsOf(auth));\n const timeoutMs = (): number => defaultTimeout(timeoutOption, store.get().hello);\n const subscriptionLane = createSubscriptionLane({\n socket,\n timeoutMs,\n hello: () => store.get().hello,\n backoffRemaining: backoff.remaining,\n });\n const topics = createTopics({\n socket,\n lane: subscriptionLane,\n backoffRemaining: backoff.remaining,\n reportRateLimited: backoff.report,\n });\n // Retries run while the connection is trying to connect: `close` makes it idle.\n const retry = createRetry(socket, () => store.get().status === \"connecting\");\n const session: Session = { established: false, topics, lane: subscriptionLane, retry };\n const lifecycle = createLifecycle(socket, store, backoff, session);\n const reconnected = new Set<() => void>();\n const wiring: Wiring = {\n socket,\n store,\n lifecycle,\n session,\n reconnected,\n hellos: new Set(),\n onProtocolMismatch: options.onProtocolMismatch ?? reloadOncePerSession,\n };\n listenToSocket(wiring);\n listenToPushes(wiring);\n\n return Object.freeze({\n url: options.url,\n get timeoutMs(): number {\n return timeoutMs();\n },\n socket,\n subscriptionLane,\n getState: store.get,\n subscribe: store.subscribe,\n open: lifecycle.open,\n close: lifecycle.close,\n retain: lifecycle.retain,\n setAuth(next: ConnectionAuth): boolean {\n if (authKey(next) === authKey(auth)) {\n return false;\n }\n auth = next;\n session.established = false;\n retry.stop();\n failBuffered(\n socket,\n new QuickdrawError(\"CANCELLED\", \"The credentials changed before the call was sent\"),\n );\n store.set({ hello: null, serviceAccess: null });\n lifecycle.reconnect();\n return true;\n },\n nextCallId(): number {\n const id = nextId;\n nextId = nextId >= Number.MAX_SAFE_INTEGER ? 0 : nextId + 1;\n return id;\n },\n reportRateLimited: backoff.report,\n backoffRemaining: backoff.remaining,\n onReconnect: (listener: () => void) => addListener(reconnected, listener),\n onHello: (listener: (hello: HelloFrame) => void) => listenForHellos(wiring, listener),\n watch: topics.watch,\n waitForJoin: topics.waitForJoin,\n });\n}\n","// The hosts of the app rooms a connection's socket is in (host mode, RFC\n// 0003 section 12.5): the server's `qd:host [room, host]` frames, read by\n// one listener per connection, made on first use (the live data makes it\n// when a provider mounts, before its socket connects). A room's host is\n// forgotten when the socket leaves the room (`qd:presence` with\n// `users: []`) or disconnects: a new socket is in no room, and hears each\n// room's host again as it joins.\n//\n// React-free.\n\nimport { SERVER_EVENTS } from \"../../contract/names\";\nimport { isRecord } from \"../../protocol/guards\";\nimport { isRoomName, isSocketId } from \"../../protocol/peer\";\nimport type { QuickdrawConnection } from \"../connection\";\nimport { notifyEach } from \"../watch\";\n\n/** Hears a room's host change: its socket id, or `null` when the room has none now. */\nexport type HostListener = (room: string, host: string | null) => void;\n\n/** The hosts of one connection's rooms. */\nexport interface HostDirectory {\n /** The host of `room`, when the socket is in it and it has one. */\n of(room: string): string | undefined;\n /** The rooms whose host is known, with it. */\n readonly rooms: ReadonlyMap<string, string>;\n /** Calls `listener` on each change; returns its removal. */\n listen(listener: HostListener): () => void;\n}\n\nfunction createDirectory(connection: QuickdrawConnection): HostDirectory {\n const rooms = new Map<string, string>();\n const listeners = new Set<HostListener>();\n const set = (room: string, host: string | null): void => {\n if ((rooms.get(room) ?? null) === host) {\n return;\n }\n if (host === null) {\n rooms.delete(room);\n } else {\n rooms.set(room, host);\n }\n notifyEach(listeners, (listener) => {\n listener(room, host);\n });\n };\n const { socket } = connection;\n socket.on(SERVER_EVENTS.host, (frame: unknown) => {\n const [room, host] = Array.isArray(frame) ? (frame as readonly unknown[]) : [];\n if (isRoomName(room) && (host === null || isSocketId(host))) {\n set(room, host);\n }\n });\n socket.on(SERVER_EVENTS.presence, (frame: unknown) => {\n // `{ room, users: [] }`: this socket left the room.\n if (\n isRecord(frame) &&\n isRoomName(frame.room) &&\n Array.isArray(frame.users) &&\n frame.users.length === 0\n ) {\n set(frame.room, null);\n }\n });\n socket.on(\"disconnect\", () => {\n for (const room of [...rooms.keys()]) {\n set(room, null);\n }\n });\n return Object.freeze({\n of: (room: string) => rooms.get(room),\n rooms,\n listen(listener: HostListener): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n });\n}\n\nconst directories = new WeakMap<QuickdrawConnection, HostDirectory>();\n\n/** The hosts of `connection`'s rooms, made on first use. */\nexport function hostsOf(connection: QuickdrawConnection): HostDirectory {\n let directory = directories.get(connection);\n if (directory === undefined) {\n directory = createDirectory(connection);\n directories.set(connection, directory);\n }\n return directory;\n}\n\n/**\n * The host of app room `room` (host mode): the socket id the server named\n * in `qd:host`, or `null` when the room has none or this connection's\n * socket is not in it. Without React, call it (or `onHostChange`) before\n * the joining call, so the connection hears the room's host as it joins.\n */\nexport function hostOf(connection: QuickdrawConnection, room: string): string | null {\n return hostsOf(connection).of(room) ?? null;\n}\n\n/**\n * Calls `listener(room, host)` whenever the host of a room this\n * connection's socket is in changes (`null`: none now, or the socket left\n * the room or disconnected); returns its removal.\n */\nexport function onHostChange(connection: QuickdrawConnection, listener: HostListener): () => void {\n return hostsOf(connection).listen(listener);\n}\n","// A connection's ways to the hosts of its rooms (host mode, RFC 0003 section\n// 12.5): one per room, to the host `qd:host` named (`hosts.ts`). A link\n// (`createPeerLink`, `offerer: true`) is made when a host-served channel's\n// message or a host-served stream's feed first needs one, and again when\n// the room gets another host or the link closed while a feed still needs\n// it. After a link closes, the next to the same host waits\n// (`HOST_LINK_RETRY_MS`, doubling up to `HOST_LINK_MAX_RETRY_MS` while links\n// keep closing unopened): a game that sends input each frame makes no link\n// a frame when the server refuses them. When the host is this\n// connection's own socket and it serves the room (`defineHost(...).serve`),\n// the way is an in-process link (`local.ts`): the host hears this client as\n// one of its peers, over a link like theirs.\n//\n// No peer code is imported here, nor by `hosts.ts`, since every typed\n// client reaches both: the serving hands in its in-process link's maker\n// and `controlOf` with its host (`registerLocalHost`), and `./remote` (the\n// peer link, WebRTC, the relay, the lag simulator) loads on demand, once\n// the server announces peer links (`hello.limits.peers`) or at the first\n// link, whichever comes first. A tab then holds it before a redeploy\n// removes the chunk it was built with.\n//\n// A channel's message sent while no way is open is dropped, as a channel\n// message is when the connection is backed up. A feed subscribes (`[4]`)\n// each time a way opens; its seed (`[6]`, in chunks) replaces what it held,\n// items (`[7]`) append, a refusal (`[8]`) shows its error, and a way that\n// closes leaves its items until the next seed.\n//\n// React-free.\n\nimport type { HostMessage } from \"../../protocol/envelope\";\nimport type { WireError } from \"../../protocol/errors\";\nimport { isRecord } from \"../../protocol/guards\";\nimport { MAX_PEER_MESSAGE_BYTES } from \"../../protocol/version\";\nimport type { QuickdrawConnection } from \"../connection\";\nimport type { controlOf, LinkControl } from \"../peer/core\";\nimport type { PeerLink } from \"../peer/types\";\nimport { hostsOf, type HostDirectory } from \"./hosts\";\nimport type { createLocalLink } from \"./local\";\nimport type * as Remote from \"./remote\";\n\n/** One feed of a host-served stream, as the stream store holds it. */\nexport interface HostFeed {\n readonly service: string;\n readonly stream: string;\n readonly scope: string;\n /** The seed, whole: what the feed shows from now on. */\n seeded(items: readonly unknown[]): void;\n item(item: unknown): void;\n refused(error: WireError): void;\n /** The way to the host closed: the next seed replaces what the feed holds. */\n lost(): void;\n}\n\n/** A connection's ways to its rooms' hosts. */\nexport interface HostLinks {\n /** Sends a host-served channel's message to the host of `room`; false when no way is open. */\n send(room: string, service: string, channel: string, payload: unknown): boolean;\n /** Holds a feed of a host-served stream in `room`, subscribed whenever a way opens; returns its release. */\n subscribe(room: string, feed: HostFeed): () => void;\n}\n\n/**\n * How a room's host served by this connection takes this client as a peer:\n * the host's end of their in-process link, open.\n */\nexport type LocalHost = (link: PeerLink) => void;\n\n/**\n * What the serving hands in for its in-process link, so this module\n * imports no peer code: `createLocalLink`, and `controlOf` for its ends.\n */\nexport interface LocalLinks {\n readonly createLink: typeof createLocalLink;\n readonly controlOf: typeof controlOf;\n}\n\n/**\n * After a link to a room's host closed, the next to that host waits this\n * long, doubling while links keep closing unopened.\n */\nexport const HOST_LINK_RETRY_MS = 1000;\n\n/** The longest wait before the next link to a room's host. */\nexport const HOST_LINK_MAX_RETRY_MS = 15_000;\n\n/** The items a peer keeps of a seed in chunks, the latest: as many as a feed shows. */\nexport const MAX_SEED_ITEMS = 10_000;\n\ninterface Way {\n readonly send: LinkControl[\"send\"];\n readonly close: () => void;\n}\n\n/** A room's host this connection serves, and what makes its in-process link. */\ninterface LocalServing extends LocalLinks {\n readonly host: LocalHost;\n}\n\ninterface RoomState {\n readonly room: string;\n /** The host the way below goes to. */\n host: string | undefined;\n /** Open, and what sends on it. */\n way: Way | undefined;\n /** Made, not closed yet: a link connecting or open, the in-process way, or `./remote` loading for a link. */\n pending: (() => void) | undefined;\n readonly feeds: Map<string, { readonly feed: HostFeed; chunks: unknown[] | undefined }>;\n retry: ReturnType<typeof setTimeout> | undefined;\n retryMs: number;\n /** After a link closed: no link to `host` opens before `until` (`Date.now()`). */\n backoff: { readonly host: string; readonly until: number } | undefined;\n}\n\ninterface LinksState {\n readonly connection: QuickdrawConnection;\n readonly directory: HostDirectory;\n readonly rooms: Map<string, RoomState>;\n readonly locals: Map<string, LocalServing>;\n}\n\n/** `./remote`, loading or loaded: `undefined` before the first load and after a failed one. */\nlet remote: Promise<typeof Remote> | undefined;\n\n/** Loads `./remote` once, for every connection; a failed load is tried again by the next caller. */\nfunction loadRemote(): Promise<typeof Remote> {\n remote ??= import(\"./remote\").catch((error: unknown) => {\n remote = undefined;\n throw error;\n });\n return remote;\n}\n\n/**\n * Starts loading `./remote` once `connection`'s server announces peer\n * links (`hello.limits.peers`, from `createServer({ peers: true })`); a\n * server without them never has it loaded. A failed load waits for the\n * first link, which tries again.\n */\nfunction loadWhenAnnounced(connection: QuickdrawConnection): void {\n const announced = (): boolean => {\n // As the server sent it: a hello without limits announces nothing.\n const limits: unknown = connection.getState().hello?.limits;\n return isRecord(limits) && limits.peers !== undefined;\n };\n const load = (): void => {\n loadRemote().catch(() => undefined);\n };\n if (announced()) {\n load();\n return;\n }\n const stop = connection.subscribe(() => {\n if (announced()) {\n stop();\n load();\n }\n });\n}\n\nconst feedKey = (service: string, stream: string, scope: string): string =>\n `${service}\\u0000${stream}\\u0000${scope}`;\n\nfunction onMessage(state: RoomState, message: HostMessage): void {\n if (message[0] !== 6 && message[0] !== 7 && message[0] !== 8) {\n return;\n }\n const entry = state.feeds.get(feedKey(message[1], message[2], message[3]));\n if (entry === undefined) {\n return;\n }\n if (message[0] === 6) {\n const chunks = [...(entry.chunks ?? []), ...message[4]].slice(-MAX_SEED_ITEMS);\n entry.chunks = message[5] ? undefined : chunks;\n if (message[5]) {\n entry.feed.seeded(chunks);\n }\n } else if (message[0] === 7) {\n entry.feed.item(message[4]);\n } else if (message[0] === 8) {\n entry.feed.refused(message[4]);\n }\n}\n\nfunction subscribeOn(way: Way, feed: HostFeed): void {\n way.send(\"rel\", [4, feed.service, feed.stream, feed.scope]);\n}\n\nfunction opened(state: RoomState, way: Way): void {\n state.way = way;\n state.retryMs = HOST_LINK_RETRY_MS;\n state.backoff = undefined;\n for (const entry of state.feeds.values()) {\n entry.chunks = undefined;\n subscribeOn(way, entry.feed);\n }\n}\n\n/** Ends the room's way, if any; its feeds wait for the next seed. */\nfunction disconnect(state: RoomState): void {\n const { pending } = state;\n state.pending = undefined;\n state.way = undefined;\n state.host = undefined;\n clearTimeout(state.retry);\n state.retry = undefined;\n pending?.();\n for (const entry of state.feeds.values()) {\n entry.chunks = undefined;\n entry.feed.lost();\n }\n}\n\n/**\n * Takes `link` to the room's host at `host`, whose control side is\n * `control`, as the room's way: open, it subscribes the feeds; closed, the\n * next link waits.\n */\nfunction takeLink(\n links: LinksState,\n state: RoomState,\n host: string,\n link: PeerLink,\n control: LinkControl | undefined,\n): void {\n if (control === undefined) {\n return;\n }\n const way: Way = {\n send: (label, message) => control.send(label, message),\n close: () => {\n link.close();\n },\n };\n state.pending = way.close;\n control.on((message) => {\n onMessage(state, message);\n });\n const open = (): void => {\n if (state.pending === way.close) {\n opened(state, way);\n }\n };\n link.on(\"open\", open);\n link.on(\"close\", () => {\n if (state.pending === way.close) {\n disconnect(state);\n retryLater(links, state, host);\n }\n });\n if (link.state === \"open\") {\n open();\n }\n}\n\n/** The in-process link to a host this connection serves, open at once. */\nfunction connectLocal(\n links: LinksState,\n state: RoomState,\n local: LocalServing,\n self: string,\n): void {\n const { connection } = links;\n const userId = connection.getState().hello?.userId ?? \"\";\n const [near, far] = local.createLink(\n state.room,\n self,\n userId,\n () => connection.getState().hello?.limits.peers?.maxMessageBytes ?? MAX_PEER_MESSAGE_BYTES,\n );\n local.host(far);\n takeLink(links, state, self, near, local.controlOf(near));\n}\n\n/**\n * A link to the room's host at `host`, once `./remote` has loaded: the way\n * is pending meanwhile, so `connect` makes no other. A load the room no\n * longer waits for (its way ended, or its host changed) is dropped, and a\n * load that failed is a link that closed: the next waits.\n */\nfunction connectLink(links: LinksState, state: RoomState, host: string): void {\n const loading = (): void => undefined;\n state.pending = loading;\n void loadRemote().then(\n ({ createPeerLink, controlOf }) => {\n if (state.pending !== loading) {\n return;\n }\n state.pending = undefined;\n if (links.directory.of(state.room) !== host) {\n disconnect(state);\n return;\n }\n let link;\n try {\n link = createPeerLink(links.connection, { room: state.room, to: host, offerer: true });\n } catch {\n // A link the app opened itself to the host, in this room: host mode leaves it alone.\n return;\n }\n takeLink(links, state, host, link, controlOf(link));\n },\n () => {\n if (state.pending === loading) {\n disconnect(state);\n retryLater(links, state, host);\n }\n },\n );\n}\n\n/**\n * Opens a way to the room's host, unless one to it is open or being made,\n * or a link to it closed within the wait.\n */\nfunction connect(links: LinksState, state: RoomState): void {\n const host = links.directory.of(state.room);\n if (host === undefined || (state.host === host && state.pending !== undefined)) {\n return;\n }\n if (state.backoff?.host === host && Date.now() < state.backoff.until) {\n return;\n }\n if (state.pending !== undefined) {\n disconnect(state);\n }\n state.host = host;\n const self = links.connection.socket.id;\n const local = links.locals.get(state.room);\n if (host === self) {\n if (local !== undefined && self !== undefined) {\n connectLocal(links, state, local, self);\n }\n return;\n }\n connectLink(links, state, host);\n}\n\n/**\n * A link to `host` closed: the next to it waits, and doubles the wait after\n * it. With feeds to serve then (a feed held during the wait included), links\n * again, while the room has a host.\n */\nfunction retryLater(links: LinksState, state: RoomState, host: string): void {\n const wait = state.retryMs;\n state.backoff = { host, until: Date.now() + wait };\n state.retryMs = Math.min(HOST_LINK_MAX_RETRY_MS, wait * 2);\n if (links.directory.of(state.room) === undefined) {\n return;\n }\n clearTimeout(state.retry);\n state.retry = setTimeout(() => {\n state.retry = undefined;\n state.backoff = undefined;\n if (state.feeds.size > 0) {\n connect(links, state);\n }\n }, wait);\n}\n\nfunction roomOf(links: LinksState, room: string): RoomState {\n let state = links.rooms.get(room);\n if (state === undefined) {\n state = {\n room,\n host: undefined,\n way: undefined,\n pending: undefined,\n feeds: new Map(),\n retry: undefined,\n retryMs: HOST_LINK_RETRY_MS,\n backoff: undefined,\n };\n links.rooms.set(room, state);\n }\n return state;\n}\n\nfunction createLinks(connection: QuickdrawConnection): LinksState & HostLinks {\n const links: LinksState = {\n connection,\n directory: hostsOf(connection),\n rooms: new Map(),\n locals: new Map(),\n };\n loadWhenAnnounced(connection);\n links.directory.listen((room, host) => {\n const state = links.rooms.get(room);\n if (state === undefined) {\n return;\n }\n if (host === null) {\n disconnect(state);\n return;\n }\n // The server named the room's host again (another host, or this socket\n // joined again): its first link opens at once.\n state.backoff = undefined;\n state.retryMs = HOST_LINK_RETRY_MS;\n if (state.feeds.size > 0) {\n connect(links, state);\n }\n });\n return Object.freeze({\n ...links,\n send(room: string, service: string, channel: string, payload: unknown): boolean {\n const state = roomOf(links, room);\n connect(links, state);\n return state.way?.send(\"fast\", [3, service, channel, payload]) ?? false;\n },\n subscribe(room: string, feed: HostFeed): () => void {\n const state = roomOf(links, room);\n const key = feedKey(feed.service, feed.stream, feed.scope);\n const entry = { feed, chunks: undefined };\n state.feeds.set(key, entry);\n const { way } = state;\n connect(links, state);\n // A way `connect` opened (the in-process link) subscribed every feed already.\n if (way !== undefined && state.way === way) {\n subscribeOn(way, feed);\n }\n return () => {\n if (state.feeds.get(key) === entry) {\n state.feeds.delete(key);\n state.way?.send(\"rel\", [5, feed.service, feed.stream, feed.scope]);\n }\n };\n },\n });\n}\n\nconst all = new WeakMap<QuickdrawConnection, LinksState & HostLinks>();\n\nfunction linksStateOf(connection: QuickdrawConnection): LinksState & HostLinks {\n let links = all.get(connection);\n if (links === undefined) {\n links = createLinks(connection);\n all.set(connection, links);\n }\n return links;\n}\n\n/** The ways of `connection` to its rooms' hosts, made on first use. */\nexport function hostLinksOf(connection: QuickdrawConnection): HostLinks {\n return linksStateOf(connection);\n}\n\n/**\n * Registers the host this connection serves for `room`, so this client's\n * own channels and feeds of the room reach it in process, over the link\n * `local` makes (the serving hands it in, so this module imports no peer\n * code); returns its removal.\n */\nexport function registerLocalHost(\n connection: QuickdrawConnection,\n room: string,\n host: LocalHost,\n local: LocalLinks,\n): () => void {\n const links = linksStateOf(connection);\n const serving: LocalServing = { host, ...local };\n links.locals.set(room, serving);\n const state = links.rooms.get(room);\n if (state !== undefined && state.feeds.size > 0) {\n connect(links, state);\n }\n return () => {\n if (links.locals.get(room) === serving) {\n links.locals.delete(room);\n const current = links.rooms.get(room);\n if (current !== undefined && current.host === connection.socket.id) {\n disconnect(current);\n }\n }\n };\n}\n"],"mappings":"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","names":["opened","connect"]}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ACCESS_LEVELS,
|
|
3
|
+
isAccessLevel
|
|
4
|
+
} from "./chunk-AK23XSWD.js";
|
|
1
5
|
import {
|
|
2
6
|
CRUD_MAX_IDS,
|
|
3
7
|
LIST_DEFAULT_LIMIT,
|
|
@@ -22,11 +26,7 @@ import {
|
|
|
22
26
|
reorderInput,
|
|
23
27
|
unknownKeys,
|
|
24
28
|
withId
|
|
25
|
-
} from "./chunk-
|
|
26
|
-
import {
|
|
27
|
-
ACCESS_LEVELS,
|
|
28
|
-
isAccessLevel
|
|
29
|
-
} from "./chunk-AK23XSWD.js";
|
|
29
|
+
} from "./chunk-MPQAT55F.js";
|
|
30
30
|
import {
|
|
31
31
|
isStandardSchema
|
|
32
32
|
} from "./chunk-47AEXVDM.js";
|
|
@@ -513,4 +513,4 @@ export {
|
|
|
513
513
|
sharingSpecOf,
|
|
514
514
|
sharing
|
|
515
515
|
};
|
|
516
|
-
//# sourceMappingURL=chunk-
|
|
516
|
+
//# sourceMappingURL=chunk-VZFJTKIW.js.map
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isRecord
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
|
|
5
|
-
// src/version.ts
|
|
6
|
-
var QUICKDRAW_VERSION = "5.1.2";
|
|
3
|
+
} from "./chunk-QSU47XEX.js";
|
|
7
4
|
|
|
8
5
|
// src/contract/names.ts
|
|
9
6
|
function entityRoom(service, id, level) {
|
|
@@ -22,6 +19,7 @@ function collectionTopic(collection, scope) {
|
|
|
22
19
|
function streamRoom(service, stream, scope) {
|
|
23
20
|
return scope === void 0 ? `qd:s:${service}:${stream}` : `qd:s:${service}:${stream}:${scope}`;
|
|
24
21
|
}
|
|
22
|
+
var STREAM_ROOM_PREFIX = "qd:s:";
|
|
25
23
|
function userRoom(userId) {
|
|
26
24
|
return `user:${userId}`;
|
|
27
25
|
}
|
|
@@ -57,7 +55,11 @@ var CLIENT_EVENTS = Object.freeze({
|
|
|
57
55
|
/** Unsubscribes from a stream. */
|
|
58
56
|
streamUnsub: "qd:stream:unsub",
|
|
59
57
|
/** A fire-and-forget channel message, sent volatile: `[s, channel, payload]`. */
|
|
60
|
-
channel: "qd:ch"
|
|
58
|
+
channel: "qd:ch",
|
|
59
|
+
/** A peer link's signal to another socket of an app room: `{ room, to, sig }`. */
|
|
60
|
+
peer: "qd:peer",
|
|
61
|
+
/** A peer link's message for the server to relay: `[room, to, label, text]`, dropped on `fast` when backed up. */
|
|
62
|
+
relay: "qd:relay"
|
|
61
63
|
});
|
|
62
64
|
var SERVER_EVENTS = Object.freeze({
|
|
63
65
|
/** The handshake reply: `{ protocol, server, limits, features, userId, serviceAccess }`. */
|
|
@@ -79,7 +81,13 @@ var SERVER_EVENTS = Object.freeze({
|
|
|
79
81
|
/** Reconnect within a jitter window. */
|
|
80
82
|
rotate: "qd:rotate",
|
|
81
83
|
/** The user's service grants changed. */
|
|
82
|
-
access: "qd:access"
|
|
84
|
+
access: "qd:access",
|
|
85
|
+
/** A peer link's signal from another socket of an app room: `{ room, from, userId, sig }`. */
|
|
86
|
+
peer: "qd:peer",
|
|
87
|
+
/** A peer link's message the server relays: `[room, from, label, text]`. */
|
|
88
|
+
relay: "qd:relay",
|
|
89
|
+
/** An app room's host (host mode): `[room, host]`, the host's socket id or `null`. */
|
|
90
|
+
host: "qd:host"
|
|
83
91
|
});
|
|
84
92
|
|
|
85
93
|
// src/protocol/version.ts
|
|
@@ -94,20 +102,34 @@ function isProtocolMismatch(value) {
|
|
|
94
102
|
function isAuthenticationRefused(value) {
|
|
95
103
|
return isRecord(value) && value.code === "UNAUTHENTICATED";
|
|
96
104
|
}
|
|
105
|
+
function isAuthenticationUnavailable(value) {
|
|
106
|
+
return isRecord(value) && value.code === "INTERNAL";
|
|
107
|
+
}
|
|
97
108
|
var MAX_SUBSCRIBE_IDS = 500;
|
|
98
109
|
var MAX_ENTITY_IDS_PER_SOCKET = 1e4;
|
|
99
110
|
var MAX_SCOPES_PER_SOCKET = 1e3;
|
|
100
111
|
var MAX_TOPICS_PER_SOCKET = 1e3;
|
|
101
112
|
var MAX_SCOPE_LENGTH = 256;
|
|
113
|
+
var MAX_PEER_LINKS = 64;
|
|
114
|
+
var MAX_PEER_MESSAGE_BYTES = 65536;
|
|
115
|
+
var MAX_PEER_SIGNAL_LENGTH = 16384;
|
|
116
|
+
var MAX_PEER_CANDIDATE_LENGTH = 1024;
|
|
117
|
+
var PEER_SIGNAL_RATE = 20;
|
|
118
|
+
var PEER_SIGNAL_BURST = 40;
|
|
119
|
+
var PEER_RELAY_RATE = 120;
|
|
120
|
+
var PEER_RELAY_BURST = 240;
|
|
121
|
+
var PEER_RELAY_BYTES_PER_SECOND = 1048576;
|
|
122
|
+
var PEER_RELAY_BYTES_BURST = 2097152;
|
|
123
|
+
var PEER_RELAY_FAST_BACKLOG = 64;
|
|
102
124
|
|
|
103
125
|
export {
|
|
104
|
-
QUICKDRAW_VERSION,
|
|
105
126
|
entityRoom,
|
|
106
127
|
collectionRoom,
|
|
107
128
|
topicRoom,
|
|
108
129
|
SERVICE_TOPIC,
|
|
109
130
|
collectionTopic,
|
|
110
131
|
streamRoom,
|
|
132
|
+
STREAM_ROOM_PREFIX,
|
|
111
133
|
userRoom,
|
|
112
134
|
RESERVED_ROOM_PREFIXES,
|
|
113
135
|
reservedRoomPrefix,
|
|
@@ -119,10 +141,22 @@ export {
|
|
|
119
141
|
PROTOCOL_MISMATCH,
|
|
120
142
|
isProtocolMismatch,
|
|
121
143
|
isAuthenticationRefused,
|
|
144
|
+
isAuthenticationUnavailable,
|
|
122
145
|
MAX_SUBSCRIBE_IDS,
|
|
123
146
|
MAX_ENTITY_IDS_PER_SOCKET,
|
|
124
147
|
MAX_SCOPES_PER_SOCKET,
|
|
125
148
|
MAX_TOPICS_PER_SOCKET,
|
|
126
|
-
MAX_SCOPE_LENGTH
|
|
149
|
+
MAX_SCOPE_LENGTH,
|
|
150
|
+
MAX_PEER_LINKS,
|
|
151
|
+
MAX_PEER_MESSAGE_BYTES,
|
|
152
|
+
MAX_PEER_SIGNAL_LENGTH,
|
|
153
|
+
MAX_PEER_CANDIDATE_LENGTH,
|
|
154
|
+
PEER_SIGNAL_RATE,
|
|
155
|
+
PEER_SIGNAL_BURST,
|
|
156
|
+
PEER_RELAY_RATE,
|
|
157
|
+
PEER_RELAY_BURST,
|
|
158
|
+
PEER_RELAY_BYTES_PER_SECOND,
|
|
159
|
+
PEER_RELAY_BYTES_BURST,
|
|
160
|
+
PEER_RELAY_FAST_BACKLOG
|
|
127
161
|
};
|
|
128
|
-
//# sourceMappingURL=chunk-
|
|
162
|
+
//# sourceMappingURL=chunk-X4ZU4QH5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/contract/names.ts","../src/protocol/version.ts"],"sourcesContent":["// Room and event names for protocol v5 (RFC 0003 sections 6, 7 and 8). Ported\n// from the 4.1 room helpers (4.1 `src/shared/types.ts:364-405`), which were\n// plain template strings so shared code could name a room without holding a\n// service instance. The 5.0 rooms live under a `qd:` prefix so they never\n// collide with an app's own rooms; the per-user room keeps its 4.1 name.\n\nimport type { AccessLevel } from \"./access\";\n\n/**\n * The room for one entity at one access tier: `qd:e:{service}:{id}@{level}`.\n * A subscriber joins the room for its own level, so a frame is built once and\n * stripped once per occupied tier.\n */\nexport function entityRoom(service: string, id: string, level: AccessLevel): string {\n return `qd:e:${service}:${id}@${level}`;\n}\n\n/** The room for one scope of a collection: `qd:c:{service}:{collection}:{scope}`. */\nexport function collectionRoom(service: string, collection: string, scope: string): string {\n return `qd:c:${service}:${collection}:${scope}`;\n}\n\n/** The room for a watched topic of a service: `qd:t:{service}:{topic}`. */\nexport function topicRoom(service: string, topic: string): string {\n return `qd:t:${service}:${topic}`;\n}\n\n/**\n * The change topic of a whole service (RFC 0003 section 11.3): every flush\n * that changes one of its rows or collection scopes signals it.\n */\nexport const SERVICE_TOPIC = \"service\";\n\n/** The change topic of one scope of a collection: `{collection}:{scope}` (RFC 0003 section 11.3). */\nexport function collectionTopic(collection: string, scope: string): string {\n return `${collection}:${scope}`;\n}\n\n/**\n * The room of a stream's feed (RFC 0003 section 12.5):\n * `qd:s:{service}:{stream}:{scope}` for one scope of a scoped stream, and\n * `qd:s:{service}:{stream}` for a global stream. Stream names hold no colon.\n */\nexport function streamRoom(service: string, stream: string, scope?: string): string {\n return scope === undefined ? `qd:s:${service}:${stream}` : `qd:s:${service}:${stream}:${scope}`;\n}\n\n/**\n * The prefix every room {@link streamRoom} names starts with. The builder\n * spells it out, since docs/protocol-v5.md is generated from its template.\n */\nexport const STREAM_ROOM_PREFIX = \"qd:s:\";\n\n/** The room every authenticated socket of a user joins: `user:{userId}`, as in 4.1. */\nexport function userRoom(userId: string): string {\n return `user:${userId}`;\n}\n\n/**\n * The prefixes of the rooms the framework manages: entity, collection, topic\n * and stream rooms (`qd:`) and user rooms (`user:`). `ctx.rooms.join` refuses\n * them; a socket enters those rooms only through their authorized paths.\n */\nexport const RESERVED_ROOM_PREFIXES: readonly string[] = Object.freeze([\"qd:\", \"user:\"]);\n\n/**\n * The reserved prefix `room` starts with (`qd:` or `user:`), or `undefined`\n * when an app room may have that name.\n */\nexport function reservedRoomPrefix(room: string): string | undefined {\n return RESERVED_ROOM_PREFIXES.find((prefix) => room.startsWith(prefix));\n}\n\nconst SOCKET_ID = /^[\\w-]{20}$/u;\n\n/**\n * True when `room` has the shape of a Socket.IO socket id: 20 letters,\n * digits, `_` or `-`. Socket.IO puts every socket alone in a room named by\n * its id, 15 random bytes as base64url (the `base64id` package), so an app\n * room of that shape could be one socket's own room: `ctx.rooms.join`,\n * `rooms.size`, `rooms.leave` and presence refuse such names (the 5.1\n * audit's RT-I1), and no app room has that shape.\n */\nexport function socketIdShaped(room: string): boolean {\n return SOCKET_ID.test(room);\n}\n\n/** Events a client sends to the server (RFC 0003 section 8.2). */\nexport const CLIENT_EVENTS = Object.freeze({\n /** A method call: `{ id, s, m, i, v? }`, answered by ack. */\n call: \"qd:call\",\n /** Cancels an in-flight call: `{ id }`. */\n cancel: \"qd:cancel\",\n /** Subscribes to entities by id, with the revisions held. */\n sub: \"qd:sub\",\n /** Unsubscribes from entities. */\n unsub: \"qd:unsub\",\n /** Subscribes to a collection scope, or resumes it from a revision. */\n collectionSub: \"qd:col:sub\",\n /** Unsubscribes from a collection scope. */\n collectionUnsub: \"qd:col:unsub\",\n /** Loads collection items by id. */\n collectionItems: \"qd:col:items\",\n /** Joins a watched topic: `{ s, topic }`. */\n watch: \"qd:watch\",\n /** Leaves a watched topic. */\n unwatch: \"qd:unwatch\",\n /** Subscribes to a stream and receives its seed. */\n streamSub: \"qd:stream:sub\",\n /** Unsubscribes from a stream. */\n streamUnsub: \"qd:stream:unsub\",\n /** A fire-and-forget channel message, sent volatile: `[s, channel, payload]`. */\n channel: \"qd:ch\",\n /** A peer link's signal to another socket of an app room: `{ room, to, sig }`. */\n peer: \"qd:peer\",\n /** A peer link's message for the server to relay: `[room, to, label, text]`, dropped on `fast` when backed up. */\n relay: \"qd:relay\",\n} as const);\n\n/** Events the server sends to a client (RFC 0003 section 8.3). */\nexport const SERVER_EVENTS = Object.freeze({\n /** The handshake reply: `{ protocol, server, limits, features, userId, serviceAccess }`. */\n hello: \"qd:hello\",\n /** Entity frames: full update, patch or removal. */\n entity: \"qd:e\",\n /** Collection deltas, batched per flush. */\n collection: \"qd:c\",\n /** A watched topic changed; the client invalidates its queries. */\n changed: \"qd:changed\",\n /** Access to a subscription was revoked. */\n revoked: \"qd:revoked\",\n /** Stream items. */\n stream: \"qd:stream\",\n /** Typed custom room events declared in a contract's `events`. */\n event: \"qd:event\",\n /** Who is in an app room the socket is in: the list on joining, then each user who joins or leaves. */\n presence: \"qd:presence\",\n /** Reconnect within a jitter window. */\n rotate: \"qd:rotate\",\n /** The user's service grants changed. */\n access: \"qd:access\",\n /** A peer link's signal from another socket of an app room: `{ room, from, userId, sig }`. */\n peer: \"qd:peer\",\n /** A peer link's message the server relays: `[room, from, label, text]`. */\n relay: \"qd:relay\",\n /** An app room's host (host mode): `[room, host]`, the host's socket id or `null`. */\n host: \"qd:host\",\n} as const);\n\n/** The name of an event a client sends. */\nexport type ClientEventName = (typeof CLIENT_EVENTS)[keyof typeof CLIENT_EVENTS];\n\n/** The name of an event the server sends. */\nexport type ServerEventName = (typeof SERVER_EVENTS)[keyof typeof SERVER_EVENTS];\n","// The protocol version and the connection handshake (RFC 0003 section 8.1).\n// A v5 client connects with `auth: { token, qd: { protocol: 5, client } }`.\n// The server answers with `qd:hello`, or refuses a client that names another\n// protocol with `PROTOCOL_MISMATCH` in the `connect_error` data, and one whose\n// authentication failed with `UNAUTHENTICATED` there. A client that sends no\n// `auth.qd` is a 4.x client, which only the legacy shim serves (section 8.5).\n// There is no capability negotiation: the version decides.\n\nimport type { AccessLevel } from \"../contract/access\";\nimport { isRecord } from \"./guards\";\n\n/** The wire protocol this package speaks. */\nexport const PROTOCOL_VERSION = 5;\n\n/** `auth.qd`: what a v5 client sends when it connects. */\nexport interface QdHandshake {\n /** The protocol the client speaks. The server refuses any but its own. */\n readonly protocol: number;\n /** The client package's version, for logs. */\n readonly client: string;\n}\n\n/** The Socket.IO `auth` a v5 client connects with. */\nexport interface HandshakeAuth {\n /** The session token the server's `authenticate` reads. */\n readonly token?: string;\n /** The protocol the client speaks. A client without it is a 4.x client (RFC 0003 section 8.5). */\n readonly qd: QdHandshake;\n}\n\n/**\n * True when `value` has the shape of `auth.qd`, whatever protocol it names.\n * Compare `protocol` with {@link PROTOCOL_VERSION} to decide whether to serve it.\n */\nexport function isQdHandshake(value: unknown): value is QdHandshake {\n return isRecord(value) && Number.isInteger(value.protocol) && typeof value.client === \"string\";\n}\n\n/** The code in the `connect_error` data of a connection refused for its protocol. */\nexport const PROTOCOL_MISMATCH = \"PROTOCOL_MISMATCH\";\n\n/**\n * The `data` of the error a server refuses a connection with when the client\n * speaks another protocol, or none. It is not an `ErrorCode`: no call failed.\n */\nexport interface ProtocolMismatch {\n readonly code: typeof PROTOCOL_MISMATCH;\n /** The protocol the server speaks. */\n readonly expected: number;\n}\n\n/** True when a `connect_error`'s `data` says the server speaks another protocol. */\nexport function isProtocolMismatch(value: unknown): value is ProtocolMismatch {\n return isRecord(value) && value.code === PROTOCOL_MISMATCH && Number.isInteger(value.expected);\n}\n\n/**\n * The `data` of the error a server refuses a connection with when it refused\n * the credentials: the app's `authenticate` threw a `QuickdrawError` with\n * code `UNAUTHENTICATED` (or `FORBIDDEN`), or the session it recorded\n * expired during the handshake. A client tells it apart from\n * {@link ProtocolMismatch} by `code`; reconnecting needs other credentials,\n * so the user is signed out. A server that could not check the credentials\n * refuses with {@link AuthenticationUnavailable} instead.\n */\nexport interface AuthenticationRefused {\n readonly code: \"UNAUTHENTICATED\";\n}\n\n/** True when a `connect_error`'s `data` says the server refused the connection's credentials. */\nexport function isAuthenticationRefused(value: unknown): value is AuthenticationRefused {\n return isRecord(value) && value.code === \"UNAUTHENTICATED\";\n}\n\n/**\n * The `data` of the error a server refuses a connection with when it could\n * not authenticate it: the app's `authenticate` (or `loadServiceAccess`)\n * failed with anything but a refusal, its session store or database down,\n * say. The credentials may well be good, so a client keeps the user it had\n * and connects again later, backing off as after a lost connection.\n */\nexport interface AuthenticationUnavailable {\n readonly code: \"INTERNAL\";\n}\n\n/** True when a `connect_error`'s `data` says the server could not authenticate the connection. */\nexport function isAuthenticationUnavailable(value: unknown): value is AuthenticationUnavailable {\n return isRecord(value) && value.code === \"INTERNAL\";\n}\n\n/** The most ids one `qd:sub` may name (RFC 0003 section 6), as `qd:hello` announces it. */\nexport const MAX_SUBSCRIBE_IDS = 500;\n\n/** The most rows one socket may hold entity subscriptions to at once, as `qd:hello` announces it. */\nexport const MAX_ENTITY_IDS_PER_SOCKET = 10_000;\n\n/** The most collection scopes one socket may hold at once, as `qd:hello` announces it. */\nexport const MAX_SCOPES_PER_SOCKET = 1_000;\n\n/** The most change topics one socket may watch at once, as `qd:hello` announces it. */\nexport const MAX_TOPICS_PER_SOCKET = 1_000;\n\n/** The longest collection scope value or change topic a frame may name, in characters. */\nexport const MAX_SCOPE_LENGTH = 256;\n\n/** The most peers one socket may signal at once (`qd:peer`), as `qd:hello` announces it. */\nexport const MAX_PEER_LINKS = 64;\n\n/** The longest peer link message, in UTF-8 bytes, on the data channel and through the relay alike. */\nexport const MAX_PEER_MESSAGE_BYTES = 65_536;\n\n/** The longest session description a `qd:peer` signal carries, in characters. */\nexport const MAX_PEER_SIGNAL_LENGTH = 16_384;\n\n/** The longest string of a `qd:peer` candidate (`candidate`, `sdpMid`, `usernameFragment`), in characters. */\nexport const MAX_PEER_CANDIDATE_LENGTH = 1024;\n\n/** Signals one socket may send per second (`qd:peer`); more are refused `RATE_LIMITED`. */\nexport const PEER_SIGNAL_RATE = 20;\n\n/** Signals one socket may send at once, after a pause. */\nexport const PEER_SIGNAL_BURST = 40;\n\n/** Relay messages one socket may send to one peer per second (`qd:relay`); more are dropped. */\nexport const PEER_RELAY_RATE = 120;\n\n/** Relay messages one socket may send to one peer at once, after a pause. */\nexport const PEER_RELAY_BURST = 240;\n\n/** Relay bytes one socket may send per second, to all its peers together; more are dropped. */\nexport const PEER_RELAY_BYTES_PER_SECOND = 1_048_576;\n\n/** Relay bytes one socket may send at once, after a pause. */\nexport const PEER_RELAY_BYTES_BURST = 2_097_152;\n\n/** Relay messages waiting for a socket's connection past which the relay drops a `fast` one to it. */\nexport const PEER_RELAY_FAST_BACKLOG = 64;\n\n/**\n * Each socket's lane of subscription events (`qd:sub`, `qd:col:sub`,\n * `qd:col:items`, `qd:watch`; RFC 0003 section 8.2), and how much one\n * socket may hold at once, as `qd:hello` announces them.\n */\nexport interface HelloSubscriptionLimits {\n /** Subscription events one socket may have running at once. */\n readonly maxInFlight: number;\n /** Subscription events one socket may have waiting; past that one fails with `RATE_LIMITED`. */\n readonly maxQueued: number;\n /** Rows one socket may hold entity subscriptions to at once; one more is `CONFLICT`. Absent from servers up to 5.1.0, which have no such cap. */\n readonly maxEntityIds?: number;\n /** Collection scopes one socket may hold at once; one more is `CONFLICT`. Absent from servers up to 5.1.0, which have no such cap. */\n readonly maxScopes?: number;\n /** Change topics one socket may watch at once; one more is `CONFLICT`. Absent from servers up to 5.1.0, which have no such cap. */\n readonly maxTopics?: number;\n}\n\n/**\n * What one socket may signal and relay over peer links (RFC 0003 section\n * 12.5), as `qd:hello` announces it. A client keeps its relay messages\n * within these, so a `rel` message is never dropped for its rate.\n */\nexport interface HelloPeerLimits {\n /** Peers one socket may signal at once; a signal to one more is `CONFLICT`. */\n readonly maxLinks: number;\n /** The longest link message, in UTF-8 bytes, on the data channel and through the relay alike. */\n readonly maxMessageBytes: number;\n /** Relay messages one socket may send to one peer per second; more are dropped. */\n readonly relayRate: number;\n /** Relay messages one socket may send to one peer at once, after a pause. */\n readonly relayBurst: number;\n /** Relay bytes one socket may send per second, to all its peers together; more are dropped. */\n readonly relayBytesPerSecond: number;\n /** Relay bytes one socket may send at once, after a pause. */\n readonly relayBytesBurst: number;\n}\n\n/** The limits a server announces in `qd:hello`, so a client can stay inside them. */\nexport interface HelloLimits {\n /** Queries one socket may have running at once; more wait in a queue (RFC 0003 section 9). */\n readonly maxInFlightQueries: number;\n /** Queries one socket may have waiting; past that a query fails with `RATE_LIMITED`. */\n readonly maxQueuedQueries: number;\n /** Ids one `qd:sub` may name (RFC 0003 section 6). */\n readonly maxSubscribeIds: number;\n /**\n * A call's default time limit in milliseconds, after which it fails with\n * `TIMEOUT`. A v5 client waits this long plus 2 s for an answer, so a slow\n * call ends with the server's `TIMEOUT` rather than its own.\n */\n readonly callTimeoutMs: number;\n /** The socket's lane of subscription events. */\n readonly subscriptions: HelloSubscriptionLimits;\n /** What peer links may signal and relay. Absent from a server that serves no peer links, whose `features` lacks `\"peer\"`. */\n readonly peers?: HelloPeerLimits;\n}\n\n/**\n * `qd:hello`: the server's answer to a v5 handshake, sent once per\n * connection, after the socket's listeners are in place. It says who the\n * socket acts for, so a client knows its user without another call.\n */\nexport interface HelloFrame {\n /** The protocol the server speaks. */\n readonly protocol: typeof PROTOCOL_VERSION;\n /** The server package's version. */\n readonly server: string;\n /**\n * This server's id: random, new each time a server starts. A client that\n * reconnects to the same id came back after a blip, to another id to a\n * restarted server (a game's world and clock start over) or, behind a\n * cluster, to another node, each of which has its own.\n */\n readonly serverId: string;\n /** What a client must stay within. */\n readonly limits: HelloLimits;\n /**\n * Names of optional server features that are on: `\"binary\"` (the stock\n * parser, `createServer({ binary: true })`) and `\"peer\"` (peer links,\n * `createServer({ peers: true })`). Informational: a client that finds no\n * `\"peer\"` opens no peer link.\n */\n readonly features: readonly string[];\n /** The user the socket acts for, or `null` when it is anonymous. */\n readonly userId: string | null;\n /**\n * The principal's service-wide grants when the socket connected, by\n * service name: `{ taskService: \"Admin\" }`. Empty for an anonymous socket\n * or a principal without grants. Later changes arrive as `qd:access`.\n */\n readonly serviceAccess: Readonly<Record<string, AccessLevel>>;\n}\n"],"mappings":";;;;;AAaO,SAAS,WAAW,SAAiB,IAAY,OAA4B;AAClF,SAAO,QAAQ,OAAO,IAAI,EAAE,IAAI,KAAK;AACvC;AAGO,SAAS,eAAe,SAAiB,YAAoB,OAAuB;AACzF,SAAO,QAAQ,OAAO,IAAI,UAAU,IAAI,KAAK;AAC/C;AAGO,SAAS,UAAU,SAAiB,OAAuB;AAChE,SAAO,QAAQ,OAAO,IAAI,KAAK;AACjC;AAMO,IAAM,gBAAgB;AAGtB,SAAS,gBAAgB,YAAoB,OAAuB;AACzE,SAAO,GAAG,UAAU,IAAI,KAAK;AAC/B;AAOO,SAAS,WAAW,SAAiB,QAAgB,OAAwB;AAClF,SAAO,UAAU,SAAY,QAAQ,OAAO,IAAI,MAAM,KAAK,QAAQ,OAAO,IAAI,MAAM,IAAI,KAAK;AAC/F;AAMO,IAAM,qBAAqB;AAG3B,SAAS,SAAS,QAAwB;AAC/C,SAAO,QAAQ,MAAM;AACvB;AAOO,IAAM,yBAA4C,OAAO,OAAO,CAAC,OAAO,OAAO,CAAC;AAMhF,SAAS,mBAAmB,MAAkC;AACnE,SAAO,uBAAuB,KAAK,CAAC,WAAW,KAAK,WAAW,MAAM,CAAC;AACxE;AAEA,IAAM,YAAY;AAUX,SAAS,eAAe,MAAuB;AACpD,SAAO,UAAU,KAAK,IAAI;AAC5B;AAGO,IAAM,gBAAgB,OAAO,OAAO;AAAA;AAAA,EAEzC,MAAM;AAAA;AAAA,EAEN,QAAQ;AAAA;AAAA,EAER,KAAK;AAAA;AAAA,EAEL,OAAO;AAAA;AAAA,EAEP,eAAe;AAAA;AAAA,EAEf,iBAAiB;AAAA;AAAA,EAEjB,iBAAiB;AAAA;AAAA,EAEjB,OAAO;AAAA;AAAA,EAEP,SAAS;AAAA;AAAA,EAET,WAAW;AAAA;AAAA,EAEX,aAAa;AAAA;AAAA,EAEb,SAAS;AAAA;AAAA,EAET,MAAM;AAAA;AAAA,EAEN,OAAO;AACT,CAAU;AAGH,IAAM,gBAAgB,OAAO,OAAO;AAAA;AAAA,EAEzC,OAAO;AAAA;AAAA,EAEP,QAAQ;AAAA;AAAA,EAER,YAAY;AAAA;AAAA,EAEZ,SAAS;AAAA;AAAA,EAET,SAAS;AAAA;AAAA,EAET,QAAQ;AAAA;AAAA,EAER,OAAO;AAAA;AAAA,EAEP,UAAU;AAAA;AAAA,EAEV,QAAQ;AAAA;AAAA,EAER,QAAQ;AAAA;AAAA,EAER,MAAM;AAAA;AAAA,EAEN,OAAO;AAAA;AAAA,EAEP,MAAM;AACR,CAAU;;;ACvIH,IAAM,mBAAmB;AAsBzB,SAAS,cAAc,OAAsC;AAClE,SAAO,SAAS,KAAK,KAAK,OAAO,UAAU,MAAM,QAAQ,KAAK,OAAO,MAAM,WAAW;AACxF;AAGO,IAAM,oBAAoB;AAa1B,SAAS,mBAAmB,OAA2C;AAC5E,SAAO,SAAS,KAAK,KAAK,MAAM,SAAS,qBAAqB,OAAO,UAAU,MAAM,QAAQ;AAC/F;AAgBO,SAAS,wBAAwB,OAAgD;AACtF,SAAO,SAAS,KAAK,KAAK,MAAM,SAAS;AAC3C;AAcO,SAAS,4BAA4B,OAAoD;AAC9F,SAAO,SAAS,KAAK,KAAK,MAAM,SAAS;AAC3C;AAGO,IAAM,oBAAoB;AAG1B,IAAM,4BAA4B;AAGlC,IAAM,wBAAwB;AAG9B,IAAM,wBAAwB;AAG9B,IAAM,mBAAmB;AAGzB,IAAM,iBAAiB;AAGvB,IAAM,yBAAyB;AAG/B,IAAM,yBAAyB;AAG/B,IAAM,4BAA4B;AAGlC,IAAM,mBAAmB;AAGzB,IAAM,oBAAoB;AAG1B,IAAM,kBAAkB;AAGxB,IAAM,mBAAmB;AAGzB,IAAM,8BAA8B;AAGpC,IAAM,yBAAyB;AAG/B,IAAM,0BAA0B;","names":[]}
|