@fitzzero/quickdraw-core 5.1.1 → 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.
Files changed (152) hide show
  1. package/README.md +1308 -219
  2. package/dist/{admin-DKwkIh5i.d.ts → admin-CS7JB_UH.d.ts} +2 -2
  3. package/dist/{adminSchemas-2DUA_hZ8.d.ts → adminSchemas-29cGgrg5.d.ts} +6 -4
  4. package/dist/{auth-BhWqdyb-.d.ts → auth-DtdYjrZ9.d.ts} +51 -21
  5. package/dist/{caller-CXTFswBO.d.ts → caller-BTwGl7vm.d.ts} +296 -33
  6. package/dist/chunk-2FDSKLBO.js +82 -0
  7. package/dist/chunk-2FDSKLBO.js.map +1 -0
  8. package/dist/{chunk-RT6OILOI.js → chunk-4D2DOGAW.js} +2 -14
  9. package/dist/chunk-4D2DOGAW.js.map +1 -0
  10. package/dist/{chunk-OECZPRPW.js → chunk-64I6MYBC.js} +162 -775
  11. package/dist/chunk-64I6MYBC.js.map +1 -0
  12. package/dist/chunk-7473HK24.js +17 -0
  13. package/dist/chunk-7473HK24.js.map +1 -0
  14. package/dist/chunk-7Y5ROPXL.js +33 -0
  15. package/dist/chunk-7Y5ROPXL.js.map +1 -0
  16. package/dist/chunk-A2RBQD66.js +7 -0
  17. package/dist/chunk-A2RBQD66.js.map +1 -0
  18. package/dist/chunk-AK23XSWD.js +16 -0
  19. package/dist/chunk-AK23XSWD.js.map +1 -0
  20. package/dist/chunk-ASAMUGHG.js +651 -0
  21. package/dist/chunk-ASAMUGHG.js.map +1 -0
  22. package/dist/{chunk-B2P7AZ25.js → chunk-CYE4UBOB.js} +6 -4
  23. package/dist/chunk-CYE4UBOB.js.map +1 -0
  24. package/dist/chunk-DKE6QO26.js +136 -0
  25. package/dist/chunk-DKE6QO26.js.map +1 -0
  26. package/dist/chunk-DNP65TD5.js +832 -0
  27. package/dist/chunk-DNP65TD5.js.map +1 -0
  28. package/dist/{chunk-H23KEFBA.js → chunk-DUBU3BB3.js} +30 -1
  29. package/dist/chunk-DUBU3BB3.js.map +1 -0
  30. package/dist/chunk-GYLWEJHW.js +19 -0
  31. package/dist/chunk-GYLWEJHW.js.map +1 -0
  32. package/dist/{chunk-SHEGLZEP.js → chunk-I3JCFS7H.js} +34 -13
  33. package/dist/chunk-I3JCFS7H.js.map +1 -0
  34. package/dist/{chunk-X5CWPX7H.js → chunk-IIQ3WMJK.js} +13 -2
  35. package/dist/chunk-IIQ3WMJK.js.map +1 -0
  36. package/dist/{chunk-RZTP4IEW.js → chunk-JVTONQFI.js} +6339 -4226
  37. package/dist/chunk-JVTONQFI.js.map +1 -0
  38. package/dist/{chunk-QEKVYG5F.js → chunk-KKQANRLG.js} +2 -2
  39. package/dist/chunk-KKQANRLG.js.map +1 -0
  40. package/dist/{chunk-CDKG4TFA.js → chunk-MPQAT55F.js} +38 -205
  41. package/dist/chunk-MPQAT55F.js.map +1 -0
  42. package/dist/chunk-NO4M6PO5.js +101 -0
  43. package/dist/chunk-NO4M6PO5.js.map +1 -0
  44. package/dist/{chunk-TBMKJ26R.js → chunk-NW6U45HX.js} +3 -1
  45. package/dist/chunk-NW6U45HX.js.map +1 -0
  46. package/dist/chunk-NX2V6SIL.js +202 -0
  47. package/dist/chunk-NX2V6SIL.js.map +1 -0
  48. package/dist/{chunk-ZIVPHYRG.js → chunk-NZHA4TZD.js} +72 -175
  49. package/dist/chunk-NZHA4TZD.js.map +1 -0
  50. package/dist/{chunk-2PWMUVC7.js → chunk-OCZJ4EYV.js} +518 -27
  51. package/dist/chunk-OCZJ4EYV.js.map +1 -0
  52. package/dist/{chunk-REX5YPHX.js → chunk-PHCURHC7.js} +76 -21
  53. package/dist/chunk-PHCURHC7.js.map +1 -0
  54. package/dist/{chunk-4YO5H5ON.js → chunk-Q4OGQM5I.js} +4 -10
  55. package/dist/chunk-Q4OGQM5I.js.map +1 -0
  56. package/dist/chunk-QSU47XEX.js +13 -0
  57. package/dist/chunk-QSU47XEX.js.map +1 -0
  58. package/dist/{chunk-DD4SKCNO.js → chunk-RZXJTDH5.js} +13 -2
  59. package/dist/chunk-RZXJTDH5.js.map +1 -0
  60. package/dist/{chunk-Z2LZ7IZ7.js → chunk-SN3OZVEQ.js} +5 -31
  61. package/dist/chunk-SN3OZVEQ.js.map +1 -0
  62. package/dist/{chunk-4DIQHASK.js → chunk-SQ5VUG77.js} +28 -8
  63. package/dist/chunk-SQ5VUG77.js.map +1 -0
  64. package/dist/chunk-STH5DLQP.js +1192 -0
  65. package/dist/chunk-STH5DLQP.js.map +1 -0
  66. package/dist/{chunk-QCHKWIRS.js → chunk-VZFJTKIW.js} +13 -13
  67. package/dist/{chunk-QCHKWIRS.js.map → chunk-VZFJTKIW.js.map} +1 -1
  68. package/dist/{chunk-VSQBHPWH.js → chunk-X4ZU4QH5.js} +54 -22
  69. package/dist/chunk-X4ZU4QH5.js.map +1 -0
  70. package/dist/chunk-XD7SRDUY.js +188 -0
  71. package/dist/chunk-XD7SRDUY.js.map +1 -0
  72. package/dist/chunk-XH2IZVLT.js +62 -0
  73. package/dist/chunk-XH2IZVLT.js.map +1 -0
  74. package/dist/chunk-Z7I6SONO.js +825 -0
  75. package/dist/chunk-Z7I6SONO.js.map +1 -0
  76. package/dist/cli/quickdraw-docs.js +132 -112
  77. package/dist/cli/quickdraw-docs.js.map +1 -1
  78. package/dist/client/core.d.ts +387 -0
  79. package/dist/client/core.js +109 -0
  80. package/dist/client/index.d.ts +52 -203
  81. package/dist/client/index.js +59 -218
  82. package/dist/client/index.js.map +1 -1
  83. package/dist/{clientTypes-Bc-G8n-U.d.ts → clientTypes-CUUNJwWs.d.ts} +3 -3
  84. package/dist/{connection-Cr1BU07W.d.ts → connection-2EeQ_Et0.d.ts} +22 -11
  85. package/dist/{createServer-CP9eDHZZ.d.ts → createServer-Bx5HOcYK.d.ts} +67 -17
  86. package/dist/{createTestApp-UKVGEmOf.d.ts → createTestApp-DAMANT79.d.ts} +46 -13
  87. package/dist/{envelope-Cp3YcVxC.d.ts → envelope-BMBaxppV.d.ts} +216 -10
  88. package/dist/{http-Djj2Inuu.d.ts → http-YzLAar3w.d.ts} +76 -60
  89. package/dist/index.d.ts +8 -7
  90. package/dist/index.js +82 -25
  91. package/dist/index.js.map +1 -1
  92. package/dist/{infer-Teo1xpsd.d.ts → infer-Br62cnyW.d.ts} +82 -11
  93. package/dist/logger-Yz93KzdV.d.ts +30 -0
  94. package/dist/{metrics-BdBu5o8G.d.ts → metrics-CTVpw0gu.d.ts} +2 -2
  95. package/dist/origins-kvpxnVcW.d.ts +8 -0
  96. package/dist/parser.js +2 -1
  97. package/dist/prisma/index.d.ts +14 -7
  98. package/dist/prisma/index.js +279 -70
  99. package/dist/prisma/index.js.map +1 -1
  100. package/dist/{rateLimit-ZMIZJUZF.js → rateLimit-5RKBFLUR.js} +2 -2
  101. package/dist/rateLimit-5RKBFLUR.js.map +1 -0
  102. package/dist/remote-N7OUAGOW.js +15 -0
  103. package/dist/remote-N7OUAGOW.js.map +1 -0
  104. package/dist/server/auth/index.d.ts +82 -33
  105. package/dist/server/auth/index.js +277 -270
  106. package/dist/server/auth/index.js.map +1 -1
  107. package/dist/server/express/index.d.ts +9 -2
  108. package/dist/server/express/index.js +1 -1
  109. package/dist/server/index.d.ts +268 -84
  110. package/dist/server/index.js +664 -418
  111. package/dist/server/index.js.map +1 -1
  112. package/dist/server/mcp/index.d.ts +89 -27
  113. package/dist/server/mcp/index.js +281 -62
  114. package/dist/server/mcp/index.js.map +1 -1
  115. package/dist/server/otel.d.ts +3 -3
  116. package/dist/{sharing-DVa8W-yR.d.ts → sharing-BrsmlDv_.d.ts} +3 -3
  117. package/dist/testing/client.d.ts +14 -14
  118. package/dist/testing/client.js +19 -10
  119. package/dist/testing/client.js.map +1 -1
  120. package/dist/testing/index.d.ts +50 -13
  121. package/dist/testing/index.js +113 -21
  122. package/dist/testing/index.js.map +1 -1
  123. package/dist/testing/mock.d.ts +5 -5
  124. package/dist/testing/mock.js +17 -9
  125. package/dist/tracked-BR5b2O-U.d.ts +43 -0
  126. package/dist/{types-Cu0vmx7c.d.ts → types-CLRCQGnO.d.ts} +4 -3
  127. package/dist/types-tMagHbjy.d.ts +151 -0
  128. package/dist/utils/index.d.ts +16 -4
  129. package/dist/utils/index.js +6 -3
  130. package/package.json +5 -1
  131. package/dist/chunk-2PWMUVC7.js.map +0 -1
  132. package/dist/chunk-4DIQHASK.js.map +0 -1
  133. package/dist/chunk-4YO5H5ON.js.map +0 -1
  134. package/dist/chunk-B2P7AZ25.js.map +0 -1
  135. package/dist/chunk-CDKG4TFA.js.map +0 -1
  136. package/dist/chunk-DD4SKCNO.js.map +0 -1
  137. package/dist/chunk-H23KEFBA.js.map +0 -1
  138. package/dist/chunk-OECZPRPW.js.map +0 -1
  139. package/dist/chunk-QEKVYG5F.js.map +0 -1
  140. package/dist/chunk-REX5YPHX.js.map +0 -1
  141. package/dist/chunk-RT6OILOI.js.map +0 -1
  142. package/dist/chunk-RZTP4IEW.js.map +0 -1
  143. package/dist/chunk-SHEGLZEP.js.map +0 -1
  144. package/dist/chunk-TBMKJ26R.js.map +0 -1
  145. package/dist/chunk-VSQBHPWH.js.map +0 -1
  146. package/dist/chunk-X5CWPX7H.js.map +0 -1
  147. package/dist/chunk-YSWH776Y.js +0 -13
  148. package/dist/chunk-YSWH776Y.js.map +0 -1
  149. package/dist/chunk-Z2LZ7IZ7.js.map +0 -1
  150. package/dist/chunk-ZIVPHYRG.js.map +0 -1
  151. package/dist/logger-BY28acW-.d.ts +0 -16
  152. /package/dist/{rateLimit-ZMIZJUZF.js.map → client/core.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-CDKG4TFA.js";
26
- import {
27
- ACCESS_LEVELS,
28
- isAccessLevel
29
- } from "./chunk-VSQBHPWH.js";
29
+ } from "./chunk-MPQAT55F.js";
30
30
  import {
31
31
  isStandardSchema
32
32
  } from "./chunk-47AEXVDM.js";
