@lunora/client 1.0.0-alpha.11 → 1.0.0-alpha.110
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/LICENSE.md +6 -0
- package/README.md +16 -3
- package/dist/auth/index.d.mts +36 -12
- package/dist/auth/index.d.ts +36 -12
- package/dist/auth/index.mjs +1 -60
- package/dist/index.d.mts +884 -273
- package/dist/index.d.ts +884 -273
- package/dist/index.mjs +1 -15
- package/dist/packem_shared/CONFLICT_ERROR_CODE-BOeeP_Wl.mjs +1 -0
- package/dist/packem_shared/ClientServiceWorker-DXnBXeWK.mjs +1 -0
- package/dist/packem_shared/DEFAULT_MAX_BUFFER-D3QH2iaq.mjs +1 -0
- package/dist/packem_shared/LunoraClient-D4FH_fjY.mjs +1 -0
- package/dist/packem_shared/OfflineQueue-BuHVa8B0.mjs +1 -0
- package/dist/packem_shared/RETIRE_AFTER_DURABLE_SEQ_ADVANCE-DoEHtuR8.mjs +1 -0
- package/dist/packem_shared/SKIP-d7LeP-sY.mjs +1 -0
- package/dist/packem_shared/SubscriptionRegistry-CS-AFUI9.mjs +1 -0
- package/dist/packem_shared/TabCoordinator-CXD7vulJ.mjs +1 -0
- package/dist/packem_shared/anyApi-CBOws2ZA.mjs +1 -0
- package/dist/packem_shared/applyDelta-DwcQmRsV.mjs +1 -0
- package/dist/packem_shared/createAsyncStoragePersistence-CeUW9NTj.mjs +1 -0
- package/dist/packem_shared/createAsyncStorageQueryCache-BfZkthAE.mjs +1 -0
- package/dist/packem_shared/createCallRunner-Bwcrfh76.mjs +1 -0
- package/dist/packem_shared/createClientQuery-B8Nfj-7o.mjs +1 -0
- package/dist/packem_shared/createInMemoryBookmarkStorage-BooZhW0n.mjs +1 -0
- package/dist/packem_shared/createInMemoryPersistence-Cz2W6XVj.mjs +1 -0
- package/dist/packem_shared/createInMemoryQueryCache-D2uNHLfM.mjs +1 -0
- package/dist/packem_shared/createLocalStore-BFdXRTFz.mjs +1 -0
- package/dist/packem_shared/createMutatorRunner-CuMRJpUx.mjs +1 -0
- package/dist/packem_shared/createReconnect-CjTmjJDH.mjs +1 -0
- package/dist/packem_shared/createReply-CwFABEJs.mjs +1 -0
- package/dist/packem_shared/createServerClient-Chmnq4DL.mjs +1 -0
- package/dist/packem_shared/createSnapshotPrecondition-GRVKxPsh.mjs +1 -0
- package/dist/packem_shared/delta-merge-vxvH3PvF.mjs +1 -0
- package/dist/packem_shared/deserializePreloaded-C6NTQdFa.mjs +1 -0
- package/dist/packem_shared/function-reference.d-Br_hsKje.d.mts +45 -0
- package/dist/packem_shared/function-reference.d-Br_hsKje.d.ts +45 -0
- package/dist/packem_shared/getServerSession-BaOoo1p6.mjs +1 -0
- package/dist/packem_shared/httpStream-CPdblK2G.mjs +11 -0
- package/dist/packem_shared/idb-utility-C8WS390w.mjs +1 -0
- package/dist/packem_shared/local-store-BjenEm3t.mjs +1 -0
- package/dist/packem_shared/lunora-client.d-CGR-MObo.d.ts +4007 -0
- package/dist/packem_shared/lunora-client.d-qVBwKXE1.d.mts +4007 -0
- package/dist/packem_shared/offline-queue-DSCpAxOp.mjs +1 -0
- package/dist/packem_shared/preload.d-0j8xgrYf.d.mts +21 -0
- package/dist/packem_shared/preload.d-DTpiMCut.d.ts +21 -0
- package/dist/packem_shared/preloadQuery-uFy24PCR.mjs +1 -0
- package/dist/packem_shared/single-blob-store-DrhzObic.mjs +1 -0
- package/dist/packem_shared/wire-codec-Sw1rAu-Z.mjs +1 -0
- package/dist/packem_shared/wire-key-HsUDTlQ7.mjs +1 -0
- package/dist/pagination/index.d.mts +42 -42
- package/dist/pagination/index.d.ts +42 -42
- package/dist/pagination/index.mjs +1 -61
- package/dist/query/index.d.mts +111 -43
- package/dist/query/index.d.ts +111 -43
- package/dist/query/index.mjs +1 -1
- package/dist/service.d.mts +49 -0
- package/dist/service.d.ts +49 -0
- package/dist/service.mjs +1 -0
- package/dist/ssr/index.d.mts +109 -79
- package/dist/ssr/index.d.ts +109 -79
- package/dist/ssr/index.mjs +1 -4
- package/dist/upload.d.mts +35 -0
- package/dist/upload.d.ts +35 -0
- package/dist/upload.mjs +1 -0
- package/package.json +16 -1
- package/dist/packem_shared/CONFLICT_ERROR_CODE-aUdVbEDw.mjs +0 -4
- package/dist/packem_shared/DEFAULT_MAX_BUFFER-BDkqO5PW.mjs +0 -107
- package/dist/packem_shared/LunoraClient-6J-4BGWK.mjs +0 -2722
- package/dist/packem_shared/OfflineQueue-BI0FNNvc.mjs +0 -1
- package/dist/packem_shared/SKIP-vItZChkw.mjs +0 -50
- package/dist/packem_shared/SubscriptionRegistry-Dn-7k7eo.mjs +0 -1
- package/dist/packem_shared/applyDelta-4jFGTPA3.mjs +0 -61
- package/dist/packem_shared/createAsyncStoragePersistence-1Z5BZ8RC.mjs +0 -45
- package/dist/packem_shared/createInMemoryBookmarkStorage-BoN7a7TH.mjs +0 -11
- package/dist/packem_shared/createInMemoryPersistence-DlFjWtOm.mjs +0 -117
- package/dist/packem_shared/createInMemoryQueryCache-4AkTV38-.mjs +0 -150
- package/dist/packem_shared/createLocalStore-IOur0jHF.mjs +0 -1
- package/dist/packem_shared/createMutationRunner-BqsavzvG.mjs +0 -21
- package/dist/packem_shared/createMutatorRunner-BETvCd0p.mjs +0 -31
- package/dist/packem_shared/createReconnect-Di_-oHH7.mjs +0 -22
- package/dist/packem_shared/createServerClient-CtklXrs2.mjs +0 -11
- package/dist/packem_shared/deserializePreloaded-C0eJTY_W.mjs +0 -4
- package/dist/packem_shared/getServerSession-8jXewqxd.mjs +0 -13
- package/dist/packem_shared/local-store-BNgN3Dw3.mjs +0 -111
- package/dist/packem_shared/lunora-client.d-Dm1BYq19.d.mts +0 -2206
- package/dist/packem_shared/lunora-client.d-Dm1BYq19.d.ts +0 -2206
- package/dist/packem_shared/offline-queue-7Wc4onA0.mjs +0 -164
- package/dist/packem_shared/preload.d-BfWzGJWV.d.mts +0 -20
- package/dist/packem_shared/preload.d-Sp_Ef-_u.d.ts +0 -20
- package/dist/packem_shared/preloadQuery-lobFkD2Z.mjs +0 -13
- package/dist/packem_shared/subscription-C1Jy7HiF.mjs +0 -55
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
const isStaleVersion = (current, stamped) => current !== void 0 && stamped !== current;
|
|
2
|
-
|
|
3
|
-
let idCounter = 0;
|
|
4
|
-
const nextId = () => {
|
|
5
|
-
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
6
|
-
return crypto.randomUUID();
|
|
7
|
-
}
|
|
8
|
-
idCounter += 1;
|
|
9
|
-
const entropy = typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function" ? [...crypto.getRandomValues(new Uint8Array(8))].map((byte) => byte.toString(16).padStart(2, "0")).join("") : (
|
|
10
|
-
// eslint-disable-next-line sonarjs/pseudo-random -- non-cryptographic uniqueness entropy, not a security token; only reached when neither crypto.randomUUID nor crypto.getRandomValues exists
|
|
11
|
-
Math.random().toString(16).slice(2, 12)
|
|
12
|
-
);
|
|
13
|
-
return `m_${Date.now().toString(36)}_${idCounter.toString(36)}_${entropy}`;
|
|
14
|
-
};
|
|
15
|
-
const reportPersistenceError = (handler, operation, error, mutationId) => {
|
|
16
|
-
if (handler) {
|
|
17
|
-
handler({ error, mutationId, operation });
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
console.warn(`[lunora] offline-queue persistence ${operation} failed`, error);
|
|
21
|
-
};
|
|
22
|
-
class OfflineQueue {
|
|
23
|
-
/** Opt-in to queueing mutations before the targeted shard's first connect. */
|
|
24
|
-
queueBeforeFirstConnect;
|
|
25
|
-
maxItems;
|
|
26
|
-
onPersistenceError;
|
|
27
|
-
persistence;
|
|
28
|
-
onEvict;
|
|
29
|
-
onSizeChange;
|
|
30
|
-
/** App/schema version stamped on persisted writes; mismatched records are purged on hydrate. */
|
|
31
|
-
version;
|
|
32
|
-
items = [];
|
|
33
|
-
constructor(options = {}, deps = {}) {
|
|
34
|
-
this.maxItems = options.maxItems ?? 1e3;
|
|
35
|
-
this.queueBeforeFirstConnect = options.queueBeforeFirstConnect ?? false;
|
|
36
|
-
this.onPersistenceError = options.onPersistenceError;
|
|
37
|
-
this.persistence = deps.persistence;
|
|
38
|
-
this.onEvict = deps.onEvict;
|
|
39
|
-
this.onSizeChange = deps.onSizeChange;
|
|
40
|
-
this.version = deps.version;
|
|
41
|
-
}
|
|
42
|
-
get size() {
|
|
43
|
-
return this.items.length;
|
|
44
|
-
}
|
|
45
|
-
enqueue(entry) {
|
|
46
|
-
const item = entry;
|
|
47
|
-
item.id ??= nextId();
|
|
48
|
-
this.items.push(item);
|
|
49
|
-
this.persistence?.append({
|
|
50
|
-
args: item.args,
|
|
51
|
-
functionPath: item.functionPath,
|
|
52
|
-
id: item.id,
|
|
53
|
-
identity: item.identity,
|
|
54
|
-
shardKey: item.shardKey,
|
|
55
|
-
...this.version === void 0 ? {} : { version: this.version }
|
|
56
|
-
}).catch((error) => {
|
|
57
|
-
reportPersistenceError(this.onPersistenceError, "append", error, item.id);
|
|
58
|
-
});
|
|
59
|
-
while (this.items.length > this.maxItems) {
|
|
60
|
-
const dropped = this.items.shift();
|
|
61
|
-
if (dropped) {
|
|
62
|
-
if (dropped.id) {
|
|
63
|
-
this.persistence?.remove(dropped.id).catch((error2) => {
|
|
64
|
-
reportPersistenceError(this.onPersistenceError, "remove", error2, dropped.id);
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
const error = new Error("offline queue overflow");
|
|
68
|
-
error.code = "OFFLINE_QUEUE_OVERFLOW";
|
|
69
|
-
dropped.reject(error);
|
|
70
|
-
this.onEvict?.(dropped, error);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
this.notifySize();
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Restore mutations persisted in a prior session and re-queue them in FIFO
|
|
77
|
-
* order. Restored entries already live in durable storage, so they are not
|
|
78
|
-
* re-appended; they carry no-op `resolve`/`reject` (the original awaiter is
|
|
79
|
-
* gone after a reload). No-op when no persistence adapter is configured.
|
|
80
|
-
* Returns the distinct shard keys of the restored writes so the caller can
|
|
81
|
-
* open their sockets to trigger a flush.
|
|
82
|
-
*/
|
|
83
|
-
async hydrate() {
|
|
84
|
-
if (!this.persistence) {
|
|
85
|
-
return [];
|
|
86
|
-
}
|
|
87
|
-
const persisted = await this.persistence.load();
|
|
88
|
-
const shardKeys = /* @__PURE__ */ new Set();
|
|
89
|
-
for (const mutation of persisted) {
|
|
90
|
-
if (this.items.some((item) => item.id === mutation.id)) {
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
if (isStaleVersion(this.version, mutation.version)) {
|
|
94
|
-
this.persistence.remove(mutation.id).catch((error) => {
|
|
95
|
-
reportPersistenceError(this.onPersistenceError, "remove", error, mutation.id);
|
|
96
|
-
});
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
this.items.push({
|
|
100
|
-
args: mutation.args,
|
|
101
|
-
functionPath: mutation.functionPath,
|
|
102
|
-
id: mutation.id,
|
|
103
|
-
identity: mutation.identity,
|
|
104
|
-
reject: () => void 0,
|
|
105
|
-
resolve: () => void 0,
|
|
106
|
-
shardKey: mutation.shardKey
|
|
107
|
-
});
|
|
108
|
-
shardKeys.add(mutation.shardKey);
|
|
109
|
-
}
|
|
110
|
-
this.notifySize();
|
|
111
|
-
return [...shardKeys];
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Remove and return queued mutations. With no `predicate`, drains the whole
|
|
115
|
-
* queue. With one, drains only matching entries (preserving FIFO order) and
|
|
116
|
-
* leaves the rest queued — used to flush a single shard's writes when its
|
|
117
|
-
* socket reconnects while other shards are still down.
|
|
118
|
-
*/
|
|
119
|
-
drain(predicate) {
|
|
120
|
-
if (!predicate) {
|
|
121
|
-
const drained2 = [...this.items];
|
|
122
|
-
this.items.length = 0;
|
|
123
|
-
this.notifySize();
|
|
124
|
-
return drained2;
|
|
125
|
-
}
|
|
126
|
-
const drained = [];
|
|
127
|
-
const kept = [];
|
|
128
|
-
for (const item of this.items) {
|
|
129
|
-
(predicate(item) ? drained : kept).push(item);
|
|
130
|
-
}
|
|
131
|
-
this.items.length = 0;
|
|
132
|
-
this.items.push(...kept);
|
|
133
|
-
this.notifySize();
|
|
134
|
-
return drained;
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Return previously-drained mutations to the front of the queue, preserving
|
|
138
|
-
* their FIFO order, without re-persisting them — they were never unpersisted,
|
|
139
|
-
* so durable storage still holds them. Used when a flush aborts on a transient
|
|
140
|
-
* transport failure: the unreplayed writes stay queued for the next reconnect.
|
|
141
|
-
*/
|
|
142
|
-
requeue(items) {
|
|
143
|
-
if (items.length === 0) {
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
this.items.unshift(...items);
|
|
147
|
-
this.notifySize();
|
|
148
|
-
}
|
|
149
|
-
clear() {
|
|
150
|
-
for (const item of this.items) {
|
|
151
|
-
const error = new Error("CLIENT_CLOSED");
|
|
152
|
-
error.code = "CLIENT_CLOSED";
|
|
153
|
-
item.reject(error);
|
|
154
|
-
}
|
|
155
|
-
this.items.length = 0;
|
|
156
|
-
this.notifySize();
|
|
157
|
-
}
|
|
158
|
-
/** Notify the size observer (the client's pending-sync count) after any change. */
|
|
159
|
-
notifySize() {
|
|
160
|
-
this.onSizeChange?.(this.items.length);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export { OfflineQueue as O, isStaleVersion as i, nextId as n, reportPersistenceError as r };
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-Dm1BYq19.mjs";
|
|
2
|
-
/**
|
|
3
|
-
* Run a query once on the server (during SSR) and capture its result in a
|
|
4
|
-
* serializable {@link Preloaded} token. Embed the token in the rendered HTML and
|
|
5
|
-
* pass it to `usePreloadedQuery` on the client: the first client render shows
|
|
6
|
-
* the server value with no loading flash, then a live subscription takes over.
|
|
7
|
-
*
|
|
8
|
-
* The query is executed through the supplied {@link LunoraClient} over the same
|
|
9
|
-
* HTTP RPC path the browser uses, so the SSR client only needs a `fetch`
|
|
10
|
-
* implementation that can reach the worker — no in-process Durable Object access.
|
|
11
|
-
*/
|
|
12
|
-
declare const preloadQuery: <F extends FunctionReference>(client: LunoraClient, function_: F, args: ArgsOf<F>, options?: {
|
|
13
|
-
shardKey?: string;
|
|
14
|
-
}) => Promise<Preloaded<ReturnOf<F>>>;
|
|
15
|
-
/**
|
|
16
|
-
* Read the captured value out of a {@link Preloaded} token without subscribing.
|
|
17
|
-
* Useful on the server (or in tests) when you only need the data, not a live feed.
|
|
18
|
-
*/
|
|
19
|
-
declare const preloadedQueryResult: <T>(preloaded: Preloaded<T>) => T;
|
|
20
|
-
export { preloadedQueryResult as a, preloadQuery as p };
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { F as FunctionReference, L as LunoraClient, A as ArgsOf, R as ReturnOf, P as Preloaded } from "./lunora-client.d-Dm1BYq19.js";
|
|
2
|
-
/**
|
|
3
|
-
* Run a query once on the server (during SSR) and capture its result in a
|
|
4
|
-
* serializable {@link Preloaded} token. Embed the token in the rendered HTML and
|
|
5
|
-
* pass it to `usePreloadedQuery` on the client: the first client render shows
|
|
6
|
-
* the server value with no loading flash, then a live subscription takes over.
|
|
7
|
-
*
|
|
8
|
-
* The query is executed through the supplied {@link LunoraClient} over the same
|
|
9
|
-
* HTTP RPC path the browser uses, so the SSR client only needs a `fetch`
|
|
10
|
-
* implementation that can reach the worker — no in-process Durable Object access.
|
|
11
|
-
*/
|
|
12
|
-
declare const preloadQuery: <F extends FunctionReference>(client: LunoraClient, function_: F, args: ArgsOf<F>, options?: {
|
|
13
|
-
shardKey?: string;
|
|
14
|
-
}) => Promise<Preloaded<ReturnOf<F>>>;
|
|
15
|
-
/**
|
|
16
|
-
* Read the captured value out of a {@link Preloaded} token without subscribing.
|
|
17
|
-
* Useful on the server (or in tests) when you only need the data, not a live feed.
|
|
18
|
-
*/
|
|
19
|
-
declare const preloadedQueryResult: <T>(preloaded: Preloaded<T>) => T;
|
|
20
|
-
export { preloadedQueryResult as a, preloadQuery as p };
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
const preloadQuery = async (client, function_, args, options = {}) => {
|
|
2
|
-
const value = await client.query(function_, args, options);
|
|
3
|
-
return {
|
|
4
|
-
__lunoraPreloaded: true,
|
|
5
|
-
args: args ?? {},
|
|
6
|
-
functionPath: function_.__lunoraRef,
|
|
7
|
-
shardKey: options.shardKey,
|
|
8
|
-
value
|
|
9
|
-
};
|
|
10
|
-
};
|
|
11
|
-
const preloadedQueryResult = (preloaded) => preloaded.value;
|
|
12
|
-
|
|
13
|
-
export { preloadQuery, preloadedQueryResult };
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
const compareKeys = (a, b) => {
|
|
2
|
-
if (a < b) {
|
|
3
|
-
return -1;
|
|
4
|
-
}
|
|
5
|
-
return a > b ? 1 : 0;
|
|
6
|
-
};
|
|
7
|
-
const stableStringify = (value) => {
|
|
8
|
-
if (value === void 0) {
|
|
9
|
-
return "null";
|
|
10
|
-
}
|
|
11
|
-
if (value === null || typeof value !== "object") {
|
|
12
|
-
return JSON.stringify(value);
|
|
13
|
-
}
|
|
14
|
-
if (Array.isArray(value)) {
|
|
15
|
-
return `[${value.map((item) => stableStringify(item)).join(",")}]`;
|
|
16
|
-
}
|
|
17
|
-
const record = value;
|
|
18
|
-
const keys = Object.keys(record).toSorted(compareKeys);
|
|
19
|
-
const parts = [];
|
|
20
|
-
for (const key of keys) {
|
|
21
|
-
const raw = record[key];
|
|
22
|
-
if (raw === void 0) {
|
|
23
|
-
continue;
|
|
24
|
-
}
|
|
25
|
-
parts.push(`${JSON.stringify(key)}:${stableStringify(raw)}`);
|
|
26
|
-
}
|
|
27
|
-
return `{${parts.join(",")}}`;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
class SubscriptionRegistry {
|
|
31
|
-
static key(functionPath, args, shardKey) {
|
|
32
|
-
return `${functionPath}::${stableStringify(args)}::${shardKey ?? ""}`;
|
|
33
|
-
}
|
|
34
|
-
byKey = /* @__PURE__ */ new Map();
|
|
35
|
-
byId = /* @__PURE__ */ new Map();
|
|
36
|
-
get(key) {
|
|
37
|
-
return this.byKey.get(key);
|
|
38
|
-
}
|
|
39
|
-
getById(id) {
|
|
40
|
-
return this.byId.get(id);
|
|
41
|
-
}
|
|
42
|
-
add(state) {
|
|
43
|
-
this.byKey.set(SubscriptionRegistry.key(state.fn.__lunoraRef, state.args, state.shardKey), state);
|
|
44
|
-
this.byId.set(state.id, state);
|
|
45
|
-
}
|
|
46
|
-
remove(state) {
|
|
47
|
-
this.byKey.delete(SubscriptionRegistry.key(state.fn.__lunoraRef, state.args, state.shardKey));
|
|
48
|
-
this.byId.delete(state.id);
|
|
49
|
-
}
|
|
50
|
-
all() {
|
|
51
|
-
return [...this.byKey.values()];
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export { SubscriptionRegistry as S, stableStringify as s };
|