@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,202 @@
1
+ import {
2
+ isTimeLimit,
3
+ mayConnect,
4
+ notifyEach,
5
+ retryAfterOf
6
+ } from "./chunk-STH5DLQP.js";
7
+ import {
8
+ CLIENT_EVENTS,
9
+ reservedRoomPrefix,
10
+ socketIdShaped
11
+ } from "./chunk-X4ZU4QH5.js";
12
+ import {
13
+ QuickdrawError,
14
+ fromWire
15
+ } from "./chunk-Q4OGQM5I.js";
16
+ import {
17
+ isRecord
18
+ } from "./chunk-QSU47XEX.js";
19
+
20
+ // src/client/call.ts
21
+ var UNSENT_TIMEOUT = "operation has timed out";
22
+ function isNotModified(result) {
23
+ return result.nm === true;
24
+ }
25
+ function cancelledError() {
26
+ return new QuickdrawError("CANCELLED", "The call was cancelled");
27
+ }
28
+ function internalError(message) {
29
+ return new QuickdrawError("INTERNAL", message);
30
+ }
31
+ var lostInFlight = /* @__PURE__ */ new WeakSet();
32
+ function isUnknownOutcome(error) {
33
+ return error instanceof QuickdrawError && (error.code === "TIMEOUT" || lostInFlight.has(error));
34
+ }
35
+ function lostError() {
36
+ const error = internalError("No answer: the connection to the server is down");
37
+ lostInFlight.add(error);
38
+ return error;
39
+ }
40
+ function refusedBeforeSending(connection, kind, signal) {
41
+ if (signal?.aborted === true) {
42
+ return cancelledError();
43
+ }
44
+ if (!connection.socket.connected && !mayConnect(connection)) {
45
+ return internalError("Not connected to the server");
46
+ }
47
+ const remaining = connection.backoffRemaining(kind);
48
+ if (remaining > 0) {
49
+ return new QuickdrawError(
50
+ "RATE_LIMITED",
51
+ `Waiting before more ${kind} calls: the server answered RATE_LIMITED`,
52
+ { retryAfterMs: Math.ceil(remaining) }
53
+ );
54
+ }
55
+ return void 0;
56
+ }
57
+ function envelopeOf(id, request) {
58
+ return {
59
+ id,
60
+ s: request.service,
61
+ m: request.method,
62
+ ...request.input === void 0 ? {} : { i: request.input },
63
+ ...request.v === void 0 ? {} : { v: request.v }
64
+ };
65
+ }
66
+ function readReply(connection, kind, reply) {
67
+ if (isRecord(reply) && reply.ok === true) {
68
+ const versioned = typeof reply.v === "string" || typeof reply.v === "number";
69
+ if (reply.nm === true && !versioned) {
70
+ return internalError("The server answered not modified without a version");
71
+ }
72
+ return reply;
73
+ }
74
+ if (isRecord(reply) && reply.ok === false) {
75
+ const error = fromWire(reply.e);
76
+ if (error.code === "RATE_LIMITED") {
77
+ connection.reportRateLimited(kind, retryAfterOf(error));
78
+ }
79
+ return error;
80
+ }
81
+ return internalError("The server's reply is not a call reply");
82
+ }
83
+ function isQueuedCall(data, id) {
84
+ return Array.isArray(data) && data[0] === CLIENT_EVENTS.call && isRecord(data[1]) && data[1].id === id;
85
+ }
86
+ function cancelCall(socket, id) {
87
+ const queued = socket.sendBuffer.findIndex((packet) => isQueuedCall(packet.data, id));
88
+ if (queued >= 0) {
89
+ socket.sendBuffer.splice(queued, 1);
90
+ } else if (socket.connected) {
91
+ socket.emit(CLIENT_EVENTS.cancel, { id });
92
+ }
93
+ }
94
+ function call(connection, request) {
95
+ const kind = request.kind ?? "query";
96
+ const { signal } = request;
97
+ const refused = refusedBeforeSending(connection, kind, signal);
98
+ if (refused !== void 0) {
99
+ return Promise.reject(refused);
100
+ }
101
+ const { socket } = connection;
102
+ const timeoutMs = request.timeoutMs ?? connection.timeoutMs;
103
+ if (!isTimeLimit(timeoutMs)) {
104
+ return Promise.reject(
105
+ new TypeError(
106
+ "call: timeoutMs must be a number of milliseconds, above 0 and at most 2^31 - 1"
107
+ )
108
+ );
109
+ }
110
+ const id = connection.nextCallId();
111
+ return new Promise((resolve, reject) => {
112
+ let settled = false;
113
+ const settle = (outcome) => {
114
+ if (settled) {
115
+ return;
116
+ }
117
+ settled = true;
118
+ signal?.removeEventListener("abort", onAbort);
119
+ if (outcome instanceof QuickdrawError) {
120
+ reject(outcome);
121
+ } else {
122
+ notifyEach([request.onReply], (onReply) => {
123
+ onReply?.(outcome);
124
+ });
125
+ resolve(outcome);
126
+ }
127
+ };
128
+ function onAbort() {
129
+ cancelCall(socket, id);
130
+ settle(cancelledError());
131
+ }
132
+ socket.timeout(timeoutMs).emit(CLIENT_EVENTS.call, envelopeOf(id, request), (error, reply) => {
133
+ if (error === null) {
134
+ settle(readReply(connection, kind, reply));
135
+ } else if (error instanceof QuickdrawError) {
136
+ settle(error);
137
+ } else if (socket.connected) {
138
+ settle(new QuickdrawError("TIMEOUT", `No answer within ${timeoutMs} ms`));
139
+ } else if (error.message === UNSENT_TIMEOUT) {
140
+ settle(internalError("No answer: the connection to the server is down"));
141
+ } else {
142
+ settle(lostError());
143
+ }
144
+ });
145
+ signal?.addEventListener("abort", onAbort, { once: true });
146
+ });
147
+ }
148
+ async function callData(connection, request) {
149
+ const result = await call(connection, request);
150
+ if (isNotModified(result)) {
151
+ throw internalError("The server answered not modified to a call that sent no version");
152
+ }
153
+ return result.d;
154
+ }
155
+ function shouldRetry(failureCount, error) {
156
+ return failureCount < 1 && (!(error instanceof QuickdrawError) || error.code === "INTERNAL");
157
+ }
158
+
159
+ // src/client/host/rooms.ts
160
+ function appRoom(value) {
161
+ return typeof value === "string" && value !== "" && reservedRoomPrefix(value) === void 0 && !socketIdShaped(value) ? value : void 0;
162
+ }
163
+ function computed(room, value) {
164
+ try {
165
+ return appRoom(room(value));
166
+ } catch {
167
+ return void 0;
168
+ }
169
+ }
170
+ function channelRoomOf(def, payload, rooms) {
171
+ const room = def.requires?.room;
172
+ if (typeof room === "string") {
173
+ return room;
174
+ }
175
+ if (typeof room === "function") {
176
+ return computed(room, payload);
177
+ }
178
+ if (typeof room === "object") {
179
+ for (const known of rooms) {
180
+ if (known.startsWith(room.prefix)) {
181
+ return known;
182
+ }
183
+ }
184
+ }
185
+ return void 0;
186
+ }
187
+ function streamRoomOf(def, scope) {
188
+ const access = typeof def.access === "object" ? def.access : void 0;
189
+ const room = access?.room;
190
+ return typeof room === "function" ? computed(room, scope) : void 0;
191
+ }
192
+
193
+ export {
194
+ isNotModified,
195
+ isUnknownOutcome,
196
+ call,
197
+ callData,
198
+ shouldRetry,
199
+ channelRoomOf,
200
+ streamRoomOf
201
+ };
202
+ //# sourceMappingURL=chunk-NX2V6SIL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client/call.ts","../src/client/host/rooms.ts"],"sourcesContent":["// One method call over a client connection (RFC 0003 sections 8.2, 9 and\n// 11.2): a `qd:call` envelope with a fresh id, answered through Socket.IO's\n// acknowledgement. 4.1 sent one event per method and resolved the\n// `{ success, data }` reply (4.1 `src/client/useServiceQuery.ts:94-121`).\n//\n// - A failed call rejects with the `QuickdrawError` the reply carries.\n// `RATE_LIMITED` also starts the connection's backoff for the call's kind,\n// and while it lasts a call of that kind fails at once, without being\n// sent, so a client that is being limited stops spending the budget.\n// - Aborting the signal rejects with `CANCELLED` and sends `qd:cancel`; a\n// call still waiting in Socket.IO's send buffer is dropped instead, so it\n// never reaches the server.\n// - No answer within the time limit rejects with `TIMEOUT`. It is never\n// retried here: the server may still be running the call.\n// - A connection that drops before the answer, or is not open at all,\n// rejects with `INTERNAL`. A call made while the socket reconnects, or\n// while the connection waits to connect again after the server could not\n// authenticate it (`mayConnect`), waits in Socket.IO's send buffer, within\n// its time limit; new credentials meanwhile fail it with `CANCELLED`\n// instead of sending it as them (`connection.ts`).\n// - A `TIMEOUT`, and the `INTERNAL` of a call whose connection dropped after\n// it was sent, leave its outcome unknown: the server may have run it, and\n// may still be running it. `isUnknownOutcome` tells them from refusals, so\n// an optimistic addition waits for its scope's next load instead of being\n// refused (`optimisticCall.ts`).\n// - A reply `{ ok: true, nm: true, v }` resolves as it is: the version the\n// caller sent is current, and the caller keeps its copy.\n//\n// React-free: the hooks call through here, and so can any other client.\n\nimport type { MethodKind } from \"../contract/methods\";\nimport { CLIENT_EVENTS } from \"../contract/names\";\nimport type {\n CallEnvelope,\n CallId,\n CallNotModified,\n CallSuccess,\n Version,\n} from \"../protocol/envelope\";\nimport { QuickdrawError, fromWire } from \"../protocol/errors\";\nimport { isRecord } from \"../protocol/guards\";\nimport { retryAfterOf } from \"./backoff\";\nimport {\n isTimeLimit,\n mayConnect,\n type QuickdrawConnection,\n type QuickdrawSocket,\n} from \"./connection\";\nimport { notifyEach } from \"./watch\";\n\n/**\n * What Socket.IO's client fails an acknowledgement with when its time limit\n * passes; with the socket down, the packet was still in its send buffer,\n * which drops it. Any other failure while the socket is down (it says \"socket\n * has been disconnected\") came after the packet was sent.\n */\nconst UNSENT_TIMEOUT = \"operation has timed out\";\n\n/** One call, as {@link call} takes it. */\nexport interface CallRequest {\n /** The service's name on the wire: the contract's `name`. */\n readonly service: string;\n readonly method: string;\n /** The input; left out of the envelope when `undefined`. */\n readonly input?: unknown;\n /** The method's kind, which picks the backoff a `RATE_LIMITED` answer starts. Default `\"query\"`. */\n readonly kind?: MethodKind;\n /** Aborting it cancels the call. */\n readonly signal?: AbortSignal;\n /** How long to wait for the answer. Default: the connection's `timeoutMs`. */\n readonly timeoutMs?: number;\n /** The version of the result the caller holds; the server answers \"not modified\" while it is current. */\n readonly v?: Version;\n /**\n * Called with the result the moment the reply arrives, before any frame\n * the server sent after it is handled; the returned promise settles only\n * after more microtasks, and a client on Node can handle several frames in\n * between. What must follow reply order goes here: an optimistic layer\n * ends at the first newer frame after its call's reply.\n */\n readonly onReply?: (result: CallResult) => void;\n}\n\n/** What a call resolves with: the data, or \"not modified\" when the `v` it sent is current. */\nexport type CallResult<Output = unknown> = CallSuccess<Output> | CallNotModified;\n\n/** True when the server answered that the caller's version is current. */\nexport function isNotModified(result: CallResult): result is CallNotModified {\n return result.nm === true;\n}\n\n/** The error a cancelled call rejects with. */\nfunction cancelledError(): QuickdrawError {\n return new QuickdrawError(\"CANCELLED\", \"The call was cancelled\");\n}\n\nfunction internalError(message: string): QuickdrawError {\n return new QuickdrawError(\"INTERNAL\", message);\n}\n\n/** The errors of calls whose connection dropped after they were sent: their outcome is unknown. */\nconst lostInFlight = new WeakSet<QuickdrawError>();\n\n/**\n * True when `error` leaves a call's outcome unknown: a `TIMEOUT` (the\n * server may still be running it, or ran it and the answer was lost), or the\n * `INTERNAL` of a call whose connection dropped after it was sent. The write\n * may have been made: before sending it again, make sure the call is\n * idempotent (a create that carries an id the client made, which the server\n * keeps, fails `CONFLICT` the second time instead of writing a second row).\n * False for a refusal, and for a call that was never sent.\n *\n * @example\n * onError: (error) => setStatus(isUnknownOutcome(error) ? \"checking\" : \"failed\")\n */\nexport function isUnknownOutcome(error: unknown): boolean {\n return error instanceof QuickdrawError && (error.code === \"TIMEOUT\" || lostInFlight.has(error));\n}\n\n/** The error of a call whose connection dropped after it was sent. */\nfunction lostError(): QuickdrawError {\n const error = internalError(\"No answer: the connection to the server is down\");\n lostInFlight.add(error);\n return error;\n}\n\n/** Why a call fails before it is sent, if it does. */\nfunction refusedBeforeSending(\n connection: QuickdrawConnection,\n kind: MethodKind,\n signal: AbortSignal | undefined,\n): QuickdrawError | undefined {\n if (signal?.aborted === true) {\n return cancelledError();\n }\n if (!connection.socket.connected && !mayConnect(connection)) {\n return internalError(\"Not connected to the server\");\n }\n const remaining = connection.backoffRemaining(kind);\n if (remaining > 0) {\n return new QuickdrawError(\n \"RATE_LIMITED\",\n `Waiting before more ${kind} calls: the server answered RATE_LIMITED`,\n { retryAfterMs: Math.ceil(remaining) },\n );\n }\n return undefined;\n}\n\nfunction envelopeOf(id: CallId, request: CallRequest): CallEnvelope {\n return {\n id,\n s: request.service,\n m: request.method,\n ...(request.input === undefined ? {} : { i: request.input }),\n ...(request.v === undefined ? {} : { v: request.v }),\n };\n}\n\n/** The result or error a reply stands for; `RATE_LIMITED` starts the kind's backoff. */\nfunction readReply(\n connection: QuickdrawConnection,\n kind: MethodKind,\n reply: unknown,\n): CallResult | QuickdrawError {\n if (isRecord(reply) && reply.ok === true) {\n const versioned = typeof reply.v === \"string\" || typeof reply.v === \"number\";\n if (reply.nm === true && !versioned) {\n return internalError(\"The server answered not modified without a version\");\n }\n return reply as unknown as CallResult;\n }\n if (isRecord(reply) && reply.ok === false) {\n const error = fromWire(reply.e);\n if (error.code === \"RATE_LIMITED\") {\n connection.reportRateLimited(kind, retryAfterOf(error));\n }\n return error;\n }\n return internalError(\"The server's reply is not a call reply\");\n}\n\n/** True when a send-buffer packet is the `qd:call` frame of call `id`. */\nfunction isQueuedCall(data: unknown, id: CallId): boolean {\n return (\n Array.isArray(data) && data[0] === CLIENT_EVENTS.call && isRecord(data[1]) && data[1].id === id\n );\n}\n\n/** Drops the call from Socket.IO's send buffer, or else asks the server to cancel it. */\nfunction cancelCall(socket: QuickdrawSocket, id: CallId): void {\n const queued = socket.sendBuffer.findIndex((packet) => isQueuedCall(packet.data, id));\n if (queued >= 0) {\n socket.sendBuffer.splice(queued, 1);\n } else if (socket.connected) {\n socket.emit(CLIENT_EVENTS.cancel, { id });\n }\n}\n\n/**\n * Calls `request.method` of `request.service` over `connection`. Resolves\n * with `{ ok: true, d, v? }`, or `{ ok: true, nm: true, v }` when the `v` it\n * sent is current; rejects with a `QuickdrawError` (see the top of this file\n * for which code when).\n *\n * @example\n * const reply = await call(connection, { service: \"taskService\", method: \"get\", input: { id } });\n * const task = isNotModified(reply) ? cached : reply.d;\n */\nexport function call<Output = unknown>(\n connection: QuickdrawConnection,\n request: CallRequest,\n): Promise<CallResult<Output>> {\n const kind = request.kind ?? \"query\";\n const { signal } = request;\n const refused = refusedBeforeSending(connection, kind, signal);\n if (refused !== undefined) {\n return Promise.reject(refused);\n }\n const { socket } = connection;\n const timeoutMs = request.timeoutMs ?? connection.timeoutMs;\n if (!isTimeLimit(timeoutMs)) {\n return Promise.reject(\n new TypeError(\n \"call: timeoutMs must be a number of milliseconds, above 0 and at most 2^31 - 1\",\n ),\n );\n }\n const id = connection.nextCallId();\n return new Promise<CallResult<Output>>((resolve, reject) => {\n let settled = false;\n const settle = (outcome: CallResult | QuickdrawError): void => {\n if (settled) {\n return;\n }\n settled = true;\n signal?.removeEventListener(\"abort\", onAbort);\n if (outcome instanceof QuickdrawError) {\n reject(outcome);\n } else {\n notifyEach([request.onReply], (onReply) => {\n onReply?.(outcome);\n });\n resolve(outcome as CallResult<Output>);\n }\n };\n function onAbort(): void {\n cancelCall(socket, id);\n settle(cancelledError());\n }\n socket\n .timeout(timeoutMs)\n .emit(CLIENT_EVENTS.call, envelopeOf(id, request), (error: Error | null, reply: unknown) => {\n if (error === null) {\n settle(readReply(connection, kind, reply));\n } else if (error instanceof QuickdrawError) {\n // Failed by the connection before it was sent (new credentials).\n settle(error);\n } else if (socket.connected) {\n settle(new QuickdrawError(\"TIMEOUT\", `No answer within ${timeoutMs} ms`));\n } else if (error.message === UNSENT_TIMEOUT) {\n // Still in the send buffer when its time ran out: Socket.IO drops it unsent.\n settle(internalError(\"No answer: the connection to the server is down\"));\n } else {\n // Sent, then the connection dropped: Socket.IO fails the acknowledgements it waits for.\n settle(lostError());\n }\n });\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n\n/**\n * Calls a method and resolves with its data. For a caller that sends no\n * version, which is never answered \"not modified\".\n */\nexport async function callData<Output = unknown>(\n connection: QuickdrawConnection,\n request: Omit<CallRequest, \"v\">,\n): Promise<Output> {\n const result = await call<Output>(connection, request);\n if (isNotModified(result)) {\n throw internalError(\"The server answered not modified to a call that sent no version\");\n }\n return result.d;\n}\n\n/**\n * The default `retry` of query hooks: one more attempt after an `INTERNAL`\n * error, which includes a dropped connection, or an error that is not a\n * `QuickdrawError`; none for any other code. A `TIMEOUT` is not retried,\n * because the server may still be running the call, and `RATE_LIMITED`\n * waits for the backoff instead.\n */\nexport function shouldRetry(failureCount: number, error: unknown): boolean {\n return failureCount < 1 && (!(error instanceof QuickdrawError) || error.code === \"INTERNAL\");\n}\n","// The app room a host-served member names (host mode, RFC 0003 section\n// 12.5), read from the contract as the server reads it: a channel's\n// `requires: { room }` (a name, a function of the payload, or `{ prefix }`)\n// and a stream's `access: { room: (scope) => room }`. A computed name that\n// is not a string, is empty, is the framework's (`qd:`, `user:`) or has the\n// shape of a socket id names no room. The peer side asks which host to send\n// to; the host side checks that a message or a subscribe is for its room.\n//\n// React-free.\n\nimport { reservedRoomPrefix, socketIdShaped } from \"../../contract/names\";\nimport type { ChannelDef, StreamDef } from \"../../contract/realtime\";\n\n/** `value`, when it can name an app room. */\nfunction appRoom(value: unknown): string | undefined {\n return typeof value === \"string\" &&\n value !== \"\" &&\n reservedRoomPrefix(value) === undefined &&\n !socketIdShaped(value)\n ? value\n : undefined;\n}\n\n/** Calls a room function of the contract; one that throws names no room. */\nfunction computed(room: unknown, value: unknown): string | undefined {\n try {\n return appRoom((room as (input: unknown) => unknown)(value));\n } catch {\n return undefined;\n }\n}\n\n/**\n * The room a host-served channel's message is for: its requirement's name,\n * the one it computes from `payload`, or for `{ prefix }` the first of\n * `rooms` (the rooms whose host is known) that starts with it.\n */\nexport function channelRoomOf(\n def: ChannelDef,\n payload: unknown,\n rooms: Iterable<string>,\n): string | undefined {\n const room = def.requires?.room;\n if (typeof room === \"string\") {\n return room;\n }\n if (typeof room === \"function\") {\n return computed(room, payload);\n }\n if (typeof room === \"object\") {\n for (const known of rooms) {\n if (known.startsWith(room.prefix)) {\n return known;\n }\n }\n }\n return undefined;\n}\n\n/** The room whose host serves a host-served stream's feed of `scope`. */\nexport function streamRoomOf(def: StreamDef, scope: string): string | undefined {\n const access = typeof def.access === \"object\" ? def.access : undefined;\n const room = access?.room;\n return typeof room === \"function\" ? computed(room, scope) : undefined;\n}\n"],"mappings":"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","names":[]}
@@ -10,23 +10,18 @@ import {
10
10
  createBinding,
11
11
  createInvalidationCoordinator,
12
12
  createMutationTrace,
13
- createQuickdrawConnection,
14
13
  entityScopes,
15
14
  entryQuery,
16
15
  feedKey,
17
16
  invalidateWith,
18
- isTimeLimit,
19
17
  isWatchedQuery,
20
18
  liveDataOf,
21
19
  loopGuardOf,
22
20
  newItem,
23
- notifyEach,
24
21
  overlaysOf,
25
22
  reconnectJitter,
26
23
  refetchOnAccessChanges,
27
24
  registerQuery,
28
- reloadOncePerSession,
29
- retryAfterOf,
30
25
  searchCall,
31
26
  searchTargetOf,
32
27
  serverStateOf,
@@ -43,167 +38,45 @@ import {
43
38
  useQuickdrawContext,
44
39
  userOf,
45
40
  viewPredicate
46
- } from "./chunk-OECZPRPW.js";
41
+ } from "./chunk-64I6MYBC.js";
47
42
  import {
48
- CLIENT_EVENTS,
49
- SERVICE_TOPIC,
50
- collectionTopic
51
- } from "./chunk-VSQBHPWH.js";
43
+ call,
44
+ callData,
45
+ channelRoomOf,
46
+ isNotModified,
47
+ isUnknownOutcome,
48
+ shouldRetry,
49
+ streamRoomOf
50
+ } from "./chunk-NX2V6SIL.js";
52
51
  import {
53
- isScopedStream
54
- } from "./chunk-RT6OILOI.js";
52
+ createQuickdrawConnection,
53
+ hostLinksOf,
54
+ hostsOf,
55
+ mayConnect,
56
+ reloadOncePerSession
57
+ } from "./chunk-STH5DLQP.js";
55
58
  import {
56
59
  buildCaller,
57
60
  collectionKey,
58
61
  entityKey,
59
62
  methodKey,
60
63
  methodKeyPrefix
61
- } from "./chunk-X5CWPX7H.js";
64
+ } from "./chunk-IIQ3WMJK.js";
65
+ import {
66
+ CLIENT_EVENTS,
67
+ SERVICE_TOPIC,
68
+ collectionTopic
69
+ } from "./chunk-X4ZU4QH5.js";
70
+ import {
71
+ isHostServed,
72
+ isScopedStream
73
+ } from "./chunk-GYLWEJHW.js";
74
+ import {
75
+ QuickdrawError
76
+ } from "./chunk-Q4OGQM5I.js";
62
77
  import {
63
- QuickdrawError,
64
- fromWire,
65
78
  isRecord
66
- } from "./chunk-4YO5H5ON.js";
67
-
68
- // src/client/call.ts
69
- var UNSENT_TIMEOUT = "operation has timed out";
70
- function isNotModified(result) {
71
- return result.nm === true;
72
- }
73
- function cancelledError() {
74
- return new QuickdrawError("CANCELLED", "The call was cancelled");
75
- }
76
- function internalError(message) {
77
- return new QuickdrawError("INTERNAL", message);
78
- }
79
- var lostInFlight = /* @__PURE__ */ new WeakSet();
80
- function isUnknownOutcome(error) {
81
- return error instanceof QuickdrawError && (error.code === "TIMEOUT" || lostInFlight.has(error));
82
- }
83
- function lostError() {
84
- const error = internalError("No answer: the connection to the server is down");
85
- lostInFlight.add(error);
86
- return error;
87
- }
88
- function refusedBeforeSending(connection, kind, signal) {
89
- if (signal?.aborted === true) {
90
- return cancelledError();
91
- }
92
- const { socket } = connection;
93
- if (!socket.connected && !socket.active) {
94
- return internalError("Not connected to the server");
95
- }
96
- const remaining = connection.backoffRemaining(kind);
97
- if (remaining > 0) {
98
- return new QuickdrawError(
99
- "RATE_LIMITED",
100
- `Waiting before more ${kind} calls: the server answered RATE_LIMITED`,
101
- { retryAfterMs: Math.ceil(remaining) }
102
- );
103
- }
104
- return void 0;
105
- }
106
- function envelopeOf(id, request) {
107
- return {
108
- id,
109
- s: request.service,
110
- m: request.method,
111
- ...request.input === void 0 ? {} : { i: request.input },
112
- ...request.v === void 0 ? {} : { v: request.v }
113
- };
114
- }
115
- function readReply(connection, kind, reply) {
116
- if (isRecord(reply) && reply.ok === true) {
117
- const versioned = typeof reply.v === "string" || typeof reply.v === "number";
118
- if (reply.nm === true && !versioned) {
119
- return internalError("The server answered not modified without a version");
120
- }
121
- return reply;
122
- }
123
- if (isRecord(reply) && reply.ok === false) {
124
- const error = fromWire(reply.e);
125
- if (error.code === "RATE_LIMITED") {
126
- connection.reportRateLimited(kind, retryAfterOf(error));
127
- }
128
- return error;
129
- }
130
- return internalError("The server's reply is not a call reply");
131
- }
132
- function isQueuedCall(data, id) {
133
- return Array.isArray(data) && data[0] === CLIENT_EVENTS.call && isRecord(data[1]) && data[1].id === id;
134
- }
135
- function cancelCall(socket, id) {
136
- const queued = socket.sendBuffer.findIndex((packet) => isQueuedCall(packet.data, id));
137
- if (queued >= 0) {
138
- socket.sendBuffer.splice(queued, 1);
139
- } else if (socket.connected) {
140
- socket.emit(CLIENT_EVENTS.cancel, { id });
141
- }
142
- }
143
- function call(connection, request) {
144
- const kind = request.kind ?? "query";
145
- const { signal } = request;
146
- const refused = refusedBeforeSending(connection, kind, signal);
147
- if (refused !== void 0) {
148
- return Promise.reject(refused);
149
- }
150
- const { socket } = connection;
151
- const timeoutMs = request.timeoutMs ?? connection.timeoutMs;
152
- if (!isTimeLimit(timeoutMs)) {
153
- return Promise.reject(
154
- new TypeError(
155
- "call: timeoutMs must be a number of milliseconds, above 0 and at most 2^31 - 1"
156
- )
157
- );
158
- }
159
- const id = connection.nextCallId();
160
- return new Promise((resolve, reject) => {
161
- let settled = false;
162
- const settle = (outcome) => {
163
- if (settled) {
164
- return;
165
- }
166
- settled = true;
167
- signal?.removeEventListener("abort", onAbort);
168
- if (outcome instanceof QuickdrawError) {
169
- reject(outcome);
170
- } else {
171
- notifyEach([request.onReply], (onReply) => {
172
- onReply?.(outcome);
173
- });
174
- resolve(outcome);
175
- }
176
- };
177
- function onAbort() {
178
- cancelCall(socket, id);
179
- settle(cancelledError());
180
- }
181
- socket.timeout(timeoutMs).emit(CLIENT_EVENTS.call, envelopeOf(id, request), (error, reply) => {
182
- if (error === null) {
183
- settle(readReply(connection, kind, reply));
184
- } else if (error instanceof QuickdrawError) {
185
- settle(error);
186
- } else if (socket.connected) {
187
- settle(new QuickdrawError("TIMEOUT", `No answer within ${timeoutMs} ms`));
188
- } else if (error.message === UNSENT_TIMEOUT) {
189
- settle(internalError("No answer: the connection to the server is down"));
190
- } else {
191
- settle(lostError());
192
- }
193
- });
194
- signal?.addEventListener("abort", onAbort, { once: true });
195
- });
196
- }
197
- async function callData(connection, request) {
198
- const result = await call(connection, request);
199
- if (isNotModified(result)) {
200
- throw internalError("The server answered not modified to a call that sent no version");
201
- }
202
- return result.d;
203
- }
204
- function shouldRetry(failureCount, error) {
205
- return failureCount < 1 && (!(error instanceof QuickdrawError) || error.code === "INTERNAL");
206
- }
79
+ } from "./chunk-QSU47XEX.js";
207
80
 
