@lunora/client 0.0.0 → 1.0.0-alpha.2

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 (41) hide show
  1. package/LICENSE.md +105 -0
  2. package/README.md +111 -9
  3. package/__assets__/package-og.svg +14 -0
  4. package/dist/auth/index.d.mts +20 -0
  5. package/dist/auth/index.d.ts +20 -0
  6. package/dist/auth/index.mjs +60 -0
  7. package/dist/index.d.mts +281 -0
  8. package/dist/index.d.ts +281 -0
  9. package/dist/index.mjs +14 -0
  10. package/dist/packem_shared/CONFLICT_ERROR_CODE-aUdVbEDw.mjs +4 -0
  11. package/dist/packem_shared/DEFAULT_MAX_BUFFER-BDkqO5PW.mjs +107 -0
  12. package/dist/packem_shared/LunoraClient-UiULzH_1.mjs +2165 -0
  13. package/dist/packem_shared/OfflineQueue-D5p_QgF_.mjs +127 -0
  14. package/dist/packem_shared/SKIP-vItZChkw.mjs +50 -0
  15. package/dist/packem_shared/SubscriptionRegistry-B-Qx_Gux.mjs +26 -0
  16. package/dist/packem_shared/applyDelta-4jFGTPA3.mjs +61 -0
  17. package/dist/packem_shared/createAsyncStoragePersistence-1Z5BZ8RC.mjs +45 -0
  18. package/dist/packem_shared/createInMemoryBookmarkStorage-BoN7a7TH.mjs +11 -0
  19. package/dist/packem_shared/createInMemoryPersistence-CW82inU5.mjs +105 -0
  20. package/dist/packem_shared/createInMemoryQueryCache-B1PQ9Twl.mjs +138 -0
  21. package/dist/packem_shared/createLocalStore-DSUfoLqY.mjs +36 -0
  22. package/dist/packem_shared/createMutationRunner-BqsavzvG.mjs +21 -0
  23. package/dist/packem_shared/createReconnect-Di_-oHH7.mjs +22 -0
  24. package/dist/packem_shared/createServerClient-BjZc3gD8.mjs +11 -0
  25. package/dist/packem_shared/deserializePreloaded-C0eJTY_W.mjs +4 -0
  26. package/dist/packem_shared/getServerSession-8jXewqxd.mjs +13 -0
  27. package/dist/packem_shared/lunora-client.d-DGvyuJ_p.d.mts +1597 -0
  28. package/dist/packem_shared/lunora-client.d-DGvyuJ_p.d.ts +1597 -0
  29. package/dist/packem_shared/preload.d-BoDmFqSG.d.ts +20 -0
  30. package/dist/packem_shared/preload.d-dSaRMuhL.d.mts +20 -0
  31. package/dist/packem_shared/preloadQuery-lobFkD2Z.mjs +13 -0
  32. package/dist/pagination/index.d.mts +82 -0
  33. package/dist/pagination/index.d.ts +82 -0
  34. package/dist/pagination/index.mjs +61 -0
  35. package/dist/query/index.d.mts +62 -0
  36. package/dist/query/index.d.ts +62 -0
  37. package/dist/query/index.mjs +1 -0
  38. package/dist/ssr/index.d.mts +115 -0
  39. package/dist/ssr/index.d.ts +115 -0
  40. package/dist/ssr/index.mjs +4 -0
  41. package/package.json +53 -17
