@lunora/db 1.0.0-alpha.8 → 1.0.0-alpha.9
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/collections/index.d.mts +2 -2
- package/dist/collections/index.d.ts +2 -2
- package/dist/collections/index.mjs +2 -2
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +4 -4
- package/dist/mutators/index.d.mts +2 -2
- package/dist/mutators/index.d.ts +2 -2
- package/dist/mutators/index.mjs +1 -1
- package/dist/packem_shared/{OUTBOX_MUTATION_FN_NAME-DZrP1gna.mjs → OUTBOX_MUTATION_FN_NAME-Cf8iP6Wa.mjs} +3 -1
- package/dist/packem_shared/{bindMutators-Bh6giR9x.mjs → bindMutators-DNhICkoy.mjs} +1 -1
- package/dist/packem_shared/{collection-options.d-CbS3J_Fm.d.mts → collection-options.d-lJBOVJgq.d.mts} +9 -0
- package/dist/packem_shared/{collection-options.d-CbS3J_Fm.d.ts → collection-options.d-lJBOVJgq.d.ts} +9 -0
- package/dist/packem_shared/{createCheckpointRegistry-C1kzNWCs.mjs → createCheckpointRegistry-SvwBczzv.mjs} +4 -1
- package/dist/packem_shared/{define-collections.d-Po97lX6i.d.ts → define-collections.d-CdORnl3S.d.mts} +69 -4
- package/dist/packem_shared/{define-collections.d-DTS0ANuU.d.mts → define-collections.d-X2MfU5Es.d.ts} +69 -4
- package/dist/packem_shared/{define-mutators.d-C3DO284A.d.ts → define-mutators.d-DhO8mWz0.d.ts} +1 -1
- package/dist/packem_shared/{define-mutators.d-DNVJIpW8.d.mts → define-mutators.d-HWj94_nL.d.mts} +1 -1
- package/dist/packem_shared/{defineCollections-DR6fcIkQ.mjs → defineCollections-BJNtgnWB.mjs} +36 -5
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { type C as CheckpointRegistry, type L as LunoraCollectionConfig, type a as LunoraCollectionOptions, c as createCheckpointRegistry, l as lunoraCollectionOptions } from "../packem_shared/collection-options.d-
|
|
2
|
-
export { type C as CollectionDef, type I as InsertBinding, type L as LunoraDb, d as defineCollections } from "../packem_shared/define-collections.d-
|
|
1
|
+
export { type C as CheckpointRegistry, type L as LunoraCollectionConfig, type a as LunoraCollectionOptions, c as createCheckpointRegistry, l as lunoraCollectionOptions } from "../packem_shared/collection-options.d-lJBOVJgq.mjs";
|
|
2
|
+
export { type C as CollectionDef, type I as InsertBinding, type L as LunoraDb, d as defineCollections } from "../packem_shared/define-collections.d-CdORnl3S.mjs";
|
|
3
3
|
import '@lunora/client';
|
|
4
4
|
import '@tanstack/db';
|
|
5
5
|
import '@tanstack/offline-transactions';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { type C as CheckpointRegistry, type L as LunoraCollectionConfig, type a as LunoraCollectionOptions, c as createCheckpointRegistry, l as lunoraCollectionOptions } from "../packem_shared/collection-options.d-
|
|
2
|
-
export { type C as CollectionDef, type I as InsertBinding, type L as LunoraDb, d as defineCollections } from "../packem_shared/define-collections.d-
|
|
1
|
+
export { type C as CheckpointRegistry, type L as LunoraCollectionConfig, type a as LunoraCollectionOptions, c as createCheckpointRegistry, l as lunoraCollectionOptions } from "../packem_shared/collection-options.d-lJBOVJgq.js";
|
|
2
|
+
export { type C as CollectionDef, type I as InsertBinding, type L as LunoraDb, d as defineCollections } from "../packem_shared/define-collections.d-X2MfU5Es.js";
|
|
3
3
|
import '@lunora/client';
|
|
4
4
|
import '@tanstack/db';
|
|
5
5
|
import '@tanstack/offline-transactions';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { createCheckpointRegistry, lunoraCollectionOptions } from '../packem_shared/createCheckpointRegistry-
|
|
2
|
-
export { defineCollections } from '../packem_shared/defineCollections-
|
|
1
|
+
export { createCheckpointRegistry, lunoraCollectionOptions } from '../packem_shared/createCheckpointRegistry-SvwBczzv.mjs';
|
|
2
|
+
export { defineCollections } from '../packem_shared/defineCollections-BJNtgnWB.mjs';
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { type C as CheckpointRegistry, type E as ExecutorOutboxSinkOptions, type L as LunoraCollectionConfig, type a as LunoraCollectionOptions, O as OUTBOX_MUTATION_FN_NAME, type b as OutboxExecutor, type d as OutboxMutationMetadata, type R as Row, type S as SyncWriter, c as createCheckpointRegistry, e as createExecutorOutboxSink, f as createOptimisticOnlineDetector, l as lunoraCollectionOptions, m as makeDiffEmit, r as runOutboxMutation, t as toMap } from "./packem_shared/collection-options.d-
|
|
2
|
-
export { type C as CollectionDef, type I as InsertBinding, type L as LunoraDb, d as defineCollections } from "./packem_shared/define-collections.d-
|
|
3
|
-
export { type B as BindMutatorsContext, type a as BoundMutators, type C as ClientMutatorContext, type b as ClientMutatorDef, c as bindMutators, d as defineMutator } from "./packem_shared/define-mutators.d-
|
|
1
|
+
export { type C as CheckpointRegistry, type E as ExecutorOutboxSinkOptions, type L as LunoraCollectionConfig, type a as LunoraCollectionOptions, O as OUTBOX_MUTATION_FN_NAME, type b as OutboxExecutor, type d as OutboxMutationMetadata, type R as Row, type S as SyncWriter, c as createCheckpointRegistry, e as createExecutorOutboxSink, f as createOptimisticOnlineDetector, l as lunoraCollectionOptions, m as makeDiffEmit, r as runOutboxMutation, t as toMap } from "./packem_shared/collection-options.d-lJBOVJgq.mjs";
|
|
2
|
+
export { type C as CollectionDef, type D as DefineCollectionsOptions, type I as InsertBinding, type L as LunoraDb, type W as WriteRejectedEvent, d as defineCollections } from "./packem_shared/define-collections.d-CdORnl3S.mjs";
|
|
3
|
+
export { type B as BindMutatorsContext, type a as BoundMutators, type C as ClientMutatorContext, type b as ClientMutatorDef, c as bindMutators, d as defineMutator } from "./packem_shared/define-mutators.d-HWj94_nL.mjs";
|
|
4
4
|
import '@lunora/client';
|
|
5
5
|
import '@tanstack/db';
|
|
6
6
|
import '@tanstack/offline-transactions';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { type C as CheckpointRegistry, type E as ExecutorOutboxSinkOptions, type L as LunoraCollectionConfig, type a as LunoraCollectionOptions, O as OUTBOX_MUTATION_FN_NAME, type b as OutboxExecutor, type d as OutboxMutationMetadata, type R as Row, type S as SyncWriter, c as createCheckpointRegistry, e as createExecutorOutboxSink, f as createOptimisticOnlineDetector, l as lunoraCollectionOptions, m as makeDiffEmit, r as runOutboxMutation, t as toMap } from "./packem_shared/collection-options.d-
|
|
2
|
-
export { type C as CollectionDef, type I as InsertBinding, type L as LunoraDb, d as defineCollections } from "./packem_shared/define-collections.d-
|
|
3
|
-
export { type B as BindMutatorsContext, type a as BoundMutators, type C as ClientMutatorContext, type b as ClientMutatorDef, c as bindMutators, d as defineMutator } from "./packem_shared/define-mutators.d-
|
|
1
|
+
export { type C as CheckpointRegistry, type E as ExecutorOutboxSinkOptions, type L as LunoraCollectionConfig, type a as LunoraCollectionOptions, O as OUTBOX_MUTATION_FN_NAME, type b as OutboxExecutor, type d as OutboxMutationMetadata, type R as Row, type S as SyncWriter, c as createCheckpointRegistry, e as createExecutorOutboxSink, f as createOptimisticOnlineDetector, l as lunoraCollectionOptions, m as makeDiffEmit, r as runOutboxMutation, t as toMap } from "./packem_shared/collection-options.d-lJBOVJgq.js";
|
|
2
|
+
export { type C as CollectionDef, type D as DefineCollectionsOptions, type I as InsertBinding, type L as LunoraDb, type W as WriteRejectedEvent, d as defineCollections } from "./packem_shared/define-collections.d-X2MfU5Es.js";
|
|
3
|
+
export { type B as BindMutatorsContext, type a as BoundMutators, type C as ClientMutatorContext, type b as ClientMutatorDef, c as bindMutators, d as defineMutator } from "./packem_shared/define-mutators.d-DhO8mWz0.js";
|
|
4
4
|
import '@lunora/client';
|
|
5
5
|
import '@tanstack/db';
|
|
6
6
|
import '@tanstack/offline-transactions';
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { createCheckpointRegistry, lunoraCollectionOptions } from './packem_shared/createCheckpointRegistry-
|
|
2
|
-
export { defineCollections } from './packem_shared/defineCollections-
|
|
3
|
-
export { bindMutators, defineMutator } from './packem_shared/bindMutators-
|
|
4
|
-
export { OUTBOX_MUTATION_FN_NAME, createExecutorOutboxSink, createOptimisticOnlineDetector, makeDiffEmit, runOutboxMutation, toMap } from './packem_shared/OUTBOX_MUTATION_FN_NAME-
|
|
1
|
+
export { createCheckpointRegistry, lunoraCollectionOptions } from './packem_shared/createCheckpointRegistry-SvwBczzv.mjs';
|
|
2
|
+
export { defineCollections } from './packem_shared/defineCollections-BJNtgnWB.mjs';
|
|
3
|
+
export { bindMutators, defineMutator } from './packem_shared/bindMutators-DNhICkoy.mjs';
|
|
4
|
+
export { OUTBOX_MUTATION_FN_NAME, createExecutorOutboxSink, createOptimisticOnlineDetector, makeDiffEmit, runOutboxMutation, toMap } from './packem_shared/OUTBOX_MUTATION_FN_NAME-Cf8iP6Wa.mjs';
|
|
5
5
|
|
|
6
6
|
const VERSION = "0.0.0";
|
|
7
7
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { type B as BindMutatorsContext, type a as BoundMutators, type C as ClientMutatorContext, type b as ClientMutatorDef, c as bindMutators, d as defineMutator } from "../packem_shared/define-mutators.d-
|
|
1
|
+
export { type B as BindMutatorsContext, type a as BoundMutators, type C as ClientMutatorContext, type b as ClientMutatorDef, c as bindMutators, d as defineMutator } from "../packem_shared/define-mutators.d-HWj94_nL.mjs";
|
|
2
2
|
import '@lunora/client';
|
|
3
3
|
import '@tanstack/db';
|
|
4
|
-
import "../packem_shared/collection-options.d-
|
|
4
|
+
import "../packem_shared/collection-options.d-lJBOVJgq.mjs";
|
|
5
5
|
import '@tanstack/offline-transactions';
|
package/dist/mutators/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { type B as BindMutatorsContext, type a as BoundMutators, type C as ClientMutatorContext, type b as ClientMutatorDef, c as bindMutators, d as defineMutator } from "../packem_shared/define-mutators.d-
|
|
1
|
+
export { type B as BindMutatorsContext, type a as BoundMutators, type C as ClientMutatorContext, type b as ClientMutatorDef, c as bindMutators, d as defineMutator } from "../packem_shared/define-mutators.d-DhO8mWz0.js";
|
|
2
2
|
import '@lunora/client';
|
|
3
3
|
import '@tanstack/db';
|
|
4
|
-
import "../packem_shared/collection-options.d-
|
|
4
|
+
import "../packem_shared/collection-options.d-lJBOVJgq.js";
|
|
5
5
|
import '@tanstack/offline-transactions';
|
package/dist/mutators/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { bindMutators, defineMutator } from '../packem_shared/bindMutators-
|
|
1
|
+
export { bindMutators, defineMutator } from '../packem_shared/bindMutators-DNhICkoy.mjs';
|
|
@@ -69,7 +69,9 @@ const runOutboxMutation = async (mutate) => {
|
|
|
69
69
|
await mutate();
|
|
70
70
|
} catch (error) {
|
|
71
71
|
if (typeof error.code === "string") {
|
|
72
|
-
|
|
72
|
+
const nonRetriable = new NonRetriableError(error instanceof Error ? error.message : String(error));
|
|
73
|
+
nonRetriable.code = error.code;
|
|
74
|
+
throw nonRetriable;
|
|
73
75
|
}
|
|
74
76
|
throw error;
|
|
75
77
|
}
|
|
@@ -176,6 +176,15 @@ interface LunoraCollectionConfig<TRow extends Row> {
|
|
|
176
176
|
id?: string;
|
|
177
177
|
/** The Lunora query that lists the rows (the full-table sync source). Mutually exclusive with {@link LunoraCollectionConfig.shape}. */
|
|
178
178
|
list?: FunctionReference;
|
|
179
|
+
/**
|
|
180
|
+
* When the collection starts syncing. `"lazy"` (default) starts on the first
|
|
181
|
+
* `useLiveQuery` subscriber; `"eager"` starts at creation (TanStack's
|
|
182
|
+
* `startSync`) — for small "instant" reference data you want warm at boot.
|
|
183
|
+
* No effect on a `scopeBy` collection, which has nothing to sync until scoped.
|
|
184
|
+
* (Even eager, TanStack pauses sync while there are no subscribers, per its
|
|
185
|
+
* `gcTime` lifecycle — "warm while referenced", not pinned forever.)
|
|
186
|
+
*/
|
|
187
|
+
load?: "eager" | "lazy";
|
|
179
188
|
/** Notified when the underlying subscription errors; the collection always leaves `loading` regardless. */
|
|
180
189
|
onError?: (error: SubscriptionError) => void;
|
|
181
190
|
/** When set, the collection stays empty until {@link LunoraCollectionOptions.scope} points it at args (sharded). */
|
package/dist/packem_shared/{collection-options.d-CbS3J_Fm.d.ts → collection-options.d-lJBOVJgq.d.ts}
RENAMED
|
@@ -176,6 +176,15 @@ interface LunoraCollectionConfig<TRow extends Row> {
|
|
|
176
176
|
id?: string;
|
|
177
177
|
/** The Lunora query that lists the rows (the full-table sync source). Mutually exclusive with {@link LunoraCollectionConfig.shape}. */
|
|
178
178
|
list?: FunctionReference;
|
|
179
|
+
/**
|
|
180
|
+
* When the collection starts syncing. `"lazy"` (default) starts on the first
|
|
181
|
+
* `useLiveQuery` subscriber; `"eager"` starts at creation (TanStack's
|
|
182
|
+
* `startSync`) — for small "instant" reference data you want warm at boot.
|
|
183
|
+
* No effect on a `scopeBy` collection, which has nothing to sync until scoped.
|
|
184
|
+
* (Even eager, TanStack pauses sync while there are no subscribers, per its
|
|
185
|
+
* `gcTime` lifecycle — "warm while referenced", not pinned forever.)
|
|
186
|
+
*/
|
|
187
|
+
load?: "eager" | "lazy";
|
|
179
188
|
/** Notified when the underlying subscription errors; the collection always leaves `loading` regardless. */
|
|
180
189
|
onError?: (error: SubscriptionError) => void;
|
|
181
190
|
/** When set, the collection stays empty until {@link LunoraCollectionOptions.scope} points it at args (sharded). */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BTreeIndex } from '@tanstack/db';
|
|
2
|
-
import { toMap, makeDiffEmit } from './OUTBOX_MUTATION_FN_NAME-
|
|
2
|
+
import { toMap, makeDiffEmit } from './OUTBOX_MUTATION_FN_NAME-Cf8iP6Wa.mjs';
|
|
3
3
|
|
|
4
4
|
const createGate = () => {
|
|
5
5
|
let highest = Number.NEGATIVE_INFINITY;
|
|
@@ -82,6 +82,9 @@ const lunoraCollectionOptions = (options) => {
|
|
|
82
82
|
defaultIndexType: BTreeIndex,
|
|
83
83
|
getKey,
|
|
84
84
|
id: options.id ?? options.list?.__lunoraRef ?? `shape:${options.shape?.name ?? ""}`,
|
|
85
|
+
// `"eager"` syncs at creation; omitted otherwise so the wire stays
|
|
86
|
+
// byte-identical to the lazy default (sync on first subscriber).
|
|
87
|
+
...options.load === "eager" ? { startSync: true } : {},
|
|
85
88
|
sync: {
|
|
86
89
|
sync: (writer) => {
|
|
87
90
|
emit = makeDiffEmit(syncedJson, writer);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FunctionReference, SubscriptionError, LunoraClient } from '@lunora/client';
|
|
2
2
|
import { Transaction, Collection } from '@tanstack/db';
|
|
3
|
-
import { OfflineExecutor } from '@tanstack/offline-transactions';
|
|
4
|
-
import { R as Row } from "./collection-options.d-
|
|
3
|
+
import { OfflineExecutor, StorageDiagnostic } from '@tanstack/offline-transactions';
|
|
4
|
+
import { R as Row } from "./collection-options.d-lJBOVJgq.mjs";
|
|
5
5
|
/** Element type of an array (the row type a `list` query returns). */
|
|
6
6
|
type Element<T> = T extends ReadonlyArray<infer E> ? E : never;
|
|
7
7
|
/** `true` for the `any` type, `false` otherwise. */
|
|
@@ -31,6 +31,15 @@ interface CollectionDef<TList extends FunctionReference, TInput = never> {
|
|
|
31
31
|
/** The Lunora query that lists the rows (the sync source). */
|
|
32
32
|
list: TList;
|
|
33
33
|
/**
|
|
34
|
+
* When this collection starts syncing — `"lazy"` (default) on the first
|
|
35
|
+
* `useLiveQuery` subscriber, or `"eager"` at creation, for small "instant"
|
|
36
|
+
* reference data you want warm at boot. Pairs with `scopeBy` for partial
|
|
37
|
+
* (per-scope) loading — together they give the full lazy/partial/eager
|
|
38
|
+
* (Linear `lazy`/`partial`/`instant`) load taxonomy declaratively. No effect
|
|
39
|
+
* on a `scopeBy` collection (nothing to sync until scoped).
|
|
40
|
+
*/
|
|
41
|
+
load?: "eager" | "lazy";
|
|
42
|
+
/**
|
|
34
43
|
* Notified when the underlying `list` subscription errors (e.g. the server
|
|
35
44
|
* rejects it). Without this the error would be swallowed and the collection
|
|
36
45
|
* could hang in `loading`; the binding always moves the collection out of
|
|
@@ -53,6 +62,62 @@ type InputOf<C> = C extends {
|
|
|
53
62
|
optimistic: (input: infer I, id: string) => unknown;
|
|
54
63
|
};
|
|
55
64
|
} ? I : never;
|
|
65
|
+
/** A queued write that was permanently dropped, passed to {@link DefineCollectionsOptions.onWriteRejected}. */
|
|
66
|
+
interface WriteRejectedEvent {
|
|
67
|
+
/**
|
|
68
|
+
* The machine-readable reason — the server's error `code` (e.g. `CONFLICT`,
|
|
69
|
+
* `FORBIDDEN`), or `UNKNOWN_MUTATION_FN` when the write referenced a collection
|
|
70
|
+
* that no longer exists (removed in a deploy). Mirrors the client's
|
|
71
|
+
* `MutationSettledEvent.code` so a consumer can branch on the verdict.
|
|
72
|
+
*/
|
|
73
|
+
code?: string;
|
|
74
|
+
/** The collection/table name the write targeted. */
|
|
75
|
+
collection: string;
|
|
76
|
+
/** The error that dropped the write (message carried by the underlying `NonRetriableError`). */
|
|
77
|
+
error: Error;
|
|
78
|
+
/**
|
|
79
|
+
* The optimistic row being rolled back (the rollback follows the callback).
|
|
80
|
+
* Absent only if the dropped transaction carried no recoverable row (e.g. some
|
|
81
|
+
* `UNKNOWN_MUTATION_FN` cases).
|
|
82
|
+
*/
|
|
83
|
+
row?: Row;
|
|
84
|
+
}
|
|
85
|
+
/** Options for {@link defineCollections}. */
|
|
86
|
+
interface DefineCollectionsOptions {
|
|
87
|
+
/**
|
|
88
|
+
* Invoked when a leadership change occurs across tabs (only the leader tab
|
|
89
|
+
* drains the durable outbox). Informational — useful for diagnostics; the
|
|
90
|
+
* library handles the election itself.
|
|
91
|
+
*/
|
|
92
|
+
onLeadershipChange?: (isLeader: boolean) => void;
|
|
93
|
+
/**
|
|
94
|
+
* Invoked when the durable outbox's storage layer fails — IndexedDB
|
|
95
|
+
* unavailable (private mode), blocked, or quota exceeded. The standalone
|
|
96
|
+
* client surfaces this via `offlineQueue.onPersistenceError`; this is the
|
|
97
|
+
* collection-layer counterpart. A storage failure means a write is NOT durable
|
|
98
|
+
* and won't survive a reload, so surface it (e.g. "your change may not be
|
|
99
|
+
* saved if you close this tab").
|
|
100
|
+
*/
|
|
101
|
+
onStorageFailure?: (diagnostic: StorageDiagnostic) => void;
|
|
102
|
+
/**
|
|
103
|
+
* Invoked as a queued write is permanently dropped: a coded application error
|
|
104
|
+
* from the server (validation, RLS denial, conflict, surfaced as a
|
|
105
|
+
* `NonRetriableError`), OR a write whose target collection no longer exists —
|
|
106
|
+
* removed/renamed in a deploy (`code: "UNKNOWN_MUTATION_FN"`). This is the
|
|
107
|
+
* aggregate, fire-and-forget-safe channel: unlike awaiting the per-action
|
|
108
|
+
* `transaction` returned by `actions[name](...)`, it fires even when the caller
|
|
109
|
+
* never retained that handle, so a UI can surface "couldn't save" instead of a
|
|
110
|
+
* silently vanishing row. Transient failures (offline, 5xx) are retried by the
|
|
111
|
+
* outbox, not reported here.
|
|
112
|
+
*
|
|
113
|
+
* Timing: the callback runs at the point of rejection; the executor's
|
|
114
|
+
* optimistic-row rollback follows immediately after. The event's `row` is the
|
|
115
|
+
* (about-to-be-removed) optimistic row, so don't depend on the collection
|
|
116
|
+
* already reflecting the removal from inside the handler — use `row`/`error`
|
|
117
|
+
* directly (e.g. for a toast).
|
|
118
|
+
*/
|
|
119
|
+
onWriteRejected?: (event: WriteRejectedEvent) => void;
|
|
120
|
+
}
|
|
56
121
|
/** The wired data layer `defineCollections` returns. */
|
|
57
122
|
interface LunoraDb<D extends Record<string, AnyDef>> {
|
|
58
123
|
/** Optimistic, durable, retried write actions — present for `insert` collections. */
|
|
@@ -81,5 +146,5 @@ interface LunoraDb<D extends Record<string, AnyDef>> {
|
|
|
81
146
|
* This is the hand-written form; `@lunora/codegen` can emit a fully-typed call to
|
|
82
147
|
* it from `schema.ts`, so an app writes nothing.
|
|
83
148
|
*/
|
|
84
|
-
declare const defineCollections: <D extends Record<string, AnyDef>>(client: LunoraClient, defs: D) => LunoraDb<D>;
|
|
85
|
-
export { CollectionDef as C, InsertBinding as I, LunoraDb as L, defineCollections as d };
|
|
149
|
+
declare const defineCollections: <D extends Record<string, AnyDef>>(client: LunoraClient, defs: D, options?: DefineCollectionsOptions) => LunoraDb<D>;
|
|
150
|
+
export { CollectionDef as C, DefineCollectionsOptions as D, InsertBinding as I, LunoraDb as L, WriteRejectedEvent as W, defineCollections as d };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FunctionReference, SubscriptionError, LunoraClient } from '@lunora/client';
|
|
2
2
|
import { Transaction, Collection } from '@tanstack/db';
|
|
3
|
-
import { OfflineExecutor } from '@tanstack/offline-transactions';
|
|
4
|
-
import { R as Row } from "./collection-options.d-
|
|
3
|
+
import { OfflineExecutor, StorageDiagnostic } from '@tanstack/offline-transactions';
|
|
4
|
+
import { R as Row } from "./collection-options.d-lJBOVJgq.js";
|
|
5
5
|
/** Element type of an array (the row type a `list` query returns). */
|
|
6
6
|
type Element<T> = T extends ReadonlyArray<infer E> ? E : never;
|
|
7
7
|
/** `true` for the `any` type, `false` otherwise. */
|
|
@@ -31,6 +31,15 @@ interface CollectionDef<TList extends FunctionReference, TInput = never> {
|
|
|
31
31
|
/** The Lunora query that lists the rows (the sync source). */
|
|
32
32
|
list: TList;
|
|
33
33
|
/**
|
|
34
|
+
* When this collection starts syncing — `"lazy"` (default) on the first
|
|
35
|
+
* `useLiveQuery` subscriber, or `"eager"` at creation, for small "instant"
|
|
36
|
+
* reference data you want warm at boot. Pairs with `scopeBy` for partial
|
|
37
|
+
* (per-scope) loading — together they give the full lazy/partial/eager
|
|
38
|
+
* (Linear `lazy`/`partial`/`instant`) load taxonomy declaratively. No effect
|
|
39
|
+
* on a `scopeBy` collection (nothing to sync until scoped).
|
|
40
|
+
*/
|
|
41
|
+
load?: "eager" | "lazy";
|
|
42
|
+
/**
|
|
34
43
|
* Notified when the underlying `list` subscription errors (e.g. the server
|
|
35
44
|
* rejects it). Without this the error would be swallowed and the collection
|
|
36
45
|
* could hang in `loading`; the binding always moves the collection out of
|
|
@@ -53,6 +62,62 @@ type InputOf<C> = C extends {
|
|
|
53
62
|
optimistic: (input: infer I, id: string) => unknown;
|
|
54
63
|
};
|
|
55
64
|
} ? I : never;
|
|
65
|
+
/** A queued write that was permanently dropped, passed to {@link DefineCollectionsOptions.onWriteRejected}. */
|
|
66
|
+
interface WriteRejectedEvent {
|
|
67
|
+
/**
|
|
68
|
+
* The machine-readable reason — the server's error `code` (e.g. `CONFLICT`,
|
|
69
|
+
* `FORBIDDEN`), or `UNKNOWN_MUTATION_FN` when the write referenced a collection
|
|
70
|
+
* that no longer exists (removed in a deploy). Mirrors the client's
|
|
71
|
+
* `MutationSettledEvent.code` so a consumer can branch on the verdict.
|
|
72
|
+
*/
|
|
73
|
+
code?: string;
|
|
74
|
+
/** The collection/table name the write targeted. */
|
|
75
|
+
collection: string;
|
|
76
|
+
/** The error that dropped the write (message carried by the underlying `NonRetriableError`). */
|
|
77
|
+
error: Error;
|
|
78
|
+
/**
|
|
79
|
+
* The optimistic row being rolled back (the rollback follows the callback).
|
|
80
|
+
* Absent only if the dropped transaction carried no recoverable row (e.g. some
|
|
81
|
+
* `UNKNOWN_MUTATION_FN` cases).
|
|
82
|
+
*/
|
|
83
|
+
row?: Row;
|
|
84
|
+
}
|
|
85
|
+
/** Options for {@link defineCollections}. */
|
|
86
|
+
interface DefineCollectionsOptions {
|
|
87
|
+
/**
|
|
88
|
+
* Invoked when a leadership change occurs across tabs (only the leader tab
|
|
89
|
+
* drains the durable outbox). Informational — useful for diagnostics; the
|
|
90
|
+
* library handles the election itself.
|
|
91
|
+
*/
|
|
92
|
+
onLeadershipChange?: (isLeader: boolean) => void;
|
|
93
|
+
/**
|
|
94
|
+
* Invoked when the durable outbox's storage layer fails — IndexedDB
|
|
95
|
+
* unavailable (private mode), blocked, or quota exceeded. The standalone
|
|
96
|
+
* client surfaces this via `offlineQueue.onPersistenceError`; this is the
|
|
97
|
+
* collection-layer counterpart. A storage failure means a write is NOT durable
|
|
98
|
+
* and won't survive a reload, so surface it (e.g. "your change may not be
|
|
99
|
+
* saved if you close this tab").
|
|
100
|
+
*/
|
|
101
|
+
onStorageFailure?: (diagnostic: StorageDiagnostic) => void;
|
|
102
|
+
/**
|
|
103
|
+
* Invoked as a queued write is permanently dropped: a coded application error
|
|
104
|
+
* from the server (validation, RLS denial, conflict, surfaced as a
|
|
105
|
+
* `NonRetriableError`), OR a write whose target collection no longer exists —
|
|
106
|
+
* removed/renamed in a deploy (`code: "UNKNOWN_MUTATION_FN"`). This is the
|
|
107
|
+
* aggregate, fire-and-forget-safe channel: unlike awaiting the per-action
|
|
108
|
+
* `transaction` returned by `actions[name](...)`, it fires even when the caller
|
|
109
|
+
* never retained that handle, so a UI can surface "couldn't save" instead of a
|
|
110
|
+
* silently vanishing row. Transient failures (offline, 5xx) are retried by the
|
|
111
|
+
* outbox, not reported here.
|
|
112
|
+
*
|
|
113
|
+
* Timing: the callback runs at the point of rejection; the executor's
|
|
114
|
+
* optimistic-row rollback follows immediately after. The event's `row` is the
|
|
115
|
+
* (about-to-be-removed) optimistic row, so don't depend on the collection
|
|
116
|
+
* already reflecting the removal from inside the handler — use `row`/`error`
|
|
117
|
+
* directly (e.g. for a toast).
|
|
118
|
+
*/
|
|
119
|
+
onWriteRejected?: (event: WriteRejectedEvent) => void;
|
|
120
|
+
}
|
|
56
121
|
/** The wired data layer `defineCollections` returns. */
|
|
57
122
|
interface LunoraDb<D extends Record<string, AnyDef>> {
|
|
58
123
|
/** Optimistic, durable, retried write actions — present for `insert` collections. */
|
|
@@ -81,5 +146,5 @@ interface LunoraDb<D extends Record<string, AnyDef>> {
|
|
|
81
146
|
* This is the hand-written form; `@lunora/codegen` can emit a fully-typed call to
|
|
82
147
|
* it from `schema.ts`, so an app writes nothing.
|
|
83
148
|
*/
|
|
84
|
-
declare const defineCollections: <D extends Record<string, AnyDef>>(client: LunoraClient, defs: D) => LunoraDb<D>;
|
|
85
|
-
export { CollectionDef as C, InsertBinding as I, LunoraDb as L, defineCollections as d };
|
|
149
|
+
declare const defineCollections: <D extends Record<string, AnyDef>>(client: LunoraClient, defs: D, options?: DefineCollectionsOptions) => LunoraDb<D>;
|
|
150
|
+
export { CollectionDef as C, DefineCollectionsOptions as D, InsertBinding as I, LunoraDb as L, WriteRejectedEvent as W, defineCollections as d };
|
package/dist/packem_shared/{define-mutators.d-C3DO284A.d.ts → define-mutators.d-DhO8mWz0.d.ts}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LunoraClient } from '@lunora/client';
|
|
2
2
|
import { Collection, Transaction } from '@tanstack/db';
|
|
3
|
-
import { C as CheckpointRegistry, R as Row } from "./collection-options.d-
|
|
3
|
+
import { C as CheckpointRegistry, R as Row } from "./collection-options.d-lJBOVJgq.js";
|
|
4
4
|
/** The local store a client mutator's optimistic body writes against. */
|
|
5
5
|
interface ClientMutatorContext {
|
|
6
6
|
/** The wired collections, keyed by name — apply optimistic inserts/updates/deletes here. */
|
package/dist/packem_shared/{define-mutators.d-DNVJIpW8.d.mts → define-mutators.d-HWj94_nL.d.mts}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LunoraClient } from '@lunora/client';
|
|
2
2
|
import { Collection, Transaction } from '@tanstack/db';
|
|
3
|
-
import { C as CheckpointRegistry, R as Row } from "./collection-options.d-
|
|
3
|
+
import { C as CheckpointRegistry, R as Row } from "./collection-options.d-lJBOVJgq.mjs";
|
|
4
4
|
/** The local store a client mutator's optimistic body writes against. */
|
|
5
5
|
interface ClientMutatorContext {
|
|
6
6
|
/** The wired collections, keyed by name — apply optimistic inserts/updates/deletes here. */
|
package/dist/packem_shared/{defineCollections-DR6fcIkQ.mjs → defineCollections-BJNtgnWB.mjs}
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createCollection } from '@tanstack/db';
|
|
2
2
|
import { startOfflineExecutor, NonRetriableError } from '@tanstack/offline-transactions';
|
|
3
|
-
import { lunoraCollectionOptions } from './createCheckpointRegistry-
|
|
4
|
-
import { createOptimisticOnlineDetector, runOutboxMutation, OUTBOX_MUTATION_FN_NAME } from './OUTBOX_MUTATION_FN_NAME-
|
|
3
|
+
import { lunoraCollectionOptions } from './createCheckpointRegistry-SvwBczzv.mjs';
|
|
4
|
+
import { createOptimisticOnlineDetector, runOutboxMutation, OUTBOX_MUTATION_FN_NAME } from './OUTBOX_MUTATION_FN_NAME-Cf8iP6Wa.mjs';
|
|
5
5
|
|
|
6
|
-
const defineCollections = (client, defs) => {
|
|
6
|
+
const defineCollections = (client, defs, options = {}) => {
|
|
7
7
|
const collections = {};
|
|
8
8
|
const scope = {};
|
|
9
9
|
const mutationFns = {};
|
|
@@ -16,6 +16,7 @@ const defineCollections = (client, defs) => {
|
|
|
16
16
|
id: name,
|
|
17
17
|
// `AnyDef` erases `list` to `any` (`TList = any`); it's a `FunctionReference` here.
|
|
18
18
|
list: definition.list,
|
|
19
|
+
...definition.load === void 0 ? {} : { load: definition.load },
|
|
19
20
|
onError: definition.onError,
|
|
20
21
|
scopeBy: definition.scopeBy
|
|
21
22
|
});
|
|
@@ -27,7 +28,17 @@ const defineCollections = (client, defs) => {
|
|
|
27
28
|
mutationFns[name] = async ({ transaction }) => {
|
|
28
29
|
for (const mutation of transaction.mutations) {
|
|
29
30
|
const row = mutation.modified;
|
|
30
|
-
|
|
31
|
+
try {
|
|
32
|
+
await runOutboxMutation(() => client.mutation(insert.mutation, insert.toArgs(row)));
|
|
33
|
+
} catch (error) {
|
|
34
|
+
if (error instanceof NonRetriableError && options.onWriteRejected) {
|
|
35
|
+
try {
|
|
36
|
+
options.onWriteRejected({ code: error.code, collection: name, error, row });
|
|
37
|
+
} catch {
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
31
42
|
}
|
|
32
43
|
};
|
|
33
44
|
}
|
|
@@ -47,7 +58,27 @@ const defineCollections = (client, defs) => {
|
|
|
47
58
|
const executor = startOfflineExecutor({
|
|
48
59
|
collections,
|
|
49
60
|
mutationFns,
|
|
50
|
-
onlineDetector: createOptimisticOnlineDetector()
|
|
61
|
+
onlineDetector: createOptimisticOnlineDetector(),
|
|
62
|
+
...options.onLeadershipChange ? { onLeadershipChange: options.onLeadershipChange } : {},
|
|
63
|
+
...options.onStorageFailure ? { onStorageFailure: options.onStorageFailure } : {},
|
|
64
|
+
// A persisted write whose target collection was removed/renamed in a deploy
|
|
65
|
+
// hits an unregistered mutationFn. The executor drops it as a
|
|
66
|
+
// NonRetriableError *before* our per-collection `mutationFns` catch runs, so
|
|
67
|
+
// this hook is the only place to surface it on `onWriteRejected`.
|
|
68
|
+
onUnknownMutationFn: (name, tx) => {
|
|
69
|
+
try {
|
|
70
|
+
options.onWriteRejected?.({
|
|
71
|
+
code: "UNKNOWN_MUTATION_FN",
|
|
72
|
+
collection: name,
|
|
73
|
+
error: new Error(`offline write dropped: mutation "${name}" no longer exists (removed or renamed in a deploy?)`),
|
|
74
|
+
// Best-effort recovered row: the persisted `modified` shape isn't a
|
|
75
|
+
// validated `Row`, and a batched transaction surfaces only its first
|
|
76
|
+
// mutation's row. Enough to describe the dropped write to the user.
|
|
77
|
+
row: tx.mutations[0]?.modified
|
|
78
|
+
});
|
|
79
|
+
} catch {
|
|
80
|
+
}
|
|
81
|
+
}
|
|
51
82
|
});
|
|
52
83
|
const actions = {};
|
|
53
84
|
for (const [name, definition] of entries) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/db",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.9",
|
|
4
4
|
"description": "TanStack DB binding: typed, live-synced collections and a durable offline outbox over the Lunora client",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"access": "public"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@lunora/client": "1.0.0-alpha.
|
|
57
|
+
"@lunora/client": "1.0.0-alpha.9"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"@tanstack/db": "^0.6.0",
|