@lunora/client 1.0.0-alpha.11 → 1.0.0-alpha.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/index.d.mts +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +2 -2
- package/dist/packem_shared/{LunoraClient-6J-4BGWK.mjs → LunoraClient-wh6w9Ivn.mjs} +543 -36
- package/dist/packem_shared/SubscriptionRegistry-Cxr70og-.mjs +1 -0
- package/dist/packem_shared/{createServerClient-CtklXrs2.mjs → createServerClient-C5GMCGSQ.mjs} +1 -1
- package/dist/packem_shared/{lunora-client.d-Dm1BYq19.d.ts → lunora-client.d-DTwbfUWF.d.mts} +106 -1
- package/dist/packem_shared/{lunora-client.d-Dm1BYq19.d.mts → lunora-client.d-DTwbfUWF.d.ts} +106 -1
- package/dist/packem_shared/{preload.d-BfWzGJWV.d.mts → preload.d-BEp0JJeu.d.mts} +1 -1
- package/dist/packem_shared/{preload.d-Sp_Ef-_u.d.ts → preload.d-CK2IYf5B.d.ts} +1 -1
- package/dist/packem_shared/{subscription-C1Jy7HiF.mjs → subscription-DoyO04-2.mjs} +10 -0
- package/dist/query/index.d.mts +2 -2
- package/dist/query/index.d.ts +2 -2
- package/dist/ssr/index.d.mts +3 -3
- package/dist/ssr/index.d.ts +3 -3
- package/dist/ssr/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/packem_shared/SubscriptionRegistry-Dn-7k7eo.mjs +0 -1
package/dist/auth/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-
|
|
1
|
+
import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-DTwbfUWF.mjs";
|
|
2
2
|
import '@lunora/runtime';
|
|
3
3
|
interface IdentityStore {
|
|
4
4
|
/** Read the current resolved user, or `null` when signed out / not yet resolved. */
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-
|
|
1
|
+
import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-DTwbfUWF.js";
|
|
2
2
|
import '@lunora/runtime';
|
|
3
3
|
interface IdentityStore {
|
|
4
4
|
/** Read the current resolved user, or `null` when signed out / not yet resolved. */
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as PersistenceAdapter, B as BookmarkStorage, F as FunctionReference, A as ArgsOf, M as MutationCallOptions, R as ReturnOf, O as OfflineQueueOptions, Q as QueryCacheAdapter, d as ReconnectOptions } from "./packem_shared/lunora-client.d-
|
|
2
|
-
export { type C as CachedQuery, type
|
|
3
|
-
export { p as preloadQuery, a as preloadedQueryResult } from "./packem_shared/preload.d-
|
|
1
|
+
import { c as PersistenceAdapter, B as BookmarkStorage, F as FunctionReference, A as ArgsOf, M as MutationCallOptions, R as ReturnOf, O as OfflineQueueOptions, Q as QueryCacheAdapter, d as ReconnectOptions } from "./packem_shared/lunora-client.d-DTwbfUWF.mjs";
|
|
2
|
+
export { type e as BatchSlot, type C as CachedQuery, type f as ClientMessage, type g as ClientShapeSubscribeMessage, type h as ClientShapeUnsubscribeMessage, type i as ConnectionStatus, D as DEFAULT_MAX_BUFFER, type j as FunctionArgumentDescriptor, type k as FunctionDescriptor, type G as GlobalFacetResult, type l as GlobalFacetValue, type m as GlobalFilterClause, type n as GlobalTableInfo, type o as GlobalTablePage, L as LunoraClient, type p as LunoraClientError, type q as LunoraClientOptions, type r as MutationSettledEvent, type s as OptimisticLocalStore, type t as OptimisticUpdate, type u as OutboxMutation, type v as OutboxSink, type w as PersistedMutation, type P as Preloaded, type x as RowOp, type y as RpcEnvelope, type z as RpcResponseBody, type E as ScheduleRecord, type H as SchedulerPoolStatus, type I as SchedulerStatus, type J as ServerMessage, type K as ServerPokeEndMessage, type N as ServerPokePartMessage, type T as ServerPokeStartMessage, type V as ShardTrafficEntry, type W as ShardTrafficResult, type X as StorageListPage, type Y as StorageObject, type Z as StreamHandle, type _ as StreamIterable, type $ as SubscriptionCallback, type S as SubscriptionError, type b as SubscriptionErrorCallback, a0 as SubscriptionRegistry, type a1 as SubscriptionState, type a2 as SyncWatermark, type a as Unsubscribe, type U as User, type a3 as WorkflowInstanceAction, type a4 as WorkflowInstanceDetail, type a5 as WorkflowInstancePage, type a6 as WorkflowInstanceStatus, type a7 as WorkflowInstanceSummary, type a8 as WorkflowStepDetail, a9 as createLocalStore, aa as createStream } from "./packem_shared/lunora-client.d-DTwbfUWF.mjs";
|
|
3
|
+
export { p as preloadQuery, a as preloadedQueryResult } from "./packem_shared/preload.d-BEp0JJeu.mjs";
|
|
4
4
|
export type { AuthCapabilities, AuthImpersonation, AuthPage, AuthSession, AuthUser, CronJobInfo, VectorIndexSummary, VectorQueryMatch } from '@lunora/runtime';
|
|
5
5
|
/**
|
|
6
6
|
* The slice of React Native's `AsyncStorage` (or any async key/value store —
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as PersistenceAdapter, B as BookmarkStorage, F as FunctionReference, A as ArgsOf, M as MutationCallOptions, R as ReturnOf, O as OfflineQueueOptions, Q as QueryCacheAdapter, d as ReconnectOptions } from "./packem_shared/lunora-client.d-
|
|
2
|
-
export { type C as CachedQuery, type
|
|
3
|
-
export { p as preloadQuery, a as preloadedQueryResult } from "./packem_shared/preload.d-
|
|
1
|
+
import { c as PersistenceAdapter, B as BookmarkStorage, F as FunctionReference, A as ArgsOf, M as MutationCallOptions, R as ReturnOf, O as OfflineQueueOptions, Q as QueryCacheAdapter, d as ReconnectOptions } from "./packem_shared/lunora-client.d-DTwbfUWF.js";
|
|
2
|
+
export { type e as BatchSlot, type C as CachedQuery, type f as ClientMessage, type g as ClientShapeSubscribeMessage, type h as ClientShapeUnsubscribeMessage, type i as ConnectionStatus, D as DEFAULT_MAX_BUFFER, type j as FunctionArgumentDescriptor, type k as FunctionDescriptor, type G as GlobalFacetResult, type l as GlobalFacetValue, type m as GlobalFilterClause, type n as GlobalTableInfo, type o as GlobalTablePage, L as LunoraClient, type p as LunoraClientError, type q as LunoraClientOptions, type r as MutationSettledEvent, type s as OptimisticLocalStore, type t as OptimisticUpdate, type u as OutboxMutation, type v as OutboxSink, type w as PersistedMutation, type P as Preloaded, type x as RowOp, type y as RpcEnvelope, type z as RpcResponseBody, type E as ScheduleRecord, type H as SchedulerPoolStatus, type I as SchedulerStatus, type J as ServerMessage, type K as ServerPokeEndMessage, type N as ServerPokePartMessage, type T as ServerPokeStartMessage, type V as ShardTrafficEntry, type W as ShardTrafficResult, type X as StorageListPage, type Y as StorageObject, type Z as StreamHandle, type _ as StreamIterable, type $ as SubscriptionCallback, type S as SubscriptionError, type b as SubscriptionErrorCallback, a0 as SubscriptionRegistry, type a1 as SubscriptionState, type a2 as SyncWatermark, type a as Unsubscribe, type U as User, type a3 as WorkflowInstanceAction, type a4 as WorkflowInstanceDetail, type a5 as WorkflowInstancePage, type a6 as WorkflowInstanceStatus, type a7 as WorkflowInstanceSummary, type a8 as WorkflowStepDetail, a9 as createLocalStore, aa as createStream } from "./packem_shared/lunora-client.d-DTwbfUWF.js";
|
|
3
|
+
export { p as preloadQuery, a as preloadedQueryResult } from "./packem_shared/preload.d-CK2IYf5B.js";
|
|
4
4
|
export type { AuthCapabilities, AuthImpersonation, AuthPage, AuthSession, AuthUser, CronJobInfo, VectorIndexSummary, VectorQueryMatch } from '@lunora/runtime';
|
|
5
5
|
/**
|
|
6
6
|
* The slice of React Native's `AsyncStorage` (or any async key/value store —
|
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ export { default as createInMemoryBookmarkStorage } from './packem_shared/create
|
|
|
3
3
|
export { applyDelta, isMutationDelta } from './packem_shared/applyDelta-4jFGTPA3.mjs';
|
|
4
4
|
export { CONFLICT_ERROR_CODE, isConflictError } from './packem_shared/CONFLICT_ERROR_CODE-aUdVbEDw.mjs';
|
|
5
5
|
export { c as createLocalStore } from './packem_shared/local-store-BNgN3Dw3.mjs';
|
|
6
|
-
export { LunoraClient } from './packem_shared/LunoraClient-
|
|
6
|
+
export { LunoraClient } from './packem_shared/LunoraClient-wh6w9Ivn.mjs';
|
|
7
7
|
export { createMutationRunner } from './packem_shared/createMutationRunner-BqsavzvG.mjs';
|
|
8
8
|
export { createMutatorRunner } from './packem_shared/createMutatorRunner-BETvCd0p.mjs';
|
|
9
9
|
export { O as OfflineQueue } from './packem_shared/offline-queue-7Wc4onA0.mjs';
|
|
@@ -12,4 +12,4 @@ export { preloadQuery, preloadedQueryResult } from './packem_shared/preloadQuery
|
|
|
12
12
|
export { createInMemoryQueryCache, createIndexedDbQueryCache, queryCacheKey } from './packem_shared/createInMemoryQueryCache-4AkTV38-.mjs';
|
|
13
13
|
export { createReconnect } from './packem_shared/createReconnect-Di_-oHH7.mjs';
|
|
14
14
|
export { DEFAULT_MAX_BUFFER, createStream } from './packem_shared/DEFAULT_MAX_BUFFER-BDkqO5PW.mjs';
|
|
15
|
-
export { S as SubscriptionRegistry } from './packem_shared/subscription-
|
|
15
|
+
export { S as SubscriptionRegistry } from './packem_shared/subscription-DoyO04-2.mjs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as SubscriptionRegistry, s as stableStringify } from './subscription-
|
|
1
|
+
import { S as SubscriptionRegistry, s as stableStringify } from './subscription-DoyO04-2.mjs';
|
|
2
2
|
import createInMemoryBookmarkStorage from './createInMemoryBookmarkStorage-BoN7a7TH.mjs';
|
|
3
3
|
import { isMutationDelta, applyDelta } from './applyDelta-4jFGTPA3.mjs';
|
|
4
4
|
import { a as applyOptimisticLayer, d as dropConfirmedLayers, n as notifySubscription, f as foldOptimistic, c as createLocalStore } from './local-store-BNgN3Dw3.mjs';
|
|
@@ -8,6 +8,224 @@ import { resolveQueryCacheAdapter, queryCacheKey } from './createInMemoryQueryCa
|
|
|
8
8
|
import { createReconnect } from './createReconnect-Di_-oHH7.mjs';
|
|
9
9
|
import { createStream } from './DEFAULT_MAX_BUFFER-BDkqO5PW.mjs';
|
|
10
10
|
|
|
11
|
+
const MAX_BATCH_ENTRIES = 500;
|
|
12
|
+
|
|
13
|
+
const TAG = "$lunora.wire$";
|
|
14
|
+
const MAX_DEPTH = 64;
|
|
15
|
+
const MAX_BIGINT_DIGITS = 1024;
|
|
16
|
+
const UNSAFE_KEY = "__proto__";
|
|
17
|
+
const TYPED_ARRAY_CTORS = {
|
|
18
|
+
BigInt64Array,
|
|
19
|
+
BigUint64Array,
|
|
20
|
+
Float32Array,
|
|
21
|
+
Float64Array,
|
|
22
|
+
Int8Array,
|
|
23
|
+
Int16Array,
|
|
24
|
+
Int32Array,
|
|
25
|
+
Uint8Array,
|
|
26
|
+
Uint8ClampedArray,
|
|
27
|
+
Uint16Array,
|
|
28
|
+
Uint32Array
|
|
29
|
+
};
|
|
30
|
+
const ERROR_CTORS = {
|
|
31
|
+
Error,
|
|
32
|
+
EvalError,
|
|
33
|
+
RangeError,
|
|
34
|
+
ReferenceError,
|
|
35
|
+
SyntaxError,
|
|
36
|
+
TypeError,
|
|
37
|
+
URIError
|
|
38
|
+
};
|
|
39
|
+
const toBase64 = (bytes) => {
|
|
40
|
+
let binary = "";
|
|
41
|
+
const chunk = 32768;
|
|
42
|
+
for (let index = 0; index < bytes.length; index += chunk) {
|
|
43
|
+
binary += String.fromCharCode(...bytes.subarray(index, index + chunk));
|
|
44
|
+
}
|
|
45
|
+
return btoa(binary);
|
|
46
|
+
};
|
|
47
|
+
const fromBase64 = (base64) => {
|
|
48
|
+
const binary = atob(base64);
|
|
49
|
+
const bytes = new Uint8Array(binary.length);
|
|
50
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
51
|
+
bytes[index] = binary.codePointAt(index) ?? 0;
|
|
52
|
+
}
|
|
53
|
+
return bytes;
|
|
54
|
+
};
|
|
55
|
+
const encodeWire = (value, depth = 0) => {
|
|
56
|
+
if (depth > MAX_DEPTH) {
|
|
57
|
+
throw new RangeError(`wire-codec: value nesting exceeds the ${MAX_DEPTH}-level limit`);
|
|
58
|
+
}
|
|
59
|
+
if (value === void 0) {
|
|
60
|
+
return [TAG, "undefined"];
|
|
61
|
+
}
|
|
62
|
+
if (value === null) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const kind = typeof value;
|
|
66
|
+
if (kind === "bigint") {
|
|
67
|
+
return [TAG, "bigint", value.toString()];
|
|
68
|
+
}
|
|
69
|
+
if (kind === "number") {
|
|
70
|
+
const numeric = value;
|
|
71
|
+
if (Number.isNaN(numeric)) {
|
|
72
|
+
return [TAG, "nan"];
|
|
73
|
+
}
|
|
74
|
+
if (numeric === Infinity) {
|
|
75
|
+
return [TAG, "inf"];
|
|
76
|
+
}
|
|
77
|
+
if (numeric === -Infinity) {
|
|
78
|
+
return [TAG, "-inf"];
|
|
79
|
+
}
|
|
80
|
+
return numeric;
|
|
81
|
+
}
|
|
82
|
+
if (kind !== "object") {
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
if (value instanceof Date) {
|
|
86
|
+
return [TAG, "date", encodeWire(value.getTime(), depth + 1)];
|
|
87
|
+
}
|
|
88
|
+
if (value instanceof Error) {
|
|
89
|
+
const error = value;
|
|
90
|
+
const properties = {};
|
|
91
|
+
for (const key of Object.keys(error)) {
|
|
92
|
+
if (error[key] !== void 0) {
|
|
93
|
+
properties[key] = encodeWire(error[key], depth + 1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const encodedError = [TAG, "error", error.name, error.message, properties];
|
|
97
|
+
if (error.cause !== void 0) {
|
|
98
|
+
encodedError.push(encodeWire(error.cause, depth + 1));
|
|
99
|
+
}
|
|
100
|
+
return encodedError;
|
|
101
|
+
}
|
|
102
|
+
if (value instanceof URL) {
|
|
103
|
+
return [TAG, "url", value.href];
|
|
104
|
+
}
|
|
105
|
+
if (value instanceof Map) {
|
|
106
|
+
return [TAG, "map", [...value.entries()].map(([k, v]) => [encodeWire(k, depth + 1), encodeWire(v, depth + 1)])];
|
|
107
|
+
}
|
|
108
|
+
if (value instanceof Set) {
|
|
109
|
+
return [TAG, "set", [...value].map((item) => encodeWire(item, depth + 1))];
|
|
110
|
+
}
|
|
111
|
+
if (value instanceof ArrayBuffer) {
|
|
112
|
+
return [TAG, "bytes", toBase64(new Uint8Array(value)), "ArrayBuffer"];
|
|
113
|
+
}
|
|
114
|
+
if (ArrayBuffer.isView(value)) {
|
|
115
|
+
const view = value;
|
|
116
|
+
const ctorName = view.constructor.name;
|
|
117
|
+
const bytes = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
118
|
+
return ctorName === "Uint8Array" ? [TAG, "bytes", toBase64(bytes)] : [TAG, "bytes", toBase64(bytes), ctorName];
|
|
119
|
+
}
|
|
120
|
+
if (Array.isArray(value)) {
|
|
121
|
+
const encoded = value.map((item) => encodeWire(item, depth + 1));
|
|
122
|
+
return encoded.length > 0 && encoded[0] === TAG ? [TAG, "arr", encoded] : encoded;
|
|
123
|
+
}
|
|
124
|
+
const proto = Object.getPrototypeOf(value);
|
|
125
|
+
if (proto !== null && proto !== Object.prototype) {
|
|
126
|
+
const name = value.constructor?.name ?? "value";
|
|
127
|
+
throw new TypeError(
|
|
128
|
+
`wire-codec: cannot encode a ${name} over the Lunora wire — only plain objects, arrays, and the supported built-ins (Date, Error, URL, Map, Set, ArrayBuffer/typed arrays, bigint) round-trip`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
const source = value;
|
|
132
|
+
const result = {};
|
|
133
|
+
for (const key of Object.keys(source)) {
|
|
134
|
+
const field = source[key];
|
|
135
|
+
if (field !== void 0) {
|
|
136
|
+
result[key] = encodeWire(field, depth + 1);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
};
|
|
141
|
+
const decodeWire = (value, depth = 0) => {
|
|
142
|
+
if (depth > MAX_DEPTH) {
|
|
143
|
+
throw new RangeError(`wire-codec: value nesting exceeds the ${MAX_DEPTH}-level limit`);
|
|
144
|
+
}
|
|
145
|
+
if (value === null || typeof value !== "object") {
|
|
146
|
+
return value;
|
|
147
|
+
}
|
|
148
|
+
if (Array.isArray(value)) {
|
|
149
|
+
if (value[0] === TAG) {
|
|
150
|
+
const tag = value[1];
|
|
151
|
+
switch (tag) {
|
|
152
|
+
case "-inf": {
|
|
153
|
+
return -Infinity;
|
|
154
|
+
}
|
|
155
|
+
case "arr": {
|
|
156
|
+
return value[2].map((item) => decodeWire(item, depth + 1));
|
|
157
|
+
}
|
|
158
|
+
case "bigint": {
|
|
159
|
+
const raw = value[2];
|
|
160
|
+
if (typeof raw !== "string" || raw.length > MAX_BIGINT_DIGITS || !/^-?\d+$/.test(raw)) {
|
|
161
|
+
throw new RangeError(`wire-codec: invalid or over-long bigint (max ${MAX_BIGINT_DIGITS} digits)`);
|
|
162
|
+
}
|
|
163
|
+
return BigInt(raw);
|
|
164
|
+
}
|
|
165
|
+
case "date": {
|
|
166
|
+
return new Date(decodeWire(value[2], depth + 1));
|
|
167
|
+
}
|
|
168
|
+
case "map": {
|
|
169
|
+
return new Map(value[2].map(([k, v]) => [decodeWire(k, depth + 1), decodeWire(v, depth + 1)]));
|
|
170
|
+
}
|
|
171
|
+
case "set": {
|
|
172
|
+
return new Set(value[2].map((item) => decodeWire(item, depth + 1)));
|
|
173
|
+
}
|
|
174
|
+
case "url": {
|
|
175
|
+
return new URL(value[2]);
|
|
176
|
+
}
|
|
177
|
+
case "error": {
|
|
178
|
+
const name = value[2];
|
|
179
|
+
const message = value[3];
|
|
180
|
+
const Ctor = (Object.hasOwn(ERROR_CTORS, name) ? ERROR_CTORS[name] : void 0) ?? Error;
|
|
181
|
+
const error = new Ctor(message);
|
|
182
|
+
if (error.name !== name) {
|
|
183
|
+
Object.defineProperty(error, "name", { configurable: true, value: name, writable: true });
|
|
184
|
+
}
|
|
185
|
+
Object.assign(error, decodeWire(value[4], depth + 1));
|
|
186
|
+
if (value.length > 5) {
|
|
187
|
+
Object.defineProperty(error, "cause", { configurable: true, value: decodeWire(value[5], depth + 1), writable: true });
|
|
188
|
+
}
|
|
189
|
+
return error;
|
|
190
|
+
}
|
|
191
|
+
case "bytes": {
|
|
192
|
+
const bytes = fromBase64(value[2]);
|
|
193
|
+
const ctorName = value[3] ?? "Uint8Array";
|
|
194
|
+
if (ctorName === "ArrayBuffer") {
|
|
195
|
+
return bytes.buffer.byteLength === bytes.byteLength ? bytes.buffer : bytes.slice().buffer;
|
|
196
|
+
}
|
|
197
|
+
const Ctor = Object.hasOwn(TYPED_ARRAY_CTORS, ctorName) ? TYPED_ARRAY_CTORS[ctorName] : void 0;
|
|
198
|
+
return Ctor ? new Ctor(bytes.slice().buffer) : bytes;
|
|
199
|
+
}
|
|
200
|
+
case "inf": {
|
|
201
|
+
return Infinity;
|
|
202
|
+
}
|
|
203
|
+
case "nan": {
|
|
204
|
+
return Number.NaN;
|
|
205
|
+
}
|
|
206
|
+
case "undefined": {
|
|
207
|
+
return void 0;
|
|
208
|
+
}
|
|
209
|
+
default: {
|
|
210
|
+
return value.map((item) => decodeWire(item, depth + 1));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return value.map((item) => decodeWire(item, depth + 1));
|
|
215
|
+
}
|
|
216
|
+
const source = value;
|
|
217
|
+
const result = {};
|
|
218
|
+
for (const key of Object.keys(source)) {
|
|
219
|
+
const decoded = decodeWire(source[key], depth + 1);
|
|
220
|
+
if (key === UNSAFE_KEY) {
|
|
221
|
+
Object.defineProperty(result, key, { configurable: true, enumerable: true, value: decoded, writable: true });
|
|
222
|
+
} else {
|
|
223
|
+
result[key] = decoded;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return result;
|
|
227
|
+
};
|
|
228
|
+
|
|
11
229
|
class Listeners {
|
|
12
230
|
listeners = /* @__PURE__ */ new Set();
|
|
13
231
|
add(listener) {
|
|
@@ -34,6 +252,7 @@ class Listeners {
|
|
|
34
252
|
}
|
|
35
253
|
|
|
36
254
|
const RPC_PATH = "/_lunora/rpc";
|
|
255
|
+
const RPC_BATCH_PATH = "/_lunora/rpc-batch";
|
|
37
256
|
const WS_PATH = "/_lunora/ws";
|
|
38
257
|
const bucketQuery = (bucket) => bucket === void 0 || bucket === "" ? "" : `&bucket=${encodeURIComponent(bucket)}`;
|
|
39
258
|
const rollbackOptimistic = (optimisticRollbacks) => {
|
|
@@ -171,6 +390,34 @@ const sendOn = (conn, message) => {
|
|
|
171
390
|
return false;
|
|
172
391
|
}
|
|
173
392
|
};
|
|
393
|
+
const reconstructError = (errorBody) => {
|
|
394
|
+
const error = new Error(errorBody.message ?? "request failed");
|
|
395
|
+
error.code = errorBody.code;
|
|
396
|
+
if (errorBody.data !== void 0) {
|
|
397
|
+
error.data = decodeWire(errorBody.data);
|
|
398
|
+
}
|
|
399
|
+
return error;
|
|
400
|
+
};
|
|
401
|
+
const encodeCallArgs = (payload, label) => {
|
|
402
|
+
try {
|
|
403
|
+
return encodeWire(payload);
|
|
404
|
+
} catch (error) {
|
|
405
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
406
|
+
throw new TypeError(`LunoraClient: cannot encode ${label} — ${reason}`, error instanceof Error ? { cause: error } : void 0);
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
const demuxBatchResults = (rawResults, count) => {
|
|
410
|
+
const slots = Array.from({ length: count });
|
|
411
|
+
for (const entry of rawResults) {
|
|
412
|
+
if (typeof entry.id !== "number" || entry.id < 0 || entry.id >= count) {
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
const inner = entry.body;
|
|
416
|
+
slots[entry.id] = inner && "error" in inner && inner.error ? { error: reconstructError(inner.error), ok: false } : { ok: true, value: decodeWire(inner?.result) };
|
|
417
|
+
}
|
|
418
|
+
return slots.map((slot) => slot ?? { error: new Error("batch call returned no result"), ok: false });
|
|
419
|
+
};
|
|
420
|
+
const TRANSIENT_BATCH_ERROR_CODES = /* @__PURE__ */ new Set(["SHARD_ERROR", "SHARD_UNAVAILABLE"]);
|
|
174
421
|
class LunoraClient {
|
|
175
422
|
/** Hard cap on concurrently-buffered pokes — a backstop that reclaims buffers abandoned by a mid-poke disconnect (no `pokeEnd`). Far above any real concurrent-in-flight count. */
|
|
176
423
|
static MAX_POKE_BUFFERS = 256;
|
|
@@ -650,7 +897,7 @@ class LunoraClient {
|
|
|
650
897
|
this.ensureSocket(options.shardKey);
|
|
651
898
|
const conn = this.getConnection(options.shardKey);
|
|
652
899
|
if (conn) {
|
|
653
|
-
sendOn(conn, { data, topic, type: "whisper" });
|
|
900
|
+
sendOn(conn, { data: encodeCallArgs(data ?? null, `whisper data for topic '${topic}'`), topic, type: "whisper" });
|
|
654
901
|
}
|
|
655
902
|
}
|
|
656
903
|
/**
|
|
@@ -723,6 +970,62 @@ class LunoraClient {
|
|
|
723
970
|
}
|
|
724
971
|
return await this.rpc(function_.__lunoraRef, args, options.shardKey, { attachBookmark: true });
|
|
725
972
|
}
|
|
973
|
+
/**
|
|
974
|
+
* Batch several independent calls into ONE round trip (plan 088). Each call is
|
|
975
|
+
* dispatched server-side exactly as an individual RPC — per-shard
|
|
976
|
+
* authorization, `(identity, mutationId)` idempotency, and custom-mutator
|
|
977
|
+
* watermark ordering are all preserved — and the worker splits the batch by
|
|
978
|
+
* shard so calls to different shards fan out to their own DOs. Results are
|
|
979
|
+
* demuxed back in input order; a failing call does NOT fail the batch (its
|
|
980
|
+
* slot carries `{ ok: false, error }`, with `.code`/`.data` reconstructed like
|
|
981
|
+
* a single call). Args/results ride the value codec (bytes/bigint survive).
|
|
982
|
+
*
|
|
983
|
+
* No promise pipelining and no capability passing — a call's args cannot
|
|
984
|
+
* reference another call's result (see plan 088 §fence; capabilities are
|
|
985
|
+
* incompatible with DO hibernation).
|
|
986
|
+
*/
|
|
987
|
+
async batch(calls) {
|
|
988
|
+
if (this.closed) {
|
|
989
|
+
throw new Error("LunoraClient is closed");
|
|
990
|
+
}
|
|
991
|
+
if (!this.fetchImpl) {
|
|
992
|
+
throw new Error("LunoraClient: no `fetch` implementation available");
|
|
993
|
+
}
|
|
994
|
+
if (calls.length === 0) {
|
|
995
|
+
return [];
|
|
996
|
+
}
|
|
997
|
+
const response = await this.fetchImpl(joinUrl(this.url, RPC_BATCH_PATH), {
|
|
998
|
+
body: JSON.stringify({
|
|
999
|
+
calls: calls.map((call, index) => {
|
|
1000
|
+
return {
|
|
1001
|
+
args: encodeCallArgs(call.args ?? {}, `args for batch call '${call.fn.__lunoraRef}'`),
|
|
1002
|
+
functionPath: call.fn.__lunoraRef,
|
|
1003
|
+
id: index,
|
|
1004
|
+
shardKey: call.shardKey
|
|
1005
|
+
};
|
|
1006
|
+
})
|
|
1007
|
+
}),
|
|
1008
|
+
headers: this.rpcRequestHeaders({ attachBookmark: true }),
|
|
1009
|
+
method: "POST"
|
|
1010
|
+
});
|
|
1011
|
+
const bookmark = response.headers.get("x-d1-bookmark");
|
|
1012
|
+
if (bookmark) {
|
|
1013
|
+
this.bookmark.set(bookmark);
|
|
1014
|
+
}
|
|
1015
|
+
let body;
|
|
1016
|
+
try {
|
|
1017
|
+
body = await response.json();
|
|
1018
|
+
} catch {
|
|
1019
|
+
throw new Error(`LunoraClient: batch response was not JSON (status ${response.status.toString()})`);
|
|
1020
|
+
}
|
|
1021
|
+
if (!response.ok || body.error && !body.results) {
|
|
1022
|
+
if (body.error) {
|
|
1023
|
+
throw reconstructError(body.error);
|
|
1024
|
+
}
|
|
1025
|
+
throw new Error(`LunoraClient: batch request failed (status ${response.status.toString()})`);
|
|
1026
|
+
}
|
|
1027
|
+
return demuxBatchResults(body.results ?? [], calls.length);
|
|
1028
|
+
}
|
|
726
1029
|
/**
|
|
727
1030
|
* Invoke a mutation. Errors propagate as rejections.
|
|
728
1031
|
*
|
|
@@ -1549,7 +1852,14 @@ class LunoraClient {
|
|
|
1549
1852
|
const conn = this.getConnection(shardKey);
|
|
1550
1853
|
const message = {
|
|
1551
1854
|
id,
|
|
1552
|
-
|
|
1855
|
+
// Wire-encode the stream args so `bigint`/bytes survive the send (raw
|
|
1856
|
+
// `JSON.stringify` throws on a bigint); the shard `decodeWire`s them
|
|
1857
|
+
// before invoking the stream handler.
|
|
1858
|
+
query: {
|
|
1859
|
+
args: encodeCallArgs(argsRecord, `stream args for '${function_.__lunoraRef}'`),
|
|
1860
|
+
functionPath: function_.__lunoraRef,
|
|
1861
|
+
shardKey
|
|
1862
|
+
},
|
|
1553
1863
|
type: "stream"
|
|
1554
1864
|
};
|
|
1555
1865
|
const sentImmediately = conn?.wsState === "open" && sendOn(conn, message);
|
|
@@ -1975,7 +2285,10 @@ class LunoraClient {
|
|
|
1975
2285
|
}
|
|
1976
2286
|
const headers = this.rpcRequestHeaders(flags);
|
|
1977
2287
|
const response = await this.fetchImpl(joinUrl(this.url, RPC_PATH), {
|
|
1978
|
-
|
|
2288
|
+
// `encodeWire` tags leaves plain JSON can't carry (`bigint`,
|
|
2289
|
+
// `ArrayBuffer`/typed arrays, `NaN`/±Infinity); a pure-JSON `args`
|
|
2290
|
+
// encodes byte-identically, so a pre-codec server still interops.
|
|
2291
|
+
body: JSON.stringify({ args: encodeCallArgs(args, `args for '${functionPath}'`), functionPath, shardKey }),
|
|
1979
2292
|
headers,
|
|
1980
2293
|
method: "POST"
|
|
1981
2294
|
});
|
|
@@ -1993,9 +2306,7 @@ class LunoraClient {
|
|
|
1993
2306
|
throw new Error(`LunoraClient: response was not JSON (status ${response.status.toString()}${statusText})`);
|
|
1994
2307
|
}
|
|
1995
2308
|
if ("error" in body) {
|
|
1996
|
-
|
|
1997
|
-
error.code = body.error.code;
|
|
1998
|
-
throw error;
|
|
2309
|
+
throw reconstructError(body.error);
|
|
1999
2310
|
}
|
|
2000
2311
|
if (!response.ok) {
|
|
2001
2312
|
const statusText = response.statusText ? ` ${response.statusText}` : "";
|
|
@@ -2003,7 +2314,7 @@ class LunoraClient {
|
|
|
2003
2314
|
}
|
|
2004
2315
|
flags.onMutationAck?.(body.lastMutationId);
|
|
2005
2316
|
flags.onCommitCursor?.(body.commitCursor);
|
|
2006
|
-
return body.result;
|
|
2317
|
+
return decodeWire(body.result);
|
|
2007
2318
|
}
|
|
2008
2319
|
/**
|
|
2009
2320
|
* Authenticated request to a non-RPC admin endpoint (the scheduler list /
|
|
@@ -2325,7 +2636,7 @@ class LunoraClient {
|
|
|
2325
2636
|
case "chunk": {
|
|
2326
2637
|
const { data, id } = message;
|
|
2327
2638
|
const stream = this.streams.get(id);
|
|
2328
|
-
stream?.handle.push(data);
|
|
2639
|
+
stream?.handle.push(decodeWire(data));
|
|
2329
2640
|
return;
|
|
2330
2641
|
}
|
|
2331
2642
|
case "complete": {
|
|
@@ -2405,7 +2716,9 @@ class LunoraClient {
|
|
|
2405
2716
|
return;
|
|
2406
2717
|
}
|
|
2407
2718
|
const existing = buffer.parts.get(message.shapeId) ?? [];
|
|
2408
|
-
|
|
2719
|
+
for (const op of message.rowsPatch) {
|
|
2720
|
+
existing.push(op.value === void 0 ? op : { ...op, value: decodeWire(op.value) });
|
|
2721
|
+
}
|
|
2409
2722
|
buffer.parts.set(message.shapeId, existing);
|
|
2410
2723
|
if (message.lastMutationId !== void 0) {
|
|
2411
2724
|
buffer.lastMutationId.set(message.shapeId, message.lastMutationId);
|
|
@@ -2551,9 +2864,9 @@ class LunoraClient {
|
|
|
2551
2864
|
// eslint-disable-next-line class-methods-use-this -- instance method for symmetry with the other message handlers; reads no shared client state
|
|
2552
2865
|
resolveDataPayload(message, state) {
|
|
2553
2866
|
if ("data" in message && message.data !== void 0) {
|
|
2554
|
-
return message.data;
|
|
2867
|
+
return decodeWire(message.data);
|
|
2555
2868
|
}
|
|
2556
|
-
const
|
|
2869
|
+
const delta = decodeWire(message.delta);
|
|
2557
2870
|
if (isMutationDelta(delta) && state.serverBase !== void 0) {
|
|
2558
2871
|
const merged = applyDelta(state.serverBase, delta);
|
|
2559
2872
|
if (merged !== void 0) {
|
|
@@ -2568,9 +2881,10 @@ class LunoraClient {
|
|
|
2568
2881
|
if (!handlers) {
|
|
2569
2882
|
return;
|
|
2570
2883
|
}
|
|
2884
|
+
const data = decodeWire(message.data);
|
|
2571
2885
|
for (const handler of handlers) {
|
|
2572
2886
|
try {
|
|
2573
|
-
handler(
|
|
2887
|
+
handler(data, message.from);
|
|
2574
2888
|
} catch {
|
|
2575
2889
|
}
|
|
2576
2890
|
}
|
|
@@ -2674,24 +2988,123 @@ class LunoraClient {
|
|
|
2674
2988
|
async flushOfflineQueue(shardKey) {
|
|
2675
2989
|
const key = connectionKey(shardKey);
|
|
2676
2990
|
const drained = this.offlineQueue.drain((item) => connectionKey(item.shardKey) === key);
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2991
|
+
if (drained.length === 0) {
|
|
2992
|
+
return;
|
|
2993
|
+
}
|
|
2994
|
+
const currentIdentity = this.identityFingerprint();
|
|
2995
|
+
const sendable = [];
|
|
2996
|
+
for (const item of drained) {
|
|
2997
|
+
if (this.passesReplayIdentityGate(item, currentIdentity)) {
|
|
2998
|
+
sendable.push(item);
|
|
2681
2999
|
}
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
3000
|
+
}
|
|
3001
|
+
if (sendable.length === 0) {
|
|
3002
|
+
return;
|
|
3003
|
+
}
|
|
3004
|
+
const encodable = this.encodableOrSettleTerminal(sendable);
|
|
3005
|
+
if (encodable.length === 0) {
|
|
3006
|
+
return;
|
|
3007
|
+
}
|
|
3008
|
+
if (encodable.length === 1) {
|
|
3009
|
+
await this.replaySequential(encodable);
|
|
3010
|
+
return;
|
|
3011
|
+
}
|
|
3012
|
+
const toRequeue = [];
|
|
3013
|
+
for (let start = 0; start < encodable.length; start += MAX_BATCH_ENTRIES) {
|
|
3014
|
+
const chunk = encodable.slice(start, start + MAX_BATCH_ENTRIES);
|
|
3015
|
+
const outcome = await this.replayBatched(chunk);
|
|
3016
|
+
toRequeue.push(...outcome.requeue);
|
|
3017
|
+
if (outcome.stop) {
|
|
3018
|
+
toRequeue.push(...encodable.slice(start + MAX_BATCH_ENTRIES));
|
|
3019
|
+
break;
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
if (toRequeue.length > 0) {
|
|
3023
|
+
this.offlineQueue.requeue(toRequeue);
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
/**
|
|
3027
|
+
* Partition already-gated writes into the encodable ones (returned) and reject
|
|
3028
|
+
* the rest terminally. A write whose args can't be wire-encoded (e.g. a RegExp
|
|
3029
|
+
* or class instance in a `v.any()` field) can NEVER replay — the codec failure
|
|
3030
|
+
* is deterministic, not transient. Rejecting here is essential: otherwise
|
|
3031
|
+
* `encodeWire` throws mid-flush, is classified as transient (a codec error has
|
|
3032
|
+
* no `.code`), and re-queues forever — a silent hang where the caller's Promise
|
|
3033
|
+
* never settles and the optimistic write never rolls back. Encoding is cheap;
|
|
3034
|
+
* the flush is the slow reconnect path.
|
|
3035
|
+
*/
|
|
3036
|
+
encodableOrSettleTerminal(items) {
|
|
3037
|
+
const encodable = [];
|
|
3038
|
+
for (const item of items) {
|
|
3039
|
+
try {
|
|
3040
|
+
encodeCallArgs(item.args, `args for '${item.functionPath}'`);
|
|
3041
|
+
encodable.push(item);
|
|
3042
|
+
} catch (error) {
|
|
3043
|
+
this.settleReplayTerminal(item, error instanceof Error ? error : new Error(String(error)));
|
|
2693
3044
|
}
|
|
3045
|
+
}
|
|
3046
|
+
return encodable;
|
|
3047
|
+
}
|
|
3048
|
+
/**
|
|
3049
|
+
* Identity guard for one queued write about to replay: a write stamped under
|
|
3050
|
+
* one identity must never replay under another. The live `queuedIdentities`
|
|
3051
|
+
* map is the source of truth for the current session; a hydrated write whose
|
|
3052
|
+
* id isn't in the map falls back to the stamp persisted with the record
|
|
3053
|
+
* (`item.identity`), so a reload can't replay another user's queued writes.
|
|
3054
|
+
* Only legacy records (persisted before stamps were durable —
|
|
3055
|
+
* `item.identity === undefined`) replay under whatever identity is current.
|
|
3056
|
+
*
|
|
3057
|
+
* `Map.get` returns `undefined` for unstamped/hydrated ids and `item.identity`
|
|
3058
|
+
* is `undefined` for legacy records; a persisted `null` (queued while signed
|
|
3059
|
+
* out) is a real value that must not collapse into `undefined` — hence the
|
|
3060
|
+
* explicit `=== undefined` check rather than `??`. Returns `true` when the
|
|
3061
|
+
* write may replay; otherwise settles it `OFFLINE_IDENTITY_CHANGED` and returns
|
|
3062
|
+
* `false`. Either way the live stamp is consumed.
|
|
3063
|
+
*/
|
|
3064
|
+
passesReplayIdentityGate(item, currentIdentity) {
|
|
3065
|
+
const liveStamp = item.id === void 0 ? void 0 : this.queuedIdentities.get(item.id);
|
|
3066
|
+
const stamped = liveStamp === void 0 ? item.identity : liveStamp;
|
|
3067
|
+
if (stamped !== void 0 && stamped !== currentIdentity) {
|
|
2694
3068
|
this.queuedIdentities.delete(item.id ?? "");
|
|
3069
|
+
this.unpersist(item.id);
|
|
3070
|
+
const error = new Error("offline mutation skipped: auth identity changed before replay");
|
|
3071
|
+
error.code = "OFFLINE_IDENTITY_CHANGED";
|
|
3072
|
+
item.reject(error);
|
|
3073
|
+
this.emitItemSettled(item, "rejected", error);
|
|
3074
|
+
return false;
|
|
3075
|
+
}
|
|
3076
|
+
this.queuedIdentities.delete(item.id ?? "");
|
|
3077
|
+
return true;
|
|
3078
|
+
}
|
|
3079
|
+
/** Settle a write that replayed successfully: confirm its optimistic layer against the echoed commit cursor BEFORE resolving, so the gapless drop is in place when the awaiter (and any confirming frame) observes the settle. */
|
|
3080
|
+
settleReplaySuccess(item, value, commitCursor) {
|
|
3081
|
+
this.unpersist(item.id);
|
|
3082
|
+
item.onCommit?.(commitCursor);
|
|
3083
|
+
item.resolve(value);
|
|
3084
|
+
this.emitItemSettled(item, "committed");
|
|
3085
|
+
}
|
|
3086
|
+
/** Settle a write the server reached a coded verdict on: replaying would re-trigger the same failure (a poison-message loop), so drop it. */
|
|
3087
|
+
settleReplayTerminal(item, error) {
|
|
3088
|
+
this.unpersist(item.id);
|
|
3089
|
+
item.reject(error);
|
|
3090
|
+
this.emitItemSettled(item, "rejected", error);
|
|
3091
|
+
}
|
|
3092
|
+
/**
|
|
3093
|
+
* Replay already-identity-gated writes one at a time on the single-call `/rpc`
|
|
3094
|
+
* path, preserving FIFO order (parallel `.then()` chains would race the
|
|
3095
|
+
* ordering callers depend on). Each replays under its stable `mutationId` so
|
|
3096
|
+
* the server dedups a write it already committed (exactly-once). A coded error
|
|
3097
|
+
* is a server verdict (drop it); a codeless (transport/transient) failure stops
|
|
3098
|
+
* the flush and re-queues this write and every unreplayed one for the next
|
|
3099
|
+
* reconnect — their callers stay pending, and the identity guard re-applies on
|
|
3100
|
+
* retry via each record's persisted stamp.
|
|
3101
|
+
*/
|
|
3102
|
+
async replaySequential(items) {
|
|
3103
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
3104
|
+
const item = items[index];
|
|
3105
|
+
if (!item) {
|
|
3106
|
+
continue;
|
|
3107
|
+
}
|
|
2695
3108
|
try {
|
|
2696
3109
|
let commitCursor;
|
|
2697
3110
|
const value = await this.rpc(item.functionPath, item.args, item.shardKey, {
|
|
@@ -2701,22 +3114,116 @@ class LunoraClient {
|
|
|
2701
3114
|
commitCursor = cursor;
|
|
2702
3115
|
}
|
|
2703
3116
|
});
|
|
2704
|
-
this.
|
|
2705
|
-
item.onCommit?.(commitCursor);
|
|
2706
|
-
item.resolve(value);
|
|
2707
|
-
this.emitItemSettled(item, "committed");
|
|
3117
|
+
this.settleReplaySuccess(item, value, commitCursor);
|
|
2708
3118
|
} catch (error) {
|
|
2709
3119
|
if (error.code !== void 0) {
|
|
2710
|
-
this.
|
|
2711
|
-
item.reject(error);
|
|
2712
|
-
this.emitItemSettled(item, "rejected", error);
|
|
3120
|
+
this.settleReplayTerminal(item, error);
|
|
2713
3121
|
continue;
|
|
2714
3122
|
}
|
|
2715
|
-
this.offlineQueue.requeue(
|
|
3123
|
+
this.offlineQueue.requeue(items.slice(index));
|
|
2716
3124
|
return;
|
|
2717
3125
|
}
|
|
2718
3126
|
}
|
|
2719
3127
|
}
|
|
3128
|
+
/**
|
|
3129
|
+
* Coalesce already-identity-gated writes for a single shard into ONE
|
|
3130
|
+
* `/_lunora/rpc-batch` round trip (plan 088 follow-on). The worker forwards
|
|
3131
|
+
* them to the shard DO, which replays each through its single-call dispatch, so
|
|
3132
|
+
* per-entry `mutationId` idempotency and in-order application are inherited from
|
|
3133
|
+
* the proven path. Per-slot demux mirrors {@link replaySequential}'s
|
|
3134
|
+
* classification: success confirms the optimistic layer against the echoed
|
|
3135
|
+
* `commitCursor`; a coded application verdict is terminal; a transient shard
|
|
3136
|
+
* failure (`SHARD_UNAVAILABLE`/`SHARD_ERROR`), a missing slot, or a whole-batch
|
|
3137
|
+
* transport failure re-queues for the next reconnect (never dropping a durable
|
|
3138
|
+
* write). A whole-batch coded rejection (bad request / authorization denial the
|
|
3139
|
+
* server reached a verdict on) is terminal for every entry.
|
|
3140
|
+
*
|
|
3141
|
+
* Returns the writes that must be re-queued and `stop` — `true` when the whole
|
|
3142
|
+
* chunk failed at the transport level, so the caller leaves later chunks queued
|
|
3143
|
+
* rather than sending on. The caller re-queues once, in order, so requeuing is
|
|
3144
|
+
* NOT done here.
|
|
3145
|
+
*/
|
|
3146
|
+
async replayBatched(items) {
|
|
3147
|
+
if (!this.fetchImpl) {
|
|
3148
|
+
return { requeue: items, stop: true };
|
|
3149
|
+
}
|
|
3150
|
+
let response;
|
|
3151
|
+
try {
|
|
3152
|
+
response = await this.fetchImpl(joinUrl(this.url, RPC_BATCH_PATH), {
|
|
3153
|
+
body: JSON.stringify({
|
|
3154
|
+
calls: items.map((item, index) => {
|
|
3155
|
+
return {
|
|
3156
|
+
args: encodeCallArgs(item.args, `args for '${item.functionPath}'`),
|
|
3157
|
+
functionPath: item.functionPath,
|
|
3158
|
+
id: index,
|
|
3159
|
+
// Stable per-write key so the DO dedups a write it already
|
|
3160
|
+
// committed (exactly-once), exactly as the single-call replay.
|
|
3161
|
+
mutationId: item.id,
|
|
3162
|
+
shardKey: item.shardKey
|
|
3163
|
+
};
|
|
3164
|
+
})
|
|
3165
|
+
}),
|
|
3166
|
+
headers: this.rpcRequestHeaders({ attachBookmark: true }),
|
|
3167
|
+
method: "POST"
|
|
3168
|
+
});
|
|
3169
|
+
} catch {
|
|
3170
|
+
return { requeue: items, stop: true };
|
|
3171
|
+
}
|
|
3172
|
+
const bookmark = response.headers.get("x-d1-bookmark");
|
|
3173
|
+
if (bookmark) {
|
|
3174
|
+
this.bookmark.set(bookmark);
|
|
3175
|
+
}
|
|
3176
|
+
let body;
|
|
3177
|
+
try {
|
|
3178
|
+
body = await response.json();
|
|
3179
|
+
} catch {
|
|
3180
|
+
return { requeue: items, stop: true };
|
|
3181
|
+
}
|
|
3182
|
+
if (!body.results) {
|
|
3183
|
+
if (body.error) {
|
|
3184
|
+
const error = reconstructError(body.error);
|
|
3185
|
+
for (const item of items) {
|
|
3186
|
+
this.settleReplayTerminal(item, error);
|
|
3187
|
+
}
|
|
3188
|
+
return { requeue: [], stop: false };
|
|
3189
|
+
}
|
|
3190
|
+
return { requeue: items, stop: true };
|
|
3191
|
+
}
|
|
3192
|
+
return { requeue: this.settleReplayBatchSlots(items, body.results), stop: false };
|
|
3193
|
+
}
|
|
3194
|
+
/**
|
|
3195
|
+
* Demux a `/_lunora/rpc-batch` reply back onto the queued writes it replayed,
|
|
3196
|
+
* in input order. Each slot's envelope classifies its write the same way
|
|
3197
|
+
* {@link replaySequential} does: a success confirms the optimistic layer
|
|
3198
|
+
* against the echoed `commitCursor`; a coded application verdict is terminal;
|
|
3199
|
+
* a transient shard failure ({@link TRANSIENT_BATCH_ERROR_CODES}) or a slot the
|
|
3200
|
+
* server never returned is returned for the caller to re-queue.
|
|
3201
|
+
* @returns the writes that must be re-queued (transient slots), in input order
|
|
3202
|
+
*/
|
|
3203
|
+
settleReplayBatchSlots(items, results) {
|
|
3204
|
+
const bySlot = /* @__PURE__ */ new Map();
|
|
3205
|
+
for (const entry of results) {
|
|
3206
|
+
if (typeof entry.id === "number" && entry.body !== void 0) {
|
|
3207
|
+
bySlot.set(entry.id, entry.body);
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
const requeue = [];
|
|
3211
|
+
for (const [index, item] of items.entries()) {
|
|
3212
|
+
const inner = bySlot.get(index);
|
|
3213
|
+
if (inner === void 0) {
|
|
3214
|
+
requeue.push(item);
|
|
3215
|
+
} else if ("error" in inner) {
|
|
3216
|
+
if (TRANSIENT_BATCH_ERROR_CODES.has(inner.error.code)) {
|
|
3217
|
+
requeue.push(item);
|
|
3218
|
+
} else {
|
|
3219
|
+
this.settleReplayTerminal(item, reconstructError(inner.error));
|
|
3220
|
+
}
|
|
3221
|
+
} else {
|
|
3222
|
+
this.settleReplaySuccess(item, decodeWire(inner.result), inner.commitCursor);
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
return requeue;
|
|
3226
|
+
}
|
|
2720
3227
|
}
|
|
2721
3228
|
|
|
2722
3229
|
export { LunoraClient };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { S as SubscriptionRegistry } from './subscription-DoyO04-2.mjs';
|
|
@@ -358,6 +358,7 @@ interface RpcEnvelope {
|
|
|
358
358
|
type RpcResponseBody = {
|
|
359
359
|
error: {
|
|
360
360
|
code: string;
|
|
361
|
+
data?: unknown;
|
|
361
362
|
message: string;
|
|
362
363
|
};
|
|
363
364
|
} | {
|
|
@@ -1151,6 +1152,19 @@ interface SyncWatermark {
|
|
|
1151
1152
|
checkpoint?: number;
|
|
1152
1153
|
mutationId?: number;
|
|
1153
1154
|
}
|
|
1155
|
+
/** An `Error` carrying the server's machine-readable `code` and (for a `LunoraError`) structured `data`. The client's public error contract for RPC/batch failures. */
|
|
1156
|
+
type LunoraClientError = Error & {
|
|
1157
|
+
code?: string;
|
|
1158
|
+
data?: unknown;
|
|
1159
|
+
};
|
|
1160
|
+
/** One demuxed result slot of a {@link LunoraClient.batch} call (plan 088). */
|
|
1161
|
+
type BatchSlot = {
|
|
1162
|
+
error: LunoraClientError;
|
|
1163
|
+
ok: false;
|
|
1164
|
+
} | {
|
|
1165
|
+
ok: true;
|
|
1166
|
+
value: unknown;
|
|
1167
|
+
};
|
|
1154
1168
|
/**
|
|
1155
1169
|
* Lunora browser/edge client. Talks RPC over HTTP and real-time deltas over
|
|
1156
1170
|
* a single multiplexed WebSocket.
|
|
@@ -1498,6 +1512,25 @@ declare class LunoraClient {
|
|
|
1498
1512
|
shardKey?: string;
|
|
1499
1513
|
}): Promise<ReturnOf<F>>;
|
|
1500
1514
|
/**
|
|
1515
|
+
* Batch several independent calls into ONE round trip (plan 088). Each call is
|
|
1516
|
+
* dispatched server-side exactly as an individual RPC — per-shard
|
|
1517
|
+
* authorization, `(identity, mutationId)` idempotency, and custom-mutator
|
|
1518
|
+
* watermark ordering are all preserved — and the worker splits the batch by
|
|
1519
|
+
* shard so calls to different shards fan out to their own DOs. Results are
|
|
1520
|
+
* demuxed back in input order; a failing call does NOT fail the batch (its
|
|
1521
|
+
* slot carries `{ ok: false, error }`, with `.code`/`.data` reconstructed like
|
|
1522
|
+
* a single call). Args/results ride the value codec (bytes/bigint survive).
|
|
1523
|
+
*
|
|
1524
|
+
* No promise pipelining and no capability passing — a call's args cannot
|
|
1525
|
+
* reference another call's result (see plan 088 §fence; capabilities are
|
|
1526
|
+
* incompatible with DO hibernation).
|
|
1527
|
+
*/
|
|
1528
|
+
batch(calls: ReadonlyArray<{
|
|
1529
|
+
args?: Record<string, unknown>;
|
|
1530
|
+
fn: FunctionReference;
|
|
1531
|
+
shardKey?: string;
|
|
1532
|
+
}>): Promise<BatchSlot[]>;
|
|
1533
|
+
/**
|
|
1501
1534
|
* Invoke a mutation. Errors propagate as rejections.
|
|
1502
1535
|
*
|
|
1503
1536
|
* Offline-queue semantics: a mutation is queued (and replayed on reconnect)
|
|
@@ -2202,5 +2235,77 @@ declare class LunoraClient {
|
|
|
2202
2235
|
*/
|
|
2203
2236
|
private clearQueryCacheForIdentityChange;
|
|
2204
2237
|
private flushOfflineQueue;
|
|
2238
|
+
/**
|
|
2239
|
+
* Partition already-gated writes into the encodable ones (returned) and reject
|
|
2240
|
+
* the rest terminally. A write whose args can't be wire-encoded (e.g. a RegExp
|
|
2241
|
+
* or class instance in a `v.any()` field) can NEVER replay — the codec failure
|
|
2242
|
+
* is deterministic, not transient. Rejecting here is essential: otherwise
|
|
2243
|
+
* `encodeWire` throws mid-flush, is classified as transient (a codec error has
|
|
2244
|
+
* no `.code`), and re-queues forever — a silent hang where the caller's Promise
|
|
2245
|
+
* never settles and the optimistic write never rolls back. Encoding is cheap;
|
|
2246
|
+
* the flush is the slow reconnect path.
|
|
2247
|
+
*/
|
|
2248
|
+
private encodableOrSettleTerminal;
|
|
2249
|
+
/**
|
|
2250
|
+
* Identity guard for one queued write about to replay: a write stamped under
|
|
2251
|
+
* one identity must never replay under another. The live `queuedIdentities`
|
|
2252
|
+
* map is the source of truth for the current session; a hydrated write whose
|
|
2253
|
+
* id isn't in the map falls back to the stamp persisted with the record
|
|
2254
|
+
* (`item.identity`), so a reload can't replay another user's queued writes.
|
|
2255
|
+
* Only legacy records (persisted before stamps were durable —
|
|
2256
|
+
* `item.identity === undefined`) replay under whatever identity is current.
|
|
2257
|
+
*
|
|
2258
|
+
* `Map.get` returns `undefined` for unstamped/hydrated ids and `item.identity`
|
|
2259
|
+
* is `undefined` for legacy records; a persisted `null` (queued while signed
|
|
2260
|
+
* out) is a real value that must not collapse into `undefined` — hence the
|
|
2261
|
+
* explicit `=== undefined` check rather than `??`. Returns `true` when the
|
|
2262
|
+
* write may replay; otherwise settles it `OFFLINE_IDENTITY_CHANGED` and returns
|
|
2263
|
+
* `false`. Either way the live stamp is consumed.
|
|
2264
|
+
*/
|
|
2265
|
+
private passesReplayIdentityGate;
|
|
2266
|
+
/** Settle a write that replayed successfully: confirm its optimistic layer against the echoed commit cursor BEFORE resolving, so the gapless drop is in place when the awaiter (and any confirming frame) observes the settle. */
|
|
2267
|
+
private settleReplaySuccess;
|
|
2268
|
+
/** Settle a write the server reached a coded verdict on: replaying would re-trigger the same failure (a poison-message loop), so drop it. */
|
|
2269
|
+
private settleReplayTerminal;
|
|
2270
|
+
/**
|
|
2271
|
+
* Replay already-identity-gated writes one at a time on the single-call `/rpc`
|
|
2272
|
+
* path, preserving FIFO order (parallel `.then()` chains would race the
|
|
2273
|
+
* ordering callers depend on). Each replays under its stable `mutationId` so
|
|
2274
|
+
* the server dedups a write it already committed (exactly-once). A coded error
|
|
2275
|
+
* is a server verdict (drop it); a codeless (transport/transient) failure stops
|
|
2276
|
+
* the flush and re-queues this write and every unreplayed one for the next
|
|
2277
|
+
* reconnect — their callers stay pending, and the identity guard re-applies on
|
|
2278
|
+
* retry via each record's persisted stamp.
|
|
2279
|
+
*/
|
|
2280
|
+
private replaySequential;
|
|
2281
|
+
/**
|
|
2282
|
+
* Coalesce already-identity-gated writes for a single shard into ONE
|
|
2283
|
+
* `/_lunora/rpc-batch` round trip (plan 088 follow-on). The worker forwards
|
|
2284
|
+
* them to the shard DO, which replays each through its single-call dispatch, so
|
|
2285
|
+
* per-entry `mutationId` idempotency and in-order application are inherited from
|
|
2286
|
+
* the proven path. Per-slot demux mirrors {@link replaySequential}'s
|
|
2287
|
+
* classification: success confirms the optimistic layer against the echoed
|
|
2288
|
+
* `commitCursor`; a coded application verdict is terminal; a transient shard
|
|
2289
|
+
* failure (`SHARD_UNAVAILABLE`/`SHARD_ERROR`), a missing slot, or a whole-batch
|
|
2290
|
+
* transport failure re-queues for the next reconnect (never dropping a durable
|
|
2291
|
+
* write). A whole-batch coded rejection (bad request / authorization denial the
|
|
2292
|
+
* server reached a verdict on) is terminal for every entry.
|
|
2293
|
+
*
|
|
2294
|
+
* Returns the writes that must be re-queued and `stop` — `true` when the whole
|
|
2295
|
+
* chunk failed at the transport level, so the caller leaves later chunks queued
|
|
2296
|
+
* rather than sending on. The caller re-queues once, in order, so requeuing is
|
|
2297
|
+
* NOT done here.
|
|
2298
|
+
*/
|
|
2299
|
+
private replayBatched;
|
|
2300
|
+
/**
|
|
2301
|
+
* Demux a `/_lunora/rpc-batch` reply back onto the queued writes it replayed,
|
|
2302
|
+
* in input order. Each slot's envelope classifies its write the same way
|
|
2303
|
+
* {@link replaySequential} does: a success confirms the optimistic layer
|
|
2304
|
+
* against the echoed `commitCursor`; a coded application verdict is terminal;
|
|
2305
|
+
* a transient shard failure ({@link TRANSIENT_BATCH_ERROR_CODES}) or a slot the
|
|
2306
|
+
* server never returned is returned for the caller to re-queue.
|
|
2307
|
+
* @returns the writes that must be re-queued (transient slots), in input order
|
|
2308
|
+
*/
|
|
2309
|
+
private settleReplayBatchSlots;
|
|
2205
2310
|
}
|
|
2206
|
-
export {
|
|
2311
|
+
export { SubscriptionCallback as $, ArgsOf as A, BookmarkStorage as B, CachedQuery as C, DEFAULT_MAX_BUFFER as D, ScheduleRecord as E, FunctionReference as F, GlobalFacetResult as G, SchedulerPoolStatus as H, SchedulerStatus as I, ServerMessage as J, ServerPokeEndMessage as K, LunoraClient as L, MutationCallOptions as M, ServerPokePartMessage as N, OfflineQueueOptions as O, Preloaded as P, QueryCacheAdapter as Q, ReturnOf as R, SubscriptionError as S, ServerPokeStartMessage as T, User as U, ShardTrafficEntry as V, ShardTrafficResult as W, StorageListPage as X, StorageObject as Y, StreamHandle as Z, StreamIterable as _, Unsubscribe as a, SubscriptionRegistry as a0, SubscriptionState as a1, SyncWatermark as a2, WorkflowInstanceAction as a3, WorkflowInstanceDetail as a4, WorkflowInstancePage as a5, WorkflowInstanceStatus as a6, WorkflowInstanceSummary as a7, WorkflowStepDetail as a8, createLocalStore as a9, createStream as aa, SubscriptionErrorCallback as b, PersistenceAdapter as c, ReconnectOptions as d, BatchSlot as e, ClientMessage as f, ClientShapeSubscribeMessage as g, ClientShapeUnsubscribeMessage as h, ConnectionStatus as i, FunctionArgumentDescriptor as j, FunctionDescriptor as k, GlobalFacetValue as l, GlobalFilterClause as m, GlobalTableInfo as n, GlobalTablePage as o, LunoraClientError as p, LunoraClientOptions as q, MutationSettledEvent as r, OptimisticLocalStore as s, OptimisticUpdate as t, OutboxMutation as u, OutboxSink as v, PersistedMutation as w, RowOp as x, RpcEnvelope as y, RpcResponseBody as z };
|
|
@@ -358,6 +358,7 @@ interface RpcEnvelope {
|
|
|
358
358
|
type RpcResponseBody = {
|
|
359
359
|
error: {
|
|
360
360
|
code: string;
|
|
361
|
+
data?: unknown;
|
|
361
362
|
message: string;
|
|
362
363
|
};
|
|
363
364
|
} | {
|
|
@@ -1151,6 +1152,19 @@ interface SyncWatermark {
|
|
|
1151
1152
|
checkpoint?: number;
|
|
1152
1153
|
mutationId?: number;
|
|
1153
1154
|
}
|
|
1155
|
+
/** An `Error` carrying the server's machine-readable `code` and (for a `LunoraError`) structured `data`. The client's public error contract for RPC/batch failures. */
|
|
1156
|
+
type LunoraClientError = Error & {
|
|
1157
|
+
code?: string;
|
|
1158
|
+
data?: unknown;
|
|
1159
|
+
};
|
|
1160
|
+
/** One demuxed result slot of a {@link LunoraClient.batch} call (plan 088). */
|
|
1161
|
+
type BatchSlot = {
|
|
1162
|
+
error: LunoraClientError;
|
|
1163
|
+
ok: false;
|
|
1164
|
+
} | {
|
|
1165
|
+
ok: true;
|
|
1166
|
+
value: unknown;
|
|
1167
|
+
};
|
|
1154
1168
|
/**
|
|
1155
1169
|
* Lunora browser/edge client. Talks RPC over HTTP and real-time deltas over
|
|
1156
1170
|
* a single multiplexed WebSocket.
|
|
@@ -1498,6 +1512,25 @@ declare class LunoraClient {
|
|
|
1498
1512
|
shardKey?: string;
|
|
1499
1513
|
}): Promise<ReturnOf<F>>;
|
|
1500
1514
|
/**
|
|
1515
|
+
* Batch several independent calls into ONE round trip (plan 088). Each call is
|
|
1516
|
+
* dispatched server-side exactly as an individual RPC — per-shard
|
|
1517
|
+
* authorization, `(identity, mutationId)` idempotency, and custom-mutator
|
|
1518
|
+
* watermark ordering are all preserved — and the worker splits the batch by
|
|
1519
|
+
* shard so calls to different shards fan out to their own DOs. Results are
|
|
1520
|
+
* demuxed back in input order; a failing call does NOT fail the batch (its
|
|
1521
|
+
* slot carries `{ ok: false, error }`, with `.code`/`.data` reconstructed like
|
|
1522
|
+
* a single call). Args/results ride the value codec (bytes/bigint survive).
|
|
1523
|
+
*
|
|
1524
|
+
* No promise pipelining and no capability passing — a call's args cannot
|
|
1525
|
+
* reference another call's result (see plan 088 §fence; capabilities are
|
|
1526
|
+
* incompatible with DO hibernation).
|
|
1527
|
+
*/
|
|
1528
|
+
batch(calls: ReadonlyArray<{
|
|
1529
|
+
args?: Record<string, unknown>;
|
|
1530
|
+
fn: FunctionReference;
|
|
1531
|
+
shardKey?: string;
|
|
1532
|
+
}>): Promise<BatchSlot[]>;
|
|
1533
|
+
/**
|
|
1501
1534
|
* Invoke a mutation. Errors propagate as rejections.
|
|
1502
1535
|
*
|
|
1503
1536
|
* Offline-queue semantics: a mutation is queued (and replayed on reconnect)
|
|
@@ -2202,5 +2235,77 @@ declare class LunoraClient {
|
|
|
2202
2235
|
*/
|
|
2203
2236
|
private clearQueryCacheForIdentityChange;
|
|
2204
2237
|
private flushOfflineQueue;
|
|
2238
|
+
/**
|
|
2239
|
+
* Partition already-gated writes into the encodable ones (returned) and reject
|
|
2240
|
+
* the rest terminally. A write whose args can't be wire-encoded (e.g. a RegExp
|
|
2241
|
+
* or class instance in a `v.any()` field) can NEVER replay — the codec failure
|
|
2242
|
+
* is deterministic, not transient. Rejecting here is essential: otherwise
|
|
2243
|
+
* `encodeWire` throws mid-flush, is classified as transient (a codec error has
|
|
2244
|
+
* no `.code`), and re-queues forever — a silent hang where the caller's Promise
|
|
2245
|
+
* never settles and the optimistic write never rolls back. Encoding is cheap;
|
|
2246
|
+
* the flush is the slow reconnect path.
|
|
2247
|
+
*/
|
|
2248
|
+
private encodableOrSettleTerminal;
|
|
2249
|
+
/**
|
|
2250
|
+
* Identity guard for one queued write about to replay: a write stamped under
|
|
2251
|
+
* one identity must never replay under another. The live `queuedIdentities`
|
|
2252
|
+
* map is the source of truth for the current session; a hydrated write whose
|
|
2253
|
+
* id isn't in the map falls back to the stamp persisted with the record
|
|
2254
|
+
* (`item.identity`), so a reload can't replay another user's queued writes.
|
|
2255
|
+
* Only legacy records (persisted before stamps were durable —
|
|
2256
|
+
* `item.identity === undefined`) replay under whatever identity is current.
|
|
2257
|
+
*
|
|
2258
|
+
* `Map.get` returns `undefined` for unstamped/hydrated ids and `item.identity`
|
|
2259
|
+
* is `undefined` for legacy records; a persisted `null` (queued while signed
|
|
2260
|
+
* out) is a real value that must not collapse into `undefined` — hence the
|
|
2261
|
+
* explicit `=== undefined` check rather than `??`. Returns `true` when the
|
|
2262
|
+
* write may replay; otherwise settles it `OFFLINE_IDENTITY_CHANGED` and returns
|
|
2263
|
+
* `false`. Either way the live stamp is consumed.
|
|
2264
|
+
*/
|
|
2265
|
+
private passesReplayIdentityGate;
|
|
2266
|
+
/** Settle a write that replayed successfully: confirm its optimistic layer against the echoed commit cursor BEFORE resolving, so the gapless drop is in place when the awaiter (and any confirming frame) observes the settle. */
|
|
2267
|
+
private settleReplaySuccess;
|
|
2268
|
+
/** Settle a write the server reached a coded verdict on: replaying would re-trigger the same failure (a poison-message loop), so drop it. */
|
|
2269
|
+
private settleReplayTerminal;
|
|
2270
|
+
/**
|
|
2271
|
+
* Replay already-identity-gated writes one at a time on the single-call `/rpc`
|
|
2272
|
+
* path, preserving FIFO order (parallel `.then()` chains would race the
|
|
2273
|
+
* ordering callers depend on). Each replays under its stable `mutationId` so
|
|
2274
|
+
* the server dedups a write it already committed (exactly-once). A coded error
|
|
2275
|
+
* is a server verdict (drop it); a codeless (transport/transient) failure stops
|
|
2276
|
+
* the flush and re-queues this write and every unreplayed one for the next
|
|
2277
|
+
* reconnect — their callers stay pending, and the identity guard re-applies on
|
|
2278
|
+
* retry via each record's persisted stamp.
|
|
2279
|
+
*/
|
|
2280
|
+
private replaySequential;
|
|
2281
|
+
/**
|
|
2282
|
+
* Coalesce already-identity-gated writes for a single shard into ONE
|
|
2283
|
+
* `/_lunora/rpc-batch` round trip (plan 088 follow-on). The worker forwards
|
|
2284
|
+
* them to the shard DO, which replays each through its single-call dispatch, so
|
|
2285
|
+
* per-entry `mutationId` idempotency and in-order application are inherited from
|
|
2286
|
+
* the proven path. Per-slot demux mirrors {@link replaySequential}'s
|
|
2287
|
+
* classification: success confirms the optimistic layer against the echoed
|
|
2288
|
+
* `commitCursor`; a coded application verdict is terminal; a transient shard
|
|
2289
|
+
* failure (`SHARD_UNAVAILABLE`/`SHARD_ERROR`), a missing slot, or a whole-batch
|
|
2290
|
+
* transport failure re-queues for the next reconnect (never dropping a durable
|
|
2291
|
+
* write). A whole-batch coded rejection (bad request / authorization denial the
|
|
2292
|
+
* server reached a verdict on) is terminal for every entry.
|
|
2293
|
+
*
|
|
2294
|
+
* Returns the writes that must be re-queued and `stop` — `true` when the whole
|
|
2295
|
+
* chunk failed at the transport level, so the caller leaves later chunks queued
|
|
2296
|
+
* rather than sending on. The caller re-queues once, in order, so requeuing is
|
|
2297
|
+
* NOT done here.
|
|
2298
|
+
*/
|
|
2299
|
+
private replayBatched;
|
|
2300
|
+
/**
|
|
2301
|
+
* Demux a `/_lunora/rpc-batch` reply back onto the queued writes it replayed,
|
|
2302
|
+
* in input order. Each slot's envelope classifies its write the same way
|
|
2303
|
+
* {@link replaySequential} does: a success confirms the optimistic layer
|
|
2304
|
+
* against the echoed `commitCursor`; a coded application verdict is terminal;
|
|
2305
|
+
* a transient shard failure ({@link TRANSIENT_BATCH_ERROR_CODES}) or a slot the
|
|
2306
|
+
* server never returned is returned for the caller to re-queue.
|
|
2307
|
+
* @returns the writes that must be re-queued (transient slots), in input order
|
|
2308
|
+
*/
|
|
2309
|
+
private settleReplayBatchSlots;
|
|
2205
2310
|
}
|
|
2206
|
-
export {
|
|
2311
|
+
export { SubscriptionCallback as $, ArgsOf as A, BookmarkStorage as B, CachedQuery as C, DEFAULT_MAX_BUFFER as D, ScheduleRecord as E, FunctionReference as F, GlobalFacetResult as G, SchedulerPoolStatus as H, SchedulerStatus as I, ServerMessage as J, ServerPokeEndMessage as K, LunoraClient as L, MutationCallOptions as M, ServerPokePartMessage as N, OfflineQueueOptions as O, Preloaded as P, QueryCacheAdapter as Q, ReturnOf as R, SubscriptionError as S, ServerPokeStartMessage as T, User as U, ShardTrafficEntry as V, ShardTrafficResult as W, StorageListPage as X, StorageObject as Y, StreamHandle as Z, StreamIterable as _, Unsubscribe as a, SubscriptionRegistry as a0, SubscriptionState as a1, SyncWatermark as a2, WorkflowInstanceAction as a3, WorkflowInstanceDetail as a4, WorkflowInstancePage as a5, WorkflowInstanceStatus as a6, WorkflowInstanceSummary as a7, WorkflowStepDetail as a8, createLocalStore as a9, createStream as aa, SubscriptionErrorCallback as b, PersistenceAdapter as c, ReconnectOptions as d, BatchSlot as e, ClientMessage as f, ClientShapeSubscribeMessage as g, ClientShapeUnsubscribeMessage as h, ConnectionStatus as i, FunctionArgumentDescriptor as j, FunctionDescriptor as k, GlobalFacetValue as l, GlobalFilterClause as m, GlobalTableInfo as n, GlobalTablePage as o, LunoraClientError as p, LunoraClientOptions as q, MutationSettledEvent as r, OptimisticLocalStore as s, OptimisticUpdate as t, OutboxMutation as u, OutboxSink as v, PersistedMutation as w, RowOp as x, RpcEnvelope as y, RpcResponseBody as z };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-
|
|
1
|
+
import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-DTwbfUWF.mjs";
|
|
2
2
|
/**
|
|
3
3
|
* Run a query once on the server (during SSR) and capture its result in a
|
|
4
4
|
* serializable {@link Preloaded} token. Embed the token in the rendered HTML and
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-
|
|
1
|
+
import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-DTwbfUWF.js";
|
|
2
2
|
/**
|
|
3
3
|
* Run a query once on the server (during SSR) and capture its result in a
|
|
4
4
|
* serializable {@link Preloaded} token. Embed the token in the rendered HTML and
|
|
@@ -8,12 +8,22 @@ const stableStringify = (value) => {
|
|
|
8
8
|
if (value === void 0) {
|
|
9
9
|
return "null";
|
|
10
10
|
}
|
|
11
|
+
if (typeof value === "bigint") {
|
|
12
|
+
throw new TypeError("stableStringify: cannot use a bigint in a cache key (query/subscription/shape args) — pass it as a string");
|
|
13
|
+
}
|
|
11
14
|
if (value === null || typeof value !== "object") {
|
|
12
15
|
return JSON.stringify(value);
|
|
13
16
|
}
|
|
14
17
|
if (Array.isArray(value)) {
|
|
15
18
|
return `[${value.map((item) => stableStringify(item)).join(",")}]`;
|
|
16
19
|
}
|
|
20
|
+
const proto = Object.getPrototypeOf(value);
|
|
21
|
+
if (proto !== null && proto !== Object.prototype) {
|
|
22
|
+
const name = value.constructor?.name ?? "value";
|
|
23
|
+
throw new TypeError(
|
|
24
|
+
`stableStringify: cannot use a ${name} in a cache key (query/subscription/shape args) — only plain objects, arrays, and JSON primitives are supported`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
17
27
|
const record = value;
|
|
18
28
|
const keys = Object.keys(record).toSorted(compareKeys);
|
|
19
29
|
const parts = [];
|
package/dist/query/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as SubscriptionError, F as FunctionReference, A as ArgsOf, R as ReturnOf, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-
|
|
2
|
-
export type { b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-
|
|
1
|
+
import { S as SubscriptionError, F as FunctionReference, A as ArgsOf, R as ReturnOf, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-DTwbfUWF.mjs";
|
|
2
|
+
export type { b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-DTwbfUWF.mjs";
|
|
3
3
|
import '@lunora/runtime';
|
|
4
4
|
/**
|
|
5
5
|
* The sentinel a framework adapter resolves its reactive args to when it wants
|
package/dist/query/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as SubscriptionError, F as FunctionReference, A as ArgsOf, R as ReturnOf, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-
|
|
2
|
-
export type { b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-
|
|
1
|
+
import { S as SubscriptionError, F as FunctionReference, A as ArgsOf, R as ReturnOf, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-DTwbfUWF.js";
|
|
2
|
+
export type { b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-DTwbfUWF.js";
|
|
3
3
|
import '@lunora/runtime';
|
|
4
4
|
/**
|
|
5
5
|
* The sentinel a framework adapter resolves its reactive args to when it wants
|
package/dist/ssr/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { P as Preloaded, L as LunoraClient } from "../packem_shared/lunora-client.d-
|
|
2
|
-
export type { A as ArgsOf, F as FunctionReference, R as ReturnOf } from "../packem_shared/lunora-client.d-
|
|
3
|
-
export { p as preloadQuery, a as preloadedQueryResult } from "../packem_shared/preload.d-
|
|
1
|
+
import { P as Preloaded, L as LunoraClient } from "../packem_shared/lunora-client.d-DTwbfUWF.mjs";
|
|
2
|
+
export type { A as ArgsOf, F as FunctionReference, R as ReturnOf } from "../packem_shared/lunora-client.d-DTwbfUWF.mjs";
|
|
3
|
+
export { p as preloadQuery, a as preloadedQueryResult } from "../packem_shared/preload.d-BEp0JJeu.mjs";
|
|
4
4
|
import '@lunora/runtime';
|
|
5
5
|
/**
|
|
6
6
|
* Structural shape of a better-auth `getSession` call's resolved value.
|
package/dist/ssr/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { P as Preloaded, L as LunoraClient } from "../packem_shared/lunora-client.d-
|
|
2
|
-
export type { A as ArgsOf, F as FunctionReference, R as ReturnOf } from "../packem_shared/lunora-client.d-
|
|
3
|
-
export { p as preloadQuery, a as preloadedQueryResult } from "../packem_shared/preload.d-
|
|
1
|
+
import { P as Preloaded, L as LunoraClient } from "../packem_shared/lunora-client.d-DTwbfUWF.js";
|
|
2
|
+
export type { A as ArgsOf, F as FunctionReference, R as ReturnOf } from "../packem_shared/lunora-client.d-DTwbfUWF.js";
|
|
3
|
+
export { p as preloadQuery, a as preloadedQueryResult } from "../packem_shared/preload.d-CK2IYf5B.js";
|
|
4
4
|
import '@lunora/runtime';
|
|
5
5
|
/**
|
|
6
6
|
* Structural shape of a better-auth `getSession` call's resolved value.
|
package/dist/ssr/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { getServerSession } from '../packem_shared/getServerSession-8jXewqxd.mjs';
|
|
2
2
|
export { deserializePreloaded, serializePreloaded } from '../packem_shared/deserializePreloaded-C0eJTY_W.mjs';
|
|
3
|
-
export { createServerClient } from '../packem_shared/createServerClient-
|
|
3
|
+
export { createServerClient } from '../packem_shared/createServerClient-C5GMCGSQ.mjs';
|
|
4
4
|
export { preloadQuery, preloadedQueryResult } from '../packem_shared/preloadQuery-lobFkD2Z.mjs';
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { S as SubscriptionRegistry } from './subscription-C1Jy7HiF.mjs';
|