@@ -0,0 +1,127 @@
1
+ let idCounter = 0;
2
+ const nextId = () => {
3
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
4
+ return crypto.randomUUID();
5
+ }
6
+ idCounter += 1;
7
+ return `m_${Date.now().toString(36)}_${idCounter.toString(36)}`;
8
+ };
9
+ const reportPersistenceError = (handler, operation, error, mutationId) => {
10
+ if (handler) {
11
+ handler({ error, mutationId, operation });
12
+ return;
13
+ }
14
+ console.warn(`[lunora] offline-queue persistence ${operation} failed`, error);
15
+ };
16
+ class OfflineQueue {
17
+ /** Opt-in to queueing mutations before the targeted shard's first connect. */
18
+ queueBeforeFirstConnect;
19
+ maxItems;
20
+ onPersistenceError;
21
+ persistence;
22
+ items = [];
23
+ constructor(options = {}, persistence) {
24
+ this.maxItems = options.maxItems ?? 1e3;
25
+ this.queueBeforeFirstConnect = options.queueBeforeFirstConnect ?? false;
26
+ this.onPersistenceError = options.onPersistenceError;
27
+ this.persistence = persistence;
28
+ }
29
+ get size() {
30
+ return this.items.length;
31
+ }
32
+ enqueue(entry) {
33
+ const item = entry;
34
+ item.id ??= nextId();
35
+ this.items.push(item);
36
+ this.persistence?.append({ args: item.args, functionPath: item.functionPath, id: item.id, identity: item.identity, shardKey: item.shardKey }).catch((error) => {
37
+ reportPersistenceError(this.onPersistenceError, "append", error, item.id);
38
+ });
39
+ while (this.items.length > this.maxItems) {
40
+ const dropped = this.items.shift();
41
+ if (dropped) {
42
+ if (dropped.id) {
43
+ this.persistence?.remove(dropped.id).catch((error2) => {
44
+ reportPersistenceError(this.onPersistenceError, "remove", error2, dropped.id);
45
+ });
46
+ }
47
+ const error = new Error("offline queue overflow");
48
+ error.code = "OFFLINE_QUEUE_OVERFLOW";
49
+ dropped.reject(error);
50
+ }
51
+ }
52
+ }
53
+ /**
54
+ * Restore mutations persisted in a prior session and re-queue them in FIFO
55
+ * order. Restored entries already live in durable storage, so they are not
56
+ * re-appended; they carry no-op `resolve`/`reject` (the original awaiter is
57
+ * gone after a reload). No-op when no persistence adapter is configured.
58
+ * Returns the distinct shard keys of the restored writes so the caller can
59
+ * open their sockets to trigger a flush.
60
+ */
61
+ async hydrate() {
62
+ if (!this.persistence) {
63
+ return [];
64
+ }
65
+ const persisted = await this.persistence.load();
66
+ const shardKeys = /* @__PURE__ */ new Set();
67
+ for (const mutation of persisted) {
68
+ if (this.items.some((item) => item.id === mutation.id)) {
69
+ continue;
70
+ }
71
+ this.items.push({
72
+ args: mutation.args,
73
+ functionPath: mutation.functionPath,
74
+ id: mutation.id,
75
+ identity: mutation.identity,
76
+ reject: () => void 0,
77
+ resolve: () => void 0,
78
+ shardKey: mutation.shardKey
79
+ });
80
+ shardKeys.add(mutation.shardKey);
81
+ }
82
+ return [...shardKeys];
83
+ }
84
+ /**
85
+ * Remove and return queued mutations. With no `predicate`, drains the whole
86
+ * queue. With one, drains only matching entries (preserving FIFO order) and
87
+ * leaves the rest queued — used to flush a single shard's writes when its
88
+ * socket reconnects while other shards are still down.
89
+ */
90
+ drain(predicate) {
91
+ if (!predicate) {
92
+ const drained2 = [...this.items];
93
+ this.items.length = 0;
94
+ return drained2;
95
+ }
96
+ const drained = [];
97
+ const kept = [];
98
+ for (const item of this.items) {
99
+ (predicate(item) ? drained : kept).push(item);
100
+ }
101
+ this.items.length = 0;
102
+ this.items.push(...kept);
103
+ return drained;
104
+ }
105
+ /**
106
+ * Return previously-drained mutations to the front of the queue, preserving
107
+ * their FIFO order, without re-persisting them — they were never unpersisted,
108
+ * so durable storage still holds them. Used when a flush aborts on a transient
109
+ * transport failure: the unreplayed writes stay queued for the next reconnect.
110
+ */
111
+ requeue(items) {
112
+ if (items.length === 0) {
113
+ return;
114
+ }
115
+ this.items.unshift(...items);
116
+ }
117
+ clear() {
118
+ for (const item of this.items) {
119
+ const error = new Error("CLIENT_CLOSED");
120
+ error.code = "CLIENT_CLOSED";
121
+ item.reject(error);
122
+ }
123
+ this.items.length = 0;
124
+ }
125
+ }
126
+
127
+ export { OfflineQueue, nextId, reportPersistenceError };
@@ -0,0 +1,50 @@
1
+ const SKIP = "skip";
2
+ const toSubscriptionError = (error) => {
3
+ if (error instanceof Error) {
4
+ return { message: error.message };
5
+ }
6
+ return { message: String(error) };
7
+ };
8
+ const createQuerySubscription = (client, function_, args, sinks, options = {}) => {
9
+ if (args === SKIP) {
10
+ sinks.onReset?.();
11
+ return () => {
12
+ };
13
+ }
14
+ let cancelled = false;
15
+ const handleError = (error) => {
16
+ if (cancelled) {
17
+ return;
18
+ }
19
+ sinks.onError?.(error);
20
+ };
21
+ const onError = sinks.onError ? handleError : void 0;
22
+ let unsubscribe;
23
+ try {
24
+ unsubscribe = client.subscribe(
25
+ function_,
26
+ args,
27
+ (value) => {
28
+ if (cancelled) {
29
+ return;
30
+ }
31
+ sinks.onData(value);
32
+ },
33
+ { onError, shardKey: options.shardKey }
34
+ );
35
+ } catch (error) {
36
+ if (!sinks.onError) {
37
+ throw error;
38
+ }
39
+ handleError(toSubscriptionError(error));
40
+ return () => {
41
+ cancelled = true;
42
+ };
43
+ }
44
+ return () => {
45
+ cancelled = true;
46
+ unsubscribe();
47
+ };
48
+ };
49
+
50
+ export { SKIP, createQuerySubscription, toSubscriptionError };
@@ -0,0 +1,26 @@
1
+ class SubscriptionRegistry {
2
+ static key(functionPath, args, shardKey) {
3
+ return `${functionPath}::${JSON.stringify(args)}::${shardKey ?? ""}`;
4
+ }
5
+ byKey = /* @__PURE__ */ new Map();
6
+ byId = /* @__PURE__ */ new Map();
7
+ get(key) {
8
+ return this.byKey.get(key);
9
+ }
10
+ getById(id) {
11
+ return this.byId.get(id);
12
+ }
13
+ add(state) {
14
+ this.byKey.set(SubscriptionRegistry.key(state.fn.__lunoraRef, state.args, state.shardKey), state);
15
+ this.byId.set(state.id, state);
16
+ }
17
+ remove(state) {
18
+ this.byKey.delete(SubscriptionRegistry.key(state.fn.__lunoraRef, state.args, state.shardKey));
19
+ this.byId.delete(state.id);
20
+ }
21
+ all() {
22
+ return [...this.byKey.values()];
23
+ }
24
+ }
25
+
26
+ export { SubscriptionRegistry };
@@ -0,0 +1,61 @@
1
+ const ID_FIELD = "_id";
2
+ const CREATION_FIELD = "_creationTime";
3
+ const rowId = (row) => {
4
+ if (typeof row !== "object" || row === null) {
5
+ return void 0;
6
+ }
7
+ const id = row[ID_FIELD];
8
+ return typeof id === "string" ? id : void 0;
9
+ };
10
+ const insertionIndex = (list, row) => {
11
+ const creation = row[CREATION_FIELD];
12
+ if (typeof creation !== "number") {
13
+ return list.length;
14
+ }
15
+ for (const [index, existingRow] of list.entries()) {
16
+ const existing = existingRow[CREATION_FIELD];
17
+ if (typeof existing === "number" && existing > creation) {
18
+ return index;
19
+ }
20
+ }
21
+ return list.length;
22
+ };
23
+ const isMutationDelta = (value) => {
24
+ if (typeof value !== "object" || value === null) {
25
+ return false;
26
+ }
27
+ const candidate = value;
28
+ return typeof candidate["key"] === "string" && typeof candidate["table"] === "string" && (candidate["op"] === "insert" || candidate["op"] === "update" || candidate["op"] === "delete");
29
+ };
30
+ const applyDelta = (current, delta) => {
31
+ if (!Array.isArray(current)) {
32
+ return void 0;
33
+ }
34
+ const rows = [];
35
+ for (const element of current) {
36
+ const id = rowId(element);
37
+ if (id === void 0) {
38
+ return void 0;
39
+ }
40
+ rows.push(element);
41
+ }
42
+ const { key, op, row } = delta;
43
+ if (op === "delete") {
44
+ const next2 = rows.filter((existing) => existing[ID_FIELD] !== key);
45
+ return next2.length === rows.length ? [...rows] : next2;
46
+ }
47
+ if (row === void 0) {
48
+ return void 0;
49
+ }
50
+ const existingIndex = rows.findIndex((existing) => existing[ID_FIELD] === key);
51
+ if (existingIndex === -1) {
52
+ const next2 = [...rows];
53
+ next2.splice(insertionIndex(rows, row), 0, row);
54
+ return next2;
55
+ }
56
+ const next = [...rows];
57
+ next[existingIndex] = row;
58
+ return next;
59
+ };
60
+
61
+ export { applyDelta, isMutationDelta };
@@ -0,0 +1,45 @@
1
+ const DEFAULT_KEY = "lunora:offline-mutations";
2
+ const createAsyncStoragePersistence = (options) => {
3
+ const { storage } = options;
4
+ const key = options.key ?? DEFAULT_KEY;
5
+ let chain = Promise.resolve();
6
+ const serialize = (run) => {
7
+ const next = chain.then(run, run);
8
+ chain = next.then(
9
+ () => void 0,
10
+ () => void 0
11
+ );
12
+ return next;
13
+ };
14
+ const readAll = async () => {
15
+ const raw = await storage.getItem(key);
16
+ if (raw === null) {
17
+ return [];
18
+ }
19
+ try {
20
+ const parsed = JSON.parse(raw);
21
+ return Array.isArray(parsed) ? parsed : [];
22
+ } catch {
23
+ return [];
24
+ }
25
+ };
26
+ const writeAll = (mutations) => storage.setItem(key, JSON.stringify(mutations));
27
+ return {
28
+ append: (mutation) => serialize(async () => {
29
+ const mutations = await readAll();
30
+ mutations.push(mutation);
31
+ await writeAll(mutations);
32
+ }),
33
+ clear: () => serialize(() => storage.removeItem(key)),
34
+ load: () => serialize(readAll),
35
+ remove: (id) => serialize(async () => {
36
+ const mutations = await readAll();
37
+ const remaining = mutations.filter((mutation) => mutation.id !== id);
38
+ if (remaining.length !== mutations.length) {
39
+ await writeAll(remaining);
40
+ }
41
+ })
42
+ };
43
+ };
44
+
45
+ export { createAsyncStoragePersistence };
@@ -0,0 +1,11 @@
1
+ const createInMemoryBookmarkStorage = () => {
2
+ let value = null;
3
+ return {
4
+ get: () => value,
5
+ set: (next) => {
6
+ value = next;
7
+ }
8
+ };
9
+ };
10
+
11
+ export { createInMemoryBookmarkStorage as default };
@@ -0,0 +1,105 @@
1
+ const createInMemoryPersistence = () => {
2
+ const entries = /* @__PURE__ */ new Map();
3
+ const clone = (mutation) => {
4
+ return {
5
+ args: { ...mutation.args },
6
+ functionPath: mutation.functionPath,
7
+ id: mutation.id,
8
+ identity: mutation.identity,
9
+ shardKey: mutation.shardKey
10
+ };
11
+ };
12
+ return {
13
+ append: (mutation) => {
14
+ entries.set(mutation.id, clone(mutation));
15
+ return Promise.resolve();
16
+ },
17
+ clear: () => {
18
+ entries.clear();
19
+ return Promise.resolve();
20
+ },
21
+ load: () => Promise.resolve([...entries.values()].map((mutation) => clone(mutation))),
22
+ remove: (id) => {
23
+ entries.delete(id);
24
+ return Promise.resolve();
25
+ }
26
+ };
27
+ };
28
+ const DEFAULT_DATABASE = "lunora";
29
+ const DEFAULT_STORE = "offline-mutations";
30
+ const ID_INDEX = "by_id";
31
+ const promisifyRequest = (request) => new Promise((resolve, reject) => {
32
+ request.addEventListener("success", () => {
33
+ resolve(request.result);
34
+ });
35
+ request.addEventListener("error", () => {
36
+ reject(request.error ?? new Error("IndexedDB request failed"));
37
+ });
38
+ });
39
+ const createIndexedDbPersistence = (options = {}) => {
40
+ const factory = options.indexedDB ?? (typeof indexedDB === "undefined" ? void 0 : indexedDB);
41
+ if (!factory) {
42
+ throw new Error("createIndexedDbPersistence: no IndexedDB available — pass `indexedDB` or use createInMemoryPersistence()");
43
+ }
44
+ const databaseName = options.databaseName ?? DEFAULT_DATABASE;
45
+ const storeName = options.storeName ?? DEFAULT_STORE;
46
+ let databasePromise;
47
+ const openDatabase = () => {
48
+ if (databasePromise) {
49
+ return databasePromise;
50
+ }
51
+ databasePromise = new Promise((resolve, reject) => {
52
+ const request = factory.open(databaseName, 1);
53
+ request.addEventListener("upgradeneeded", () => {
54
+ const database = request.result;
55
+ if (!database.objectStoreNames.contains(storeName)) {
56
+ const store = database.createObjectStore(storeName, { autoIncrement: true });
57
+ store.createIndex(ID_INDEX, "id", { unique: true });
58
+ }
59
+ });
60
+ request.addEventListener("success", () => {
61
+ resolve(request.result);
62
+ });
63
+ request.addEventListener("error", () => {
64
+ reject(request.error ?? new Error("IndexedDB open failed"));
65
+ });
66
+ });
67
+ return databasePromise;
68
+ };
69
+ const withStore = async (mode, run) => {
70
+ const database = await openDatabase();
71
+ const transaction = database.transaction(storeName, mode);
72
+ const result = await run(transaction.objectStore(storeName));
73
+ await new Promise((resolve, reject) => {
74
+ transaction.addEventListener("complete", () => {
75
+ resolve();
76
+ });
77
+ transaction.addEventListener("error", () => {
78
+ reject(transaction.error ?? new Error("IndexedDB transaction failed"));
79
+ });
80
+ transaction.addEventListener("abort", () => {
81
+ reject(transaction.error ?? new Error("IndexedDB transaction aborted"));
82
+ });
83
+ });
84
+ return result;
85
+ };
86
+ return {
87
+ append: async (mutation) => {
88
+ await withStore("readwrite", (store) => promisifyRequest(store.add(mutation)));
89
+ },
90
+ clear: async () => {
91
+ await withStore("readwrite", (store) => promisifyRequest(store.clear()));
92
+ },
93
+ load: async () => withStore("readonly", (store) => promisifyRequest(store.getAll())),
94
+ remove: async (id) => {
95
+ await withStore("readwrite", async (store) => {
96
+ const key = await promisifyRequest(store.index(ID_INDEX).getKey(id));
97
+ if (key !== void 0) {
98
+ await promisifyRequest(store.delete(key));
99
+ }
100
+ });
101
+ }
102
+ };
103
+ };
104
+
105
+ export { createInMemoryPersistence, createIndexedDbPersistence };
@@ -0,0 +1,138 @@
1
+ const queryCacheKey = (functionPath, argsKey, shardKey) => `${functionPath}::${argsKey}::${shardKey ?? ""}`;
2
+ const DEFAULT_MAX_ENTRIES = 500;
3
+ const createInMemoryQueryCache = (options = {}) => {
4
+ const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
5
+ const entries = /* @__PURE__ */ new Map();
6
+ const clone = (entry) => {
7
+ return { ...entry, value: structuredClone(entry.value) };
8
+ };
9
+ const evict = () => {
10
+ if (entries.size <= maxEntries) {
11
+ return;
12
+ }
13
+ const ordered = [...entries.values()].toSorted((a, b) => a.ts - b.ts);
14
+ for (const entry of ordered) {
15
+ if (entries.size <= maxEntries) {
16
+ break;
17
+ }
18
+ entries.delete(entry.key);
19
+ }
20
+ };
21
+ return {
22
+ clear: () => {
23
+ entries.clear();
24
+ return Promise.resolve();
25
+ },
26
+ load: () => Promise.resolve([...entries.values()].map((entry) => clone(entry))),
27
+ put: (key, entry) => {
28
+ entries.set(key, clone({ ...entry, key }));
29
+ evict();
30
+ return Promise.resolve();
31
+ },
32
+ remove: (key) => {
33
+ entries.delete(key);
34
+ return Promise.resolve();
35
+ }
36
+ };
37
+ };
38
+ const DEFAULT_DATABASE = "lunora";
39
+ const DEFAULT_STORE = "query-cache";
40
+ const TS_INDEX = "by_ts";
41
+ const DATABASE_VERSION = 2;
42
+ const promisifyRequest = (request) => new Promise((resolve, reject) => {
43
+ request.addEventListener("success", () => {
44
+ resolve(request.result);
45
+ });
46
+ request.addEventListener("error", () => {
47
+ reject(request.error ?? new Error("IndexedDB request failed"));
48
+ });
49
+ });
50
+ const createIndexedDbQueryCache = (options = {}) => {
51
+ const factory = options.indexedDB ?? (typeof indexedDB === "undefined" ? void 0 : indexedDB);
52
+ if (!factory) {
53
+ throw new Error("createIndexedDbQueryCache: no IndexedDB available — pass `indexedDB` or use createInMemoryQueryCache()");
54
+ }
55
+ const databaseName = options.databaseName ?? DEFAULT_DATABASE;
56
+ const storeName = options.storeName ?? DEFAULT_STORE;
57
+ const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
58
+ let databasePromise;
59
+ const openDatabase = () => {
60
+ if (databasePromise) {
61
+ return databasePromise;
62
+ }
63
+ databasePromise = new Promise((resolve, reject) => {
64
+ const request = factory.open(databaseName, DATABASE_VERSION);
65
+ request.addEventListener("upgradeneeded", () => {
66
+ const database = request.result;
67
+ if (!database.objectStoreNames.contains(storeName)) {
68
+ const store = database.createObjectStore(storeName, { keyPath: "key" });
69
+ store.createIndex(TS_INDEX, "ts", { unique: false });
70
+ }
71
+ });
72
+ request.addEventListener("success", () => {
73
+ resolve(request.result);
74
+ });
75
+ request.addEventListener("error", () => {
76
+ reject(request.error ?? new Error("IndexedDB open failed"));
77
+ });
78
+ });
79
+ return databasePromise;
80
+ };
81
+ const withStore = async (mode, run) => {
82
+ const database = await openDatabase();
83
+ const transaction = database.transaction(storeName, mode);
84
+ const result = await run(transaction.objectStore(storeName));
85
+ await new Promise((resolve, reject) => {
86
+ transaction.addEventListener("complete", () => {
87
+ resolve();
88
+ });
89
+ transaction.addEventListener("error", () => {
90
+ reject(transaction.error ?? new Error("IndexedDB transaction failed"));
91
+ });
92
+ transaction.addEventListener("abort", () => {
93
+ reject(transaction.error ?? new Error("IndexedDB transaction aborted"));
94
+ });
95
+ });
96
+ return result;
97
+ };
98
+ const evict = async (store) => {
99
+ const count = await promisifyRequest(store.count());
100
+ let overflow = count - maxEntries;
101
+ if (overflow <= 0) {
102
+ return;
103
+ }
104
+ await new Promise((resolve, reject) => {
105
+ const cursorRequest = store.index(TS_INDEX).openCursor();
106
+ cursorRequest.addEventListener("success", () => {
107
+ const cursor = cursorRequest.result;
108
+ if (!cursor || overflow <= 0) {
109
+ resolve();
110
+ return;
111
+ }
112
+ cursor.delete();
113
+ overflow -= 1;
114
+ cursor.continue();
115
+ });
116
+ cursorRequest.addEventListener("error", () => {
117
+ reject(cursorRequest.error ?? new Error("IndexedDB eviction failed"));
118
+ });
119
+ });
120
+ };
121
+ return {
122
+ clear: async () => {
123
+ await withStore("readwrite", (store) => promisifyRequest(store.clear()));
124
+ },
125
+ load: async () => withStore("readonly", (store) => promisifyRequest(store.getAll())),
126
+ put: async (key, entry) => {
127
+ await withStore("readwrite", async (store) => {
128
+ await promisifyRequest(store.put({ ...entry, key }));
129
+ await evict(store);
130
+ });
131
+ },
132
+ remove: async (key) => {
133
+ await withStore("readwrite", (store) => promisifyRequest(store.delete(key)));
134
+ }
135
+ };
136
+ };
137
+
138
+ export { createInMemoryQueryCache, createIndexedDbQueryCache, queryCacheKey };
@@ -0,0 +1,36 @@
1
+ const createLocalStore = (subscriptions, shardKey, write, stableStringify) => {
2
+ const rollbacks = [];
3
+ const findState = (functionRef, argsKey) => {
4
+ for (const state of subscriptions.all()) {
5
+ if (state.fn.__lunoraRef === functionRef && state.shardKey === shardKey && state.argsKey === argsKey) {
6
+ return state;
7
+ }
8
+ }
9
+ return void 0;
10
+ };
11
+ const store = {
12
+ getAllQueries: (function_) => {
13
+ const matches = [];
14
+ for (const state of subscriptions.all()) {
15
+ if (state.fn.__lunoraRef === function_.__lunoraRef && state.shardKey === shardKey) {
16
+ matches.push({ args: state.args, value: state.lastValue });
17
+ }
18
+ }
19
+ return matches;
20
+ },
21
+ getQuery: (function_, args) => {
22
+ const state = findState(function_.__lunoraRef, stableStringify(args ?? {}));
23
+ return state?.lastValue;
24
+ },
25
+ setQuery: (function_, args, value) => {
26
+ const state = findState(function_.__lunoraRef, stableStringify(args ?? {}));
27
+ if (!state) {
28
+ return;
29
+ }
30
+ rollbacks.push(write(state, value));
31
+ }
32
+ };
33
+ return { rollbacks, store };
34
+ };
35
+
36
+ export { createLocalStore };
@@ -0,0 +1,21 @@
1
+ const createMutationRunner = (client, function_, sinks) => {
2
+ let inFlight = 0;
3
+ return async (args, options) => {
4
+ inFlight += 1;
5
+ sinks.setPending(true);
6
+ try {
7
+ const result = await client.mutation(function_, args, options);
8
+ sinks.setResult(result);
9
+ return result;
10
+ } catch (error) {
11
+ const normalized = error instanceof Error ? error : new Error(String(error));
12
+ sinks.setError(normalized);
13
+ throw normalized;
14
+ } finally {
15
+ inFlight -= 1;
16
+ sinks.setPending(inFlight > 0);
17
+ }
18
+ };
19
+ };
20
+
21
+ export { createMutationRunner };
@@ -0,0 +1,22 @@
1
+ const createReconnect = (options = {}, random = Math.random) => {
2
+ const initialDelayMs = options.initialDelayMs ?? 250;
3
+ const maxDelayMs = options.maxDelayMs ?? 3e4;
4
+ const jitter = options.jitter ?? true;
5
+ let attempt = 0;
6
+ return {
7
+ next() {
8
+ const exponential = Math.min(maxDelayMs, initialDelayMs * 2 ** attempt);
9
+ attempt += 1;
10
+ if (!jitter) {
11
+ return exponential;
12
+ }
13
+ const min = exponential / 2;
14
+ return Math.floor(min + random() * (exponential - min));
15
+ },
16
+ reset() {
17
+ attempt = 0;
18
+ }
19
+ };
20
+ };
21
+
22
+ export { createReconnect };
@@ -0,0 +1,11 @@
1
+ import { LunoraClient } from './LunoraClient-UiULzH_1.mjs';
2
+
3
+ const createServerClient = (options) => {
4
+ const client = new LunoraClient({ fetch: options.fetch, url: options.url });
5
+ if (options.token !== void 0) {
6
+ client.setAuthToken(options.token);
7
+ }
8
+ return client;
9
+ };
10
+
11
+ export { createServerClient };
@@ -0,0 +1,4 @@
1
+ const serializePreloaded = (preloaded) => JSON.stringify(preloaded).replaceAll("<", String.raw`\u003c`);
2
+ const deserializePreloaded = (serialized) => JSON.parse(serialized);
3
+
4
+ export { deserializePreloaded, serializePreloaded };
@@ -0,0 +1,13 @@
1
+ const extractHeaders = (source) => {
2
+ if (source instanceof Headers) {
3
+ return source;
4
+ }
5
+ return source.headers;
6
+ };
7
+ const getServerSession = async (request, auth) => {
8
+ const headers = extractHeaders(request);
9
+ const result = await auth.api.getSession({ headers });
10
+ return result ?? null;
11
+ };
12
+
13
+ export { getServerSession };