208
81
  // src/client/live/useSearch.ts
209
82
  import { useQuery as useQuery2 } from "@tanstack/react-query";
@@ -310,7 +183,7 @@ async function fetchVersioned(connection, queryClient, query, signal) {
310
183
  return fetchFresh(connection, query, signal);
311
184
  }
312
185
  async function helloKnown(connection, signal) {
313
- const ready = () => connection.getState().hello !== null || !connection.socket.active || signal?.aborted === true;
186
+ const ready = () => connection.getState().hello !== null || !mayConnect(connection) || signal?.aborted === true;
314
187
  if (ready()) {
315
188
  return;
316
189
  }
@@ -356,7 +229,9 @@ async function fetchMethodQuery(connection, queryClient, query, signal) {
356
229
  }
357
230
 
358
231
  // src/client/queryHooks.ts
359
- import { hashKey } from "@tanstack/react-query";
232
+ import {
233
+ hashKey
234
+ } from "@tanstack/react-query";
360
235
  import { useEffect, useMemo, useRef, useSyncExternalStore } from "react";
361
236
  function watchedModelsOf(target) {
362
237
  const { watch } = target;
@@ -465,6 +340,19 @@ function useOverlaySelect(queryClient, target, select) {
465
340
  };
466
341
  }, [shape, view, select]);
