taladb 0.9.1 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.browser.mjs +225 -34
- package/dist/index.d.mts +251 -13
- package/dist/index.d.ts +251 -13
- package/dist/index.js +228 -35
- package/dist/index.mjs +225 -34
- package/dist/index.react-native.mjs +225 -34
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -43,17 +43,30 @@ type Document = {
|
|
|
43
43
|
_id?: string;
|
|
44
44
|
[key: string]: Value | undefined;
|
|
45
45
|
};
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
/**
|
|
47
|
+
* The operators available on a single field.
|
|
48
|
+
*
|
|
49
|
+
* Deliberately **not** a conditional type. `T extends null | undefined ? … : …`
|
|
50
|
+
* is *distributive*, so for a union-typed field (`type: 'Cabin' | 'Villa' | …`)
|
|
51
|
+
* it spreads over every member and infers `$in?: 'Cabin'[] | 'Villa'[] | …`
|
|
52
|
+
* instead of `$in?: ('Cabin' | 'Villa' | …)[]` — making `$in` unusable on any
|
|
53
|
+
* union field without a cast.
|
|
54
|
+
*
|
|
55
|
+
* Tuple-wrapping the check (`[T] extends [null | undefined]`) stops the
|
|
56
|
+
* distribution but then strips `$exists` from optional fields, which is the one
|
|
57
|
+
* thing the conditional existed for. So there is no conditional at all: `$exists`
|
|
58
|
+
* is always available (it is meaningful on every field), and the value operators
|
|
59
|
+
* use `NonNullable<T>` so an optional field still compares against its real type.
|
|
60
|
+
*/
|
|
61
|
+
type FieldOps<T> = {
|
|
62
|
+
$eq?: NonNullable<T>;
|
|
63
|
+
$ne?: NonNullable<T>;
|
|
64
|
+
$gt?: NonNullable<T>;
|
|
65
|
+
$gte?: NonNullable<T>;
|
|
66
|
+
$lt?: NonNullable<T>;
|
|
67
|
+
$lte?: NonNullable<T>;
|
|
68
|
+
$in?: NonNullable<T>[];
|
|
69
|
+
$nin?: NonNullable<T>[];
|
|
57
70
|
$exists?: boolean;
|
|
58
71
|
/** Full-text search: matches documents where this string field contains the given token. */
|
|
59
72
|
$contains?: string;
|
|
@@ -96,6 +109,57 @@ type Update<T extends Document = Document> = {
|
|
|
96
109
|
interface Schema<T> {
|
|
97
110
|
parse(data: unknown): T;
|
|
98
111
|
}
|
|
112
|
+
/** Primitive field type for a {@link SyncSchema}. `'any'` requires presence
|
|
113
|
+
* without constraining the value's type. */
|
|
114
|
+
type SyncFieldType = 'bool' | 'int' | 'float' | 'str' | 'bytes' | 'array' | 'object' | 'any';
|
|
115
|
+
/**
|
|
116
|
+
* A tolerant, structural schema applied to documents **arriving via sync**
|
|
117
|
+
* (`db.sync()` pull). Distinct from {@link Schema} (Zod/Valibot), which is
|
|
118
|
+
* strict and runs on the *local* `insert` path: sync import is the boundary you
|
|
119
|
+
* don't control, so it validates structurally and never hard-rejects.
|
|
120
|
+
*
|
|
121
|
+
* On import, per document:
|
|
122
|
+
* - `_v` **below** `version` → upgraded in place (missing `defaults` filled,
|
|
123
|
+
* `_v` stamped) — additive-only migration.
|
|
124
|
+
* - `_v` **above** `version` → accepted untouched (the peer is ahead).
|
|
125
|
+
* - a missing/`null` `required` field or a `types` mismatch → **quarantined**
|
|
126
|
+
* (set aside, recoverable via {@link TalaDB.quarantined}), never dropped and
|
|
127
|
+
* never aborting the batch.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* const users = db.collection<User>('users', {
|
|
131
|
+
* schema: User, // strict, on insert
|
|
132
|
+
* syncSchema: { // tolerant, on import
|
|
133
|
+
* version: 1,
|
|
134
|
+
* required: ['name'],
|
|
135
|
+
* types: { name: 'str', age: 'int' },
|
|
136
|
+
* defaults: { age: 0 },
|
|
137
|
+
* },
|
|
138
|
+
* });
|
|
139
|
+
*/
|
|
140
|
+
interface SyncSchema {
|
|
141
|
+
/**
|
|
142
|
+
* Current document shape version. Omit or `0` to disable the migration step
|
|
143
|
+
* entirely — in which case {@link renames} and {@link defaults} never run, so
|
|
144
|
+
* declaring either without a `version` is rejected at `db.collection()` rather
|
|
145
|
+
* than silently quarantining the documents they were meant to upgrade.
|
|
146
|
+
*/
|
|
147
|
+
version?: number;
|
|
148
|
+
/** Fields that must be present and non-null, or the document is quarantined. */
|
|
149
|
+
required?: string[];
|
|
150
|
+
/** Expected primitive type per field. Fields absent here accept any type. */
|
|
151
|
+
types?: Record<string, SyncFieldType>;
|
|
152
|
+
/** Values applied to missing fields when upgrading a below-`version` document. */
|
|
153
|
+
defaults?: Record<string, Value>;
|
|
154
|
+
/**
|
|
155
|
+
* Field renames applied when upgrading a below-`version` document, as
|
|
156
|
+
* `{ oldName: newName }`. If the old field is present and the new one absent,
|
|
157
|
+
* the value moves. Applied before {@link defaults}. Structural (runs in the
|
|
158
|
+
* engine at import) — for renames that need computation, use
|
|
159
|
+
* {@link CollectionOptions.migrateDocument}.
|
|
160
|
+
*/
|
|
161
|
+
renames?: Record<string, string>;
|
|
162
|
+
}
|
|
99
163
|
/** Options passed to `db.collection()`. */
|
|
100
164
|
interface CollectionOptions<T extends Document = Document> {
|
|
101
165
|
/**
|
|
@@ -113,6 +177,53 @@ interface CollectionOptions<T extends Document = Document> {
|
|
|
113
177
|
* Defaults to `false`.
|
|
114
178
|
*/
|
|
115
179
|
validateOnRead?: boolean;
|
|
180
|
+
/**
|
|
181
|
+
* Tolerant structural schema applied to documents arriving via `db.sync()`.
|
|
182
|
+
* See {@link SyncSchema}. Enables validate-on-import ("validate, never cast")
|
|
183
|
+
* in the core sync path, with `_v` migration and quarantine of bad shapes.
|
|
184
|
+
* Wired on browser (OPFS worker), Node.js, and React Native; a binding whose
|
|
185
|
+
* native module predates 0.9.2 falls back to unvalidated import.
|
|
186
|
+
*
|
|
187
|
+
* Declaring a `version` also makes locally-inserted documents carry that `_v`,
|
|
188
|
+
* so they are never mistaken for legacy documents on read.
|
|
189
|
+
*/
|
|
190
|
+
syncSchema?: SyncSchema;
|
|
191
|
+
/**
|
|
192
|
+
* Lazy, read-time document migration — the arbitrary-JS complement to the
|
|
193
|
+
* structural {@link SyncSchema}. When set, every document returned by `find`
|
|
194
|
+
* / `findOne` whose `_v` is **below** `syncSchema.version` is passed through
|
|
195
|
+
* `migrateDocument(doc, fromVersion)` and stamped to the current version
|
|
196
|
+
* before you see it, so application code always reads the current shape even
|
|
197
|
+
* for documents that predate the schema (renames, computed/derived fields,
|
|
198
|
+
* splits/merges). Runs on **every runtime** (it's a pure read transform in
|
|
199
|
+
* the client — no binding support needed).
|
|
200
|
+
*
|
|
201
|
+
* Requires `syncSchema.version` (the migration target). The transform is
|
|
202
|
+
* applied to the returned value only; it is not persisted back to storage —
|
|
203
|
+
* pair with `openDB({ migrations })` or a `syncSchema` rename to rewrite
|
|
204
|
+
* stored documents eagerly. Must be pure and deterministic.
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* const users = db.collection<User>('users', {
|
|
208
|
+
* syncSchema: { version: 2 },
|
|
209
|
+
* migrateDocument: (doc, from) =>
|
|
210
|
+
* from < 2 ? { ...doc, fullName: `${doc.first} ${doc.last}` } : doc,
|
|
211
|
+
* });
|
|
212
|
+
*/
|
|
213
|
+
migrateDocument?: (doc: T, fromVersion: number) => T;
|
|
214
|
+
/**
|
|
215
|
+
* When `true`, a document upgraded by {@link migrateDocument} on read is
|
|
216
|
+
* **written back** to storage (a best-effort `updateOne` computing the
|
|
217
|
+
* `$set`/`$unset` diff) so the migration becomes permanent — after which
|
|
218
|
+
* filters and indexes on the new shape match it. Default `false` (the
|
|
219
|
+
* migrated shape is returned but not persisted).
|
|
220
|
+
*
|
|
221
|
+
* Trade-offs: reads that encounter un-migrated documents now issue writes
|
|
222
|
+
* (which fire live-query and sync-hook notifications like any other write);
|
|
223
|
+
* a failed write is swallowed and simply retried on the next read. For a
|
|
224
|
+
* one-shot eager rewrite instead, prefer `openDB({ migrations })`.
|
|
225
|
+
*/
|
|
226
|
+
persistMigrations?: boolean;
|
|
116
227
|
}
|
|
117
228
|
/** A single MongoDB-style aggregation stage. */
|
|
118
229
|
type AggregateStage<T extends Document = Document> = {
|
|
@@ -129,7 +240,14 @@ type AggregateStage<T extends Document = Document> = {
|
|
|
129
240
|
$skip: number;
|
|
130
241
|
} | {
|
|
131
242
|
$limit: number;
|
|
132
|
-
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Reshape each document. Either an **inclusion** (`{ name: 1, city: 1 }` —
|
|
246
|
+
* keep only these) or an **exclusion** (`{ description: 0 }` — keep everything
|
|
247
|
+
* else). The two cannot be mixed and doing so throws; `_id: 0` is the one
|
|
248
|
+
* exclusion allowed alongside an inclusion.
|
|
249
|
+
*/
|
|
250
|
+
| {
|
|
133
251
|
$project: Record<string, 0 | 1>;
|
|
134
252
|
};
|
|
135
253
|
/** An ordered aggregation pipeline. */
|
|
@@ -296,9 +414,30 @@ interface SyncResult {
|
|
|
296
414
|
pushed: number;
|
|
297
415
|
/** Number of documents changed locally by the pulled remote changeset. */
|
|
298
416
|
pulled: number;
|
|
417
|
+
/**
|
|
418
|
+
* Documents in the pulled changeset skipped by an import validator (a
|
|
419
|
+
* collection this client does not model). Always `0` when no `syncSchema`
|
|
420
|
+
* applied to the pass.
|
|
421
|
+
*/
|
|
422
|
+
skipped?: number;
|
|
423
|
+
/**
|
|
424
|
+
* Documents in the pulled changeset set aside by an import validator because
|
|
425
|
+
* they failed structural validation. Recoverable via {@link TalaDB.quarantined}.
|
|
426
|
+
* Always `0` when no `syncSchema` applied to the pass.
|
|
427
|
+
*/
|
|
428
|
+
quarantined?: number;
|
|
299
429
|
/** Active sync cursor. Currently `0` because timestamp adapters replay safely. */
|
|
300
430
|
cursor: number;
|
|
301
431
|
}
|
|
432
|
+
/** A document set aside during a validated sync import, with its rejection reason. */
|
|
433
|
+
interface QuarantinedDocument<T extends Document = Document> {
|
|
434
|
+
/** The rejected document, retained verbatim. */
|
|
435
|
+
document: T;
|
|
436
|
+
/** Human-readable reason the document was quarantined. */
|
|
437
|
+
reason: string;
|
|
438
|
+
/** The `changed_at` (ms epoch) the rejected change carried. */
|
|
439
|
+
changedAt: number;
|
|
440
|
+
}
|
|
302
441
|
interface TalaDB {
|
|
303
442
|
collection<T extends Document = Document>(name: string, options?: CollectionOptions<T>): Collection<T>;
|
|
304
443
|
/**
|
|
@@ -334,6 +473,20 @@ interface TalaDB {
|
|
|
334
473
|
* await db.compact();
|
|
335
474
|
*/
|
|
336
475
|
compact(): Promise<void>;
|
|
476
|
+
/**
|
|
477
|
+
* Return the documents set aside in `collection`'s quarantine table by a
|
|
478
|
+
* validated sync import (see {@link SyncSchema}). Empty when nothing was
|
|
479
|
+
* quarantined. Wired on browser and Node.js; resolves to `[]` on runtimes
|
|
480
|
+
* without support.
|
|
481
|
+
*/
|
|
482
|
+
quarantined?<T extends Document = Document>(collection: string): Promise<QuarantinedDocument<T>[]>;
|
|
483
|
+
/**
|
|
484
|
+
* Force any batched (eventual-durability) writes to durable storage, and on
|
|
485
|
+
* the browser also write the IndexedDB fallback snapshot immediately. A
|
|
486
|
+
* no-op under the default `flush_every_write: true` durability. Use for
|
|
487
|
+
* "save now" moments (before checkout, on `visibilitychange`).
|
|
488
|
+
*/
|
|
489
|
+
flush?(): Promise<void>;
|
|
337
490
|
/** Browser HTTP-push queue health, when supported by the active binding. */
|
|
338
491
|
syncStatus?(): Promise<{
|
|
339
492
|
pending: number;
|
|
@@ -377,10 +530,28 @@ interface SyncConfig {
|
|
|
377
530
|
*/
|
|
378
531
|
exclude_fields?: string[];
|
|
379
532
|
}
|
|
533
|
+
/** Storage durability settings. */
|
|
534
|
+
interface DurabilityConfig {
|
|
535
|
+
/**
|
|
536
|
+
* When `true` (default), every write commit is fsync'd immediately — a crash
|
|
537
|
+
* never loses an acknowledged write. When `false`, commits are batched for
|
|
538
|
+
* higher write throughput; call `db.flush()` to force a durable sync. Applies
|
|
539
|
+
* to Node (file) and browser OPFS storage; in-memory ignores it.
|
|
540
|
+
*/
|
|
541
|
+
flush_every_write?: boolean;
|
|
542
|
+
/**
|
|
543
|
+
* Browser IndexedDB-fallback snapshot debounce, in milliseconds (default
|
|
544
|
+
* 500). Only affects the non-OPFS browser fallback path — the OPFS and Node
|
|
545
|
+
* paths use `flush_every_write`.
|
|
546
|
+
*/
|
|
547
|
+
flush_ms?: number;
|
|
548
|
+
}
|
|
380
549
|
/** Top-level TalaDB configuration. */
|
|
381
550
|
interface TalaDbConfig {
|
|
382
551
|
/** HTTP push sync configuration. Disabled by default. */
|
|
383
552
|
sync?: SyncConfig;
|
|
553
|
+
/** Storage durability configuration. */
|
|
554
|
+
durability?: DurabilityConfig;
|
|
384
555
|
}
|
|
385
556
|
|
|
386
557
|
interface HttpSyncAdapterOptions {
|
|
@@ -430,11 +601,70 @@ declare class TalaDbValidationError extends Error {
|
|
|
430
601
|
readonly cause: unknown;
|
|
431
602
|
constructor(cause: unknown, context?: string);
|
|
432
603
|
}
|
|
604
|
+
/**
|
|
605
|
+
* Wraps a `Collection<T>` to intercept writes through a schema validator
|
|
606
|
+
* (`schema`), stamp `_v` on insert when a `syncSchema.version` is declared, and
|
|
607
|
+
* normalize reads through a lazy `migrateDocument`. Returns the collection
|
|
608
|
+
* unchanged when none of those apply.
|
|
609
|
+
*
|
|
610
|
+
* Read normalization covers `find`, `findOne`, and `subscribe` (live queries).
|
|
611
|
+
* It cannot cover `aggregate`, `findNearest`, or FTS `search`: those run inside
|
|
612
|
+
* the engine against the *stored* shape, so a below-version document is matched
|
|
613
|
+
* and projected as it sits on disk. Use `persistMigrations` or
|
|
614
|
+
* `openDB({ migrations })` to rewrite storage if you query old documents that way.
|
|
615
|
+
*
|
|
616
|
+
* @internal Exported for unit testing; not part of the public API surface.
|
|
617
|
+
*/
|
|
618
|
+
declare function applySchema<T extends Document>(col: Collection<T>, options: CollectionOptions<T>): Collection<T>;
|
|
433
619
|
|
|
620
|
+
/**
|
|
621
|
+
* A single application schema migration, run once at `openDB` when its
|
|
622
|
+
* `version` is greater than the database's stored migration version.
|
|
623
|
+
*/
|
|
624
|
+
interface Migration {
|
|
625
|
+
/** Monotonic version. Must be a positive integer, unique across the array. */
|
|
626
|
+
version: number;
|
|
627
|
+
/** Optional human-readable label for logs. */
|
|
628
|
+
description?: string;
|
|
629
|
+
/**
|
|
630
|
+
* The migration body. Receives the open database and may use the full
|
|
631
|
+
* collection API. Runs to completion before the version is advanced.
|
|
632
|
+
*
|
|
633
|
+
* **Write migrations idempotently.** TalaDB checkpoints per version (the
|
|
634
|
+
* stored version advances only after `up` fully resolves), but a single `up`
|
|
635
|
+
* is not wrapped in one atomic transaction — if it throws partway, the writes
|
|
636
|
+
* it already made persist and `up` re-runs from the start on the next open.
|
|
637
|
+
*/
|
|
638
|
+
up: (db: TalaDB) => Promise<void> | void;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Runtime-agnostic migration runner. Each binding supplies `getVersion` /
|
|
642
|
+
* `setVersion` (its own persisted counter); the loop is identical everywhere.
|
|
643
|
+
*
|
|
644
|
+
* Runs pending migrations (`version` > stored) in ascending order, advancing
|
|
645
|
+
* the stored version after each `up` resolves — checkpoint per version. If an
|
|
646
|
+
* `up` throws, the loop stops and the error propagates; the stored version
|
|
647
|
+
* reflects the last fully-applied migration, so the next open resumes there.
|
|
648
|
+
*
|
|
649
|
+
* @internal Exported for unit testing; not part of the public API surface.
|
|
650
|
+
*/
|
|
651
|
+
declare function runMigrations(db: TalaDB, getVersion: () => Promise<number>, setVersion: (v: number) => Promise<void>, migrations: Migration[]): Promise<void>;
|
|
434
652
|
/** Options for `openDB`. */
|
|
435
653
|
interface OpenDBOptions {
|
|
436
654
|
/** Encrypt native database values at rest. Never hard-code this value. */
|
|
437
655
|
passphrase?: string;
|
|
656
|
+
/**
|
|
657
|
+
* Ordered application schema migrations, run once each at open in ascending
|
|
658
|
+
* `version` order (only those newer than the stored migration version). The
|
|
659
|
+
* stored version advances after each migration succeeds — checkpoint per
|
|
660
|
+
* version, resuming from the last applied one on the next open.
|
|
661
|
+
*
|
|
662
|
+
* Supported on Node.js, the browser (via the OPFS worker), and React Native.
|
|
663
|
+
* On a binding whose native module predates 0.9.2 (no `userVersion` /
|
|
664
|
+
* `setUserVersion`), `openDB` throws rather than silently skipping the
|
|
665
|
+
* migrations — rebuild or update the native module.
|
|
666
|
+
*/
|
|
667
|
+
migrations?: Migration[];
|
|
438
668
|
/**
|
|
439
669
|
* Explicit path to a `taladb.config.yml` / `taladb.config.json` file.
|
|
440
670
|
* If omitted, TalaDB auto-discovers the file from `process.cwd()` on Node.js.
|
|
@@ -448,6 +678,14 @@ interface OpenDBOptions {
|
|
|
448
678
|
* Useful for passing config programmatically without a config file on disk.
|
|
449
679
|
*/
|
|
450
680
|
config?: TalaDbConfig;
|
|
681
|
+
/**
|
|
682
|
+
* Storage durability, e.g. `{ flush_every_write: false }` to batch commits
|
|
683
|
+
* for write throughput (call `db.flush()` to force a sync), or `{ flush_ms }`
|
|
684
|
+
* to tune the browser IndexedDB-fallback snapshot debounce. Merged into
|
|
685
|
+
* `config.durability`. Node + browser; on React Native pass it in the config
|
|
686
|
+
* JSON to `TalaDBModule.initialize`.
|
|
687
|
+
*/
|
|
688
|
+
durability?: DurabilityConfig;
|
|
451
689
|
}
|
|
452
690
|
/**
|
|
453
691
|
* Open a TalaDB database.
|
|
@@ -466,4 +704,4 @@ interface OpenDBOptions {
|
|
|
466
704
|
*/
|
|
467
705
|
declare function openDB(dbName?: string, options?: OpenDBOptions): Promise<TalaDB>;
|
|
468
706
|
|
|
469
|
-
export { type AggregatePipeline, type AggregateStage, type Collection, type CollectionIndexInfo, type CollectionOptions, type Document, type Filter, HttpSyncAdapter, type OpenDBOptions, type Schema, type SerializedChangeset, type SyncAdapter, type SyncConfig, type SyncDirection, type SyncOptions, type SyncResult, type TalaDB, type TalaDbConfig, TalaDbValidationError, type Update, type Value, type VectorIndexOptions, type VectorMetric, type VectorSearchResult, openDB };
|
|
707
|
+
export { type AggregatePipeline, type AggregateStage, type Collection, type CollectionIndexInfo, type CollectionOptions, type Document, type DurabilityConfig, type Filter, HttpSyncAdapter, type Migration, type OpenDBOptions, type Schema, type SerializedChangeset, type SyncAdapter, type SyncConfig, type SyncDirection, type SyncOptions, type SyncResult, type TalaDB, type TalaDbConfig, TalaDbValidationError, type Update, type Value, type VectorIndexOptions, type VectorMetric, type VectorSearchResult, applySchema, openDB, runMigrations };
|
package/dist/index.d.ts
CHANGED
|
@@ -43,17 +43,30 @@ type Document = {
|
|
|
43
43
|
_id?: string;
|
|
44
44
|
[key: string]: Value | undefined;
|
|
45
45
|
};
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
/**
|
|
47
|
+
* The operators available on a single field.
|
|
48
|
+
*
|
|
49
|
+
* Deliberately **not** a conditional type. `T extends null | undefined ? … : …`
|
|
50
|
+
* is *distributive*, so for a union-typed field (`type: 'Cabin' | 'Villa' | …`)
|
|
51
|
+
* it spreads over every member and infers `$in?: 'Cabin'[] | 'Villa'[] | …`
|
|
52
|
+
* instead of `$in?: ('Cabin' | 'Villa' | …)[]` — making `$in` unusable on any
|
|
53
|
+
* union field without a cast.
|
|
54
|
+
*
|
|
55
|
+
* Tuple-wrapping the check (`[T] extends [null | undefined]`) stops the
|
|
56
|
+
* distribution but then strips `$exists` from optional fields, which is the one
|
|
57
|
+
* thing the conditional existed for. So there is no conditional at all: `$exists`
|
|
58
|
+
* is always available (it is meaningful on every field), and the value operators
|
|
59
|
+
* use `NonNullable<T>` so an optional field still compares against its real type.
|
|
60
|
+
*/
|
|
61
|
+
type FieldOps<T> = {
|
|
62
|
+
$eq?: NonNullable<T>;
|
|
63
|
+
$ne?: NonNullable<T>;
|
|
64
|
+
$gt?: NonNullable<T>;
|
|
65
|
+
$gte?: NonNullable<T>;
|
|
66
|
+
$lt?: NonNullable<T>;
|
|
67
|
+
$lte?: NonNullable<T>;
|
|
68
|
+
$in?: NonNullable<T>[];
|
|
69
|
+
$nin?: NonNullable<T>[];
|
|
57
70
|
$exists?: boolean;
|
|
58
71
|
/** Full-text search: matches documents where this string field contains the given token. */
|
|
59
72
|
$contains?: string;
|
|
@@ -96,6 +109,57 @@ type Update<T extends Document = Document> = {
|
|
|
96
109
|
interface Schema<T> {
|
|
97
110
|
parse(data: unknown): T;
|
|
98
111
|
}
|
|
112
|
+
/** Primitive field type for a {@link SyncSchema}. `'any'` requires presence
|
|
113
|
+
* without constraining the value's type. */
|
|
114
|
+
type SyncFieldType = 'bool' | 'int' | 'float' | 'str' | 'bytes' | 'array' | 'object' | 'any';
|
|
115
|
+
/**
|
|
116
|
+
* A tolerant, structural schema applied to documents **arriving via sync**
|
|
117
|
+
* (`db.sync()` pull). Distinct from {@link Schema} (Zod/Valibot), which is
|
|
118
|
+
* strict and runs on the *local* `insert` path: sync import is the boundary you
|
|
119
|
+
* don't control, so it validates structurally and never hard-rejects.
|
|
120
|
+
*
|
|
121
|
+
* On import, per document:
|
|
122
|
+
* - `_v` **below** `version` → upgraded in place (missing `defaults` filled,
|
|
123
|
+
* `_v` stamped) — additive-only migration.
|
|
124
|
+
* - `_v` **above** `version` → accepted untouched (the peer is ahead).
|
|
125
|
+
* - a missing/`null` `required` field or a `types` mismatch → **quarantined**
|
|
126
|
+
* (set aside, recoverable via {@link TalaDB.quarantined}), never dropped and
|
|
127
|
+
* never aborting the batch.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* const users = db.collection<User>('users', {
|
|
131
|
+
* schema: User, // strict, on insert
|
|
132
|
+
* syncSchema: { // tolerant, on import
|
|
133
|
+
* version: 1,
|
|
134
|
+
* required: ['name'],
|
|
135
|
+
* types: { name: 'str', age: 'int' },
|
|
136
|
+
* defaults: { age: 0 },
|
|
137
|
+
* },
|
|
138
|
+
* });
|
|
139
|
+
*/
|
|
140
|
+
interface SyncSchema {
|
|
141
|
+
/**
|
|
142
|
+
* Current document shape version. Omit or `0` to disable the migration step
|
|
143
|
+
* entirely — in which case {@link renames} and {@link defaults} never run, so
|
|
144
|
+
* declaring either without a `version` is rejected at `db.collection()` rather
|
|
145
|
+
* than silently quarantining the documents they were meant to upgrade.
|
|
146
|
+
*/
|
|
147
|
+
version?: number;
|
|
148
|
+
/** Fields that must be present and non-null, or the document is quarantined. */
|
|
149
|
+
required?: string[];
|
|
150
|
+
/** Expected primitive type per field. Fields absent here accept any type. */
|
|
151
|
+
types?: Record<string, SyncFieldType>;
|
|
152
|
+
/** Values applied to missing fields when upgrading a below-`version` document. */
|
|
153
|
+
defaults?: Record<string, Value>;
|
|
154
|
+
/**
|
|
155
|
+
* Field renames applied when upgrading a below-`version` document, as
|
|
156
|
+
* `{ oldName: newName }`. If the old field is present and the new one absent,
|
|
157
|
+
* the value moves. Applied before {@link defaults}. Structural (runs in the
|
|
158
|
+
* engine at import) — for renames that need computation, use
|
|
159
|
+
* {@link CollectionOptions.migrateDocument}.
|
|
160
|
+
*/
|
|
161
|
+
renames?: Record<string, string>;
|
|
162
|
+
}
|
|
99
163
|
/** Options passed to `db.collection()`. */
|
|
100
164
|
interface CollectionOptions<T extends Document = Document> {
|
|
101
165
|
/**
|
|
@@ -113,6 +177,53 @@ interface CollectionOptions<T extends Document = Document> {
|
|
|
113
177
|
* Defaults to `false`.
|
|
114
178
|
*/
|
|
115
179
|
validateOnRead?: boolean;
|
|
180
|
+
/**
|
|
181
|
+
* Tolerant structural schema applied to documents arriving via `db.sync()`.
|
|
182
|
+
* See {@link SyncSchema}. Enables validate-on-import ("validate, never cast")
|
|
183
|
+
* in the core sync path, with `_v` migration and quarantine of bad shapes.
|
|
184
|
+
* Wired on browser (OPFS worker), Node.js, and React Native; a binding whose
|
|
185
|
+
* native module predates 0.9.2 falls back to unvalidated import.
|
|
186
|
+
*
|
|
187
|
+
* Declaring a `version` also makes locally-inserted documents carry that `_v`,
|
|
188
|
+
* so they are never mistaken for legacy documents on read.
|
|
189
|
+
*/
|
|
190
|
+
syncSchema?: SyncSchema;
|
|
191
|
+
/**
|
|
192
|
+
* Lazy, read-time document migration — the arbitrary-JS complement to the
|
|
193
|
+
* structural {@link SyncSchema}. When set, every document returned by `find`
|
|
194
|
+
* / `findOne` whose `_v` is **below** `syncSchema.version` is passed through
|
|
195
|
+
* `migrateDocument(doc, fromVersion)` and stamped to the current version
|
|
196
|
+
* before you see it, so application code always reads the current shape even
|
|
197
|
+
* for documents that predate the schema (renames, computed/derived fields,
|
|
198
|
+
* splits/merges). Runs on **every runtime** (it's a pure read transform in
|
|
199
|
+
* the client — no binding support needed).
|
|
200
|
+
*
|
|
201
|
+
* Requires `syncSchema.version` (the migration target). The transform is
|
|
202
|
+
* applied to the returned value only; it is not persisted back to storage —
|
|
203
|
+
* pair with `openDB({ migrations })` or a `syncSchema` rename to rewrite
|
|
204
|
+
* stored documents eagerly. Must be pure and deterministic.
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* const users = db.collection<User>('users', {
|
|
208
|
+
* syncSchema: { version: 2 },
|
|
209
|
+
* migrateDocument: (doc, from) =>
|
|
210
|
+
* from < 2 ? { ...doc, fullName: `${doc.first} ${doc.last}` } : doc,
|
|
211
|
+
* });
|
|
212
|
+
*/
|
|
213
|
+
migrateDocument?: (doc: T, fromVersion: number) => T;
|
|
214
|
+
/**
|
|
215
|
+
* When `true`, a document upgraded by {@link migrateDocument} on read is
|
|
216
|
+
* **written back** to storage (a best-effort `updateOne` computing the
|
|
217
|
+
* `$set`/`$unset` diff) so the migration becomes permanent — after which
|
|
218
|
+
* filters and indexes on the new shape match it. Default `false` (the
|
|
219
|
+
* migrated shape is returned but not persisted).
|
|
220
|
+
*
|
|
221
|
+
* Trade-offs: reads that encounter un-migrated documents now issue writes
|
|
222
|
+
* (which fire live-query and sync-hook notifications like any other write);
|
|
223
|
+
* a failed write is swallowed and simply retried on the next read. For a
|
|
224
|
+
* one-shot eager rewrite instead, prefer `openDB({ migrations })`.
|
|
225
|
+
*/
|
|
226
|
+
persistMigrations?: boolean;
|
|
116
227
|
}
|
|
117
228
|
/** A single MongoDB-style aggregation stage. */
|
|
118
229
|
type AggregateStage<T extends Document = Document> = {
|
|
@@ -129,7 +240,14 @@ type AggregateStage<T extends Document = Document> = {
|
|
|
129
240
|
$skip: number;
|
|
130
241
|
} | {
|
|
131
242
|
$limit: number;
|
|
132
|
-
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Reshape each document. Either an **inclusion** (`{ name: 1, city: 1 }` —
|
|
246
|
+
* keep only these) or an **exclusion** (`{ description: 0 }` — keep everything
|
|
247
|
+
* else). The two cannot be mixed and doing so throws; `_id: 0` is the one
|
|
248
|
+
* exclusion allowed alongside an inclusion.
|
|
249
|
+
*/
|
|
250
|
+
| {
|
|
133
251
|
$project: Record<string, 0 | 1>;
|
|
134
252
|
};
|
|
135
253
|
/** An ordered aggregation pipeline. */
|
|
@@ -296,9 +414,30 @@ interface SyncResult {
|
|
|
296
414
|
pushed: number;
|
|
297
415
|
/** Number of documents changed locally by the pulled remote changeset. */
|
|
298
416
|
pulled: number;
|
|
417
|
+
/**
|
|
418
|
+
* Documents in the pulled changeset skipped by an import validator (a
|
|
419
|
+
* collection this client does not model). Always `0` when no `syncSchema`
|
|
420
|
+
* applied to the pass.
|
|
421
|
+
*/
|
|
422
|
+
skipped?: number;
|
|
423
|
+
/**
|
|
424
|
+
* Documents in the pulled changeset set aside by an import validator because
|
|
425
|
+
* they failed structural validation. Recoverable via {@link TalaDB.quarantined}.
|
|
426
|
+
* Always `0` when no `syncSchema` applied to the pass.
|
|
427
|
+
*/
|
|
428
|
+
quarantined?: number;
|
|
299
429
|
/** Active sync cursor. Currently `0` because timestamp adapters replay safely. */
|
|
300
430
|
cursor: number;
|
|
301
431
|
}
|
|
432
|
+
/** A document set aside during a validated sync import, with its rejection reason. */
|
|
433
|
+
interface QuarantinedDocument<T extends Document = Document> {
|
|
434
|
+
/** The rejected document, retained verbatim. */
|
|
435
|
+
document: T;
|
|
436
|
+
/** Human-readable reason the document was quarantined. */
|
|
437
|
+
reason: string;
|
|
438
|
+
/** The `changed_at` (ms epoch) the rejected change carried. */
|
|
439
|
+
changedAt: number;
|
|
440
|
+
}
|
|
302
441
|
interface TalaDB {
|
|
303
442
|
collection<T extends Document = Document>(name: string, options?: CollectionOptions<T>): Collection<T>;
|
|
304
443
|
/**
|
|
@@ -334,6 +473,20 @@ interface TalaDB {
|
|
|
334
473
|
* await db.compact();
|
|
335
474
|
*/
|
|
336
475
|
compact(): Promise<void>;
|
|
476
|
+
/**
|
|
477
|
+
* Return the documents set aside in `collection`'s quarantine table by a
|
|
478
|
+
* validated sync import (see {@link SyncSchema}). Empty when nothing was
|
|
479
|
+
* quarantined. Wired on browser and Node.js; resolves to `[]` on runtimes
|
|
480
|
+
* without support.
|
|
481
|
+
*/
|
|
482
|
+
quarantined?<T extends Document = Document>(collection: string): Promise<QuarantinedDocument<T>[]>;
|
|
483
|
+
/**
|
|
484
|
+
* Force any batched (eventual-durability) writes to durable storage, and on
|
|
485
|
+
* the browser also write the IndexedDB fallback snapshot immediately. A
|
|
486
|
+
* no-op under the default `flush_every_write: true` durability. Use for
|
|
487
|
+
* "save now" moments (before checkout, on `visibilitychange`).
|
|
488
|
+
*/
|
|
489
|
+
flush?(): Promise<void>;
|
|
337
490
|
/** Browser HTTP-push queue health, when supported by the active binding. */
|
|
338
491
|
syncStatus?(): Promise<{
|
|
339
492
|
pending: number;
|
|
@@ -377,10 +530,28 @@ interface SyncConfig {
|
|
|
377
530
|
*/
|
|
378
531
|
exclude_fields?: string[];
|
|
379
532
|
}
|
|
533
|
+
/** Storage durability settings. */
|
|
534
|
+
interface DurabilityConfig {
|
|
535
|
+
/**
|
|
536
|
+
* When `true` (default), every write commit is fsync'd immediately — a crash
|
|
537
|
+
* never loses an acknowledged write. When `false`, commits are batched for
|
|
538
|
+
* higher write throughput; call `db.flush()` to force a durable sync. Applies
|
|
539
|
+
* to Node (file) and browser OPFS storage; in-memory ignores it.
|
|
540
|
+
*/
|
|
541
|
+
flush_every_write?: boolean;
|
|
542
|
+
/**
|
|
543
|
+
* Browser IndexedDB-fallback snapshot debounce, in milliseconds (default
|
|
544
|
+
* 500). Only affects the non-OPFS browser fallback path — the OPFS and Node
|
|
545
|
+
* paths use `flush_every_write`.
|
|
546
|
+
*/
|
|
547
|
+
flush_ms?: number;
|
|
548
|
+
}
|
|
380
549
|
/** Top-level TalaDB configuration. */
|
|
381
550
|
interface TalaDbConfig {
|
|
382
551
|
/** HTTP push sync configuration. Disabled by default. */
|
|
383
552
|
sync?: SyncConfig;
|
|
553
|
+
/** Storage durability configuration. */
|
|
554
|
+
durability?: DurabilityConfig;
|
|
384
555
|
}
|
|
385
556
|
|
|
386
557
|
interface HttpSyncAdapterOptions {
|
|
@@ -430,11 +601,70 @@ declare class TalaDbValidationError extends Error {
|
|
|
430
601
|
readonly cause: unknown;
|
|
431
602
|
constructor(cause: unknown, context?: string);
|
|
432
603
|
}
|
|
604
|
+
/**
|
|
605
|
+
* Wraps a `Collection<T>` to intercept writes through a schema validator
|
|
606
|
+
* (`schema`), stamp `_v` on insert when a `syncSchema.version` is declared, and
|
|
607
|
+
* normalize reads through a lazy `migrateDocument`. Returns the collection
|
|
608
|
+
* unchanged when none of those apply.
|
|
609
|
+
*
|
|
610
|
+
* Read normalization covers `find`, `findOne`, and `subscribe` (live queries).
|
|
611
|
+
* It cannot cover `aggregate`, `findNearest`, or FTS `search`: those run inside
|
|
612
|
+
* the engine against the *stored* shape, so a below-version document is matched
|
|
613
|
+
* and projected as it sits on disk. Use `persistMigrations` or
|
|
614
|
+
* `openDB({ migrations })` to rewrite storage if you query old documents that way.
|
|
615
|
+
*
|
|
616
|
+
* @internal Exported for unit testing; not part of the public API surface.
|
|
617
|
+
*/
|
|
618
|
+
declare function applySchema<T extends Document>(col: Collection<T>, options: CollectionOptions<T>): Collection<T>;
|
|
433
619
|
|
|
620
|
+
/**
|
|
621
|
+
* A single application schema migration, run once at `openDB` when its
|
|
622
|
+
* `version` is greater than the database's stored migration version.
|
|
623
|
+
*/
|
|
624
|
+
interface Migration {
|
|
625
|
+
/** Monotonic version. Must be a positive integer, unique across the array. */
|
|
626
|
+
version: number;
|
|
627
|
+
/** Optional human-readable label for logs. */
|
|
628
|
+
description?: string;
|
|
629
|
+
/**
|
|
630
|
+
* The migration body. Receives the open database and may use the full
|
|
631
|
+
* collection API. Runs to completion before the version is advanced.
|
|
632
|
+
*
|
|
633
|
+
* **Write migrations idempotently.** TalaDB checkpoints per version (the
|
|
634
|
+
* stored version advances only after `up` fully resolves), but a single `up`
|
|
635
|
+
* is not wrapped in one atomic transaction — if it throws partway, the writes
|
|
636
|
+
* it already made persist and `up` re-runs from the start on the next open.
|
|
637
|
+
*/
|
|
638
|
+
up: (db: TalaDB) => Promise<void> | void;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Runtime-agnostic migration runner. Each binding supplies `getVersion` /
|
|
642
|
+
* `setVersion` (its own persisted counter); the loop is identical everywhere.
|
|
643
|
+
*
|
|
644
|
+
* Runs pending migrations (`version` > stored) in ascending order, advancing
|
|
645
|
+
* the stored version after each `up` resolves — checkpoint per version. If an
|
|
646
|
+
* `up` throws, the loop stops and the error propagates; the stored version
|
|
647
|
+
* reflects the last fully-applied migration, so the next open resumes there.
|
|
648
|
+
*
|
|
649
|
+
* @internal Exported for unit testing; not part of the public API surface.
|
|
650
|
+
*/
|
|
651
|
+
declare function runMigrations(db: TalaDB, getVersion: () => Promise<number>, setVersion: (v: number) => Promise<void>, migrations: Migration[]): Promise<void>;
|
|
434
652
|
/** Options for `openDB`. */
|
|
435
653
|
interface OpenDBOptions {
|
|
436
654
|
/** Encrypt native database values at rest. Never hard-code this value. */
|
|
437
655
|
passphrase?: string;
|
|
656
|
+
/**
|
|
657
|
+
* Ordered application schema migrations, run once each at open in ascending
|
|
658
|
+
* `version` order (only those newer than the stored migration version). The
|
|
659
|
+
* stored version advances after each migration succeeds — checkpoint per
|
|
660
|
+
* version, resuming from the last applied one on the next open.
|
|
661
|
+
*
|
|
662
|
+
* Supported on Node.js, the browser (via the OPFS worker), and React Native.
|
|
663
|
+
* On a binding whose native module predates 0.9.2 (no `userVersion` /
|
|
664
|
+
* `setUserVersion`), `openDB` throws rather than silently skipping the
|
|
665
|
+
* migrations — rebuild or update the native module.
|
|
666
|
+
*/
|
|
667
|
+
migrations?: Migration[];
|
|
438
668
|
/**
|
|
439
669
|
* Explicit path to a `taladb.config.yml` / `taladb.config.json` file.
|
|
440
670
|
* If omitted, TalaDB auto-discovers the file from `process.cwd()` on Node.js.
|
|
@@ -448,6 +678,14 @@ interface OpenDBOptions {
|
|
|
448
678
|
* Useful for passing config programmatically without a config file on disk.
|
|
449
679
|
*/
|
|
450
680
|
config?: TalaDbConfig;
|
|
681
|
+
/**
|
|
682
|
+
* Storage durability, e.g. `{ flush_every_write: false }` to batch commits
|
|
683
|
+
* for write throughput (call `db.flush()` to force a sync), or `{ flush_ms }`
|
|
684
|
+
* to tune the browser IndexedDB-fallback snapshot debounce. Merged into
|
|
685
|
+
* `config.durability`. Node + browser; on React Native pass it in the config
|
|
686
|
+
* JSON to `TalaDBModule.initialize`.
|
|
687
|
+
*/
|
|
688
|
+
durability?: DurabilityConfig;
|
|
451
689
|
}
|
|
452
690
|
/**
|
|
453
691
|
* Open a TalaDB database.
|
|
@@ -466,4 +704,4 @@ interface OpenDBOptions {
|
|
|
466
704
|
*/
|
|
467
705
|
declare function openDB(dbName?: string, options?: OpenDBOptions): Promise<TalaDB>;
|
|
468
706
|
|
|
469
|
-
export { type AggregatePipeline, type AggregateStage, type Collection, type CollectionIndexInfo, type CollectionOptions, type Document, type Filter, HttpSyncAdapter, type OpenDBOptions, type Schema, type SerializedChangeset, type SyncAdapter, type SyncConfig, type SyncDirection, type SyncOptions, type SyncResult, type TalaDB, type TalaDbConfig, TalaDbValidationError, type Update, type Value, type VectorIndexOptions, type VectorMetric, type VectorSearchResult, openDB };
|
|
707
|
+
export { type AggregatePipeline, type AggregateStage, type Collection, type CollectionIndexInfo, type CollectionOptions, type Document, type DurabilityConfig, type Filter, HttpSyncAdapter, type Migration, type OpenDBOptions, type Schema, type SerializedChangeset, type SyncAdapter, type SyncConfig, type SyncDirection, type SyncOptions, type SyncResult, type TalaDB, type TalaDbConfig, TalaDbValidationError, type Update, type Value, type VectorIndexOptions, type VectorMetric, type VectorSearchResult, applySchema, openDB, runMigrations };
|