@@ -411,16 +411,16 @@ function sharingSpecOf(method) {
411
411
  return typeof method === "object" && method !== null ? SPECS2.get(method) : void 0;
412
412
  }
413
413
  var DESCRIBE2 = Object.freeze({
414
- share: "Gives a user a level on one row: Read, Moderate or Admin. A user the row is shared with already gets the new level.",
415
- shareByName: "Gives the user with this name or email a level on one row: Read, Moderate or Admin.",
416
- unshare: "Takes a user's level on one row away. The owner's cannot be taken away.",
417
- setLevel: "Changes the level of a user one row is already shared with.",
414
+ share: "Gives a user a level on one row: Read, Moderate or Admin. A user the row is shared with already gets the new level, unless theirs is above yours.",
415
+ shareByName: "Gives the user this name or email finds a level on one row: Read, Moderate or Admin. Use the email when several users may share the name.",
416
+ unshare: "Takes a user's level on one row away. The owner's cannot be taken away, nor that of a user whose level is above yours.",
417
+ setLevel: "Changes the level of a user one row is already shared with, unless their level is above yours.",
418
418
  listShares: "Lists the users one row is shared with, and their levels.",
419
419
  invite: "Makes a user a member of one row, with a role (the lowest role when none is given).",
420
- inviteByName: "Makes the user with this name or email a member of one row, with a role (the lowest role when none is given).",
421
- remove: "Ends a user's membership of one row. The last Admin member cannot be removed.",
420
+ inviteByName: "Makes the user this name or email finds a member of one row, with a role (the lowest role when none is given). Use the email when several users may share the name.",
421
+ remove: "Ends a user's membership of one row. The last Admin member cannot be removed, nor a member whose level is above yours.",
422
422
  leave: "Ends the caller's own membership of one row. The last Admin member cannot leave.",
423
- setRole: "Changes a member's role on one row.",
423
+ setRole: "Changes a member's role on one row, unless their level is above yours.",
424
424
  listMembers: "Lists the members of one row and their roles, a page at a time. Pass a page's nextCursor back as cursor for the next page."
425
425
  });
426
426
  var BUILD = Object.freeze({
@@ -513,4 +513,4 @@ export {
513
513
  sharingSpecOf,
514
514
  sharing
515
515
  };
516
- //# sourceMappingURL=chunk-QCHKWIRS.js.map
516
+ //# sourceMappingURL=chunk-VZFJTKIW.js.map