467
342
  }
343
+ function placeholderFromCache(queryClient, placeholderData) {
344
+ if (typeof placeholderData !== "function") {
345
+ return placeholderData;
346
+ }
347
+ const asked = placeholderData;
348
+ const fromCache = (previousData, previousQuery) => previousQuery === void 0 || queryClient.getQueryCache().get(previousQuery.queryHash) === previousQuery ? asked(previousData, previousQuery) : void 0;
349
+ return fromCache;
350
+ }
351
+ function placeholderOption(queryClient, queryKey, options) {
352
+ const keyDefaults = queryClient.getQueryDefaults(queryKey);
353
+ const given = "placeholderData" in options ? options.placeholderData : Object.hasOwn(keyDefaults, "placeholderData") ? keyDefaults.placeholderData : queryClient.getDefaultOptions().queries?.placeholderData;
354
+ return placeholderFromCache(queryClient, given);
355
+ }
468
356
  function hiddenResult(result) {
469
357
  return {
470
358
  ...result,
@@ -725,6 +613,7 @@ function useMethodQuery(target, input, options = {}) {
725
613
  enabled: live ? enabled ?? true : false,
726
614
  retry: retry ?? shouldRetry,
727
615
  structuralSharing: shareKeepingVersion(structuralSharing),
616
+ placeholderData: placeholderOption(queryClient, queryKey, options),
728
617
  ...shown2 === void 0 ? {} : { select: shown2 }
729
618
  });
730
619
  return awaiting ? hiddenResult(result) : result;
@@ -939,17 +828,25 @@ function clientAdminNamespace(contract, methods) {
939
828
 
940
829
  // src/client/live/useChannel.ts
941
830
  import { useCallback, useMemo as useMemo4 } from "react";
942
- function useChannel(service, channel) {
831
+ function useChannel(service, channel, def) {
943
832
  const { connection } = useQuickdrawContext(`${service}.${channel}.useChannel`);
944
833
  const state = useConnectionState(connection);
945
834
  const send = useCallback(
946
835
  (payload) => {
947
836
  const { socket } = connection;
948
- if (socket.connected) {
949
- socket.volatile.emit(CLIENT_EVENTS.channel, [service, channel, payload]);
837
+ if (!socket.connected) {
838
+ return;
839
+ }
840
+ if (def !== void 0 && isHostServed(def)) {
841
+ const room = channelRoomOf(def, payload, hostsOf(connection).rooms.keys());
842
+ if (room !== void 0) {
843
+ hostLinksOf(connection).send(room, service, channel, payload);
844
+ }
845
+ return;
950
846
  }
847
+ socket.volatile.emit(CLIENT_EVENTS.channel, [service, channel, payload]);
951
848
  },
952
- [connection, service, channel]
849
+ [connection, service, channel, def]
953
850
  );
954
851
  const isReady = state.status === "connected" && state.hello !== null;
955
852
  return useMemo4(() => ({ send, isReady }), [send, isReady]);
@@ -1016,13 +913,16 @@ function streamMember(target) {
1016
913
  }
1017
914
  function realtimeMembers(contract) {
1018
915
  const service = contract.name;
1019
- const streams = Object.entries(contract.streams).map(([stream, def]) => [
1020
- stream,
1021
- streamMember(Object.freeze({ service, stream, scoped: isScopedStream(def) }))
1022
- ]);
1023
- const channels = Object.keys(contract.channels).map((channel) => [
916
+ const streams = Object.entries(contract.streams).map(([stream, def]) => {
917
+ const host = isHostServed(def) ? { host: (scope) => streamRoomOf(def, scope) } : {};
918
+ return [
919
+ stream,
920
+ streamMember(Object.freeze({ service, stream, scoped: isScopedStream(def), ...host }))
921
+ ];
922
+ });
923
+ const channels = Object.entries(contract.channels).map(([channel, def]) => [
1024
924
  channel,
1025
- Object.freeze({ useChannel: () => useChannel(service, channel) })
925
+ Object.freeze({ useChannel: () => useChannel(service, channel, def) })
1026
926
  ]);
1027
927
  const events = Object.keys(contract.events).map((event) => [
1028
928
  event,
@@ -1244,9 +1144,11 @@ function searchMember(target, definition, contract) {
1244
1144
  var RESERVED_KEYS = ["invalidate"];
1245
1145
  function queryMember(binding, target, extra) {
1246
1146
  const key = (input) => methodKey(target.service, target.method, input);
1147
+ const defaults = target.queryDefaults;
1247
1148
  const member = Object.freeze({
1248
1149
  ...extra,
1249
- useQuery: (input, options) => useMethodQuery(target, input, options),
1150
+ // The caller's options over the method's defaults (the admin kit's reads of rows: `staleTime: 0`).
1151
+ useQuery: (input, options) => useMethodQuery(target, input, defaults === void 0 ? options : { ...defaults, ...options }),
1250
1152
  call: async (input, options) => await callData(connectionOf(binding, target, "call"), {
1251
1153
  ...target,
1252
1154
  input,
@@ -1399,15 +1301,10 @@ function useQuickdraw() {
1399
1301
  }
1400
1302
 
1401
1303
  export {
1402
- isNotModified,
1403
- isUnknownOutcome,
1404
- call,
1405
- callData,
1406
- shouldRetry,
1407
1304
  useLiveData,
1408
1305
  SEARCH_DEBOUNCE_MS,
1409
1306
  createQuickdrawClient,
1410
1307
  QuickdrawProvider,
1411
1308
  useQuickdraw
1412
1309
  };
1413
- //# sourceMappingURL=chunk-ZIVPHYRG.js.map
1310
+ //# sourceMappingURL=chunk-NZHA4TZD.js.map