@jarenjs/db 0.73.0 → 0.83.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.
- package/ARCHITECTURE.md +70 -7
- package/README.md +69 -6
- package/docs/HOSTS.md +17 -0
- package/docs/JOBS-FORMAT.md +26 -0
- package/docs/LIVE-FORMAT.md +52 -13
- package/docs/MIGRATION-FORMAT.md +34 -0
- package/docs/MODEL-FORMAT.md +163 -15
- package/docs/NATIVE-PLANS.md +111 -0
- package/docs/REPLICATION-FORMAT.md +19 -13
- package/docs/SEARCH.md +55 -0
- package/package.json +8 -4
- package/schemas/jaren-migration.draft-07.schema.json +54 -5
- package/schemas/jaren-migration.schema.json +49 -0
- package/schemas/jaren-model.authoring.schema.json +360 -0
- package/schemas/jaren-model.draft-07.schema.json +128 -0
- package/schemas/jaren-model.schema.json +128 -0
- package/src/algebra.js +26 -4
- package/src/backup.js +12 -7
- package/src/cursor.js +27 -4
- package/src/dag-job.js +2 -1
- package/src/ddl.js +13 -0
- package/src/derive.js +14 -3
- package/src/dialect.js +12 -0
- package/src/dialects/check-read.js +151 -0
- package/src/dialects/invariant-sql.js +117 -0
- package/src/dialects/postgres.js +28 -4
- package/src/dialects/sqlite.js +23 -3
- package/src/driver.js +1 -0
- package/src/drivers/bun.js +22 -4
- package/src/emit.js +133 -25
- package/src/entity.js +98 -41
- package/src/errors.js +8 -0
- package/src/graph.js +8 -1
- package/src/index.js +3 -0
- package/src/introspect.js +81 -12
- package/src/invariants.js +45 -0
- package/src/jobs.js +39 -6
- package/src/live-nested.js +27 -10
- package/src/live.js +51 -136
- package/src/migrate.js +136 -22
- package/src/model.js +12 -0
- package/src/mutation.js +165 -0
- package/src/physical.js +147 -0
- package/src/plan.js +275 -64
- package/src/query.js +175 -78
- package/src/search.js +144 -0
- package/src/sql.js +60 -0
- package/src/store.js +49 -13
- package/src/tracker.js +63 -39
- package/src/window.js +1 -0
- package/types/index.d.ts +59 -4
- package/types/search.d.ts +20 -0
- package/types/typed.d.ts +1 -0
package/src/window.js
CHANGED
|
@@ -100,6 +100,7 @@ export function createSortedWindow(terms, limit) {
|
|
|
100
100
|
return {
|
|
101
101
|
compare,
|
|
102
102
|
size: () => entries.length,
|
|
103
|
+
clear: () => { entries.length = 0; byToken.clear(); },
|
|
103
104
|
/** The visible slice: the first `limit` entries (all, unbounded). */
|
|
104
105
|
visible: () => (limit === null ? [...entries] : entries.slice(0, limit)),
|
|
105
106
|
/**
|
package/types/index.d.ts
CHANGED
|
@@ -291,6 +291,8 @@ export interface LoadContinuation {
|
|
|
291
291
|
* write may change; `'live'` (the default) reports the truth in
|
|
292
292
|
* `snapshot`. */
|
|
293
293
|
export interface PageOptions<C = LoadContinuation> extends EntityCursorOptions {
|
|
294
|
+
/** False admits no lookahead row; a full page reports hasMore: null. */
|
|
295
|
+
lookahead?: boolean;
|
|
294
296
|
limit?: number;
|
|
295
297
|
after?: C;
|
|
296
298
|
maxBytes?: number | null;
|
|
@@ -305,8 +307,11 @@ export interface PageOptions<C = LoadContinuation> extends EntityCursorOptions {
|
|
|
305
307
|
export interface Page<T, C = LoadContinuation> {
|
|
306
308
|
readonly items: T[];
|
|
307
309
|
readonly continuation: C | null;
|
|
308
|
-
readonly hasMore: boolean;
|
|
310
|
+
readonly hasMore: boolean | null;
|
|
309
311
|
readonly snapshot: boolean;
|
|
312
|
+
/** Present with lookahead:false; includes a row consumed at the byte boundary.
|
|
313
|
+
* Bytes count the serialized payloads consumed, excluding array punctuation. */
|
|
314
|
+
readonly work?: { readonly rows: number; readonly bytes: number };
|
|
310
315
|
}
|
|
311
316
|
|
|
312
317
|
export interface LoadExplanation {
|
|
@@ -594,12 +599,26 @@ export interface UntrackedReads<T = unknown> {
|
|
|
594
599
|
load(spec?: LoadSpec): Promise<T[]>;
|
|
595
600
|
}
|
|
596
601
|
|
|
602
|
+
/** Closed native mutation forms over declared SQLite column layouts. */
|
|
603
|
+
export type EntityMutation = {
|
|
604
|
+
returning?: readonly string[]; maxRows?: number; maxBytes?: number;
|
|
605
|
+
} & ({ op: 'update'; key: EntityKeyArg; expectedRevision?: number; set: Readonly<Record<string, unknown>> }
|
|
606
|
+
| { op: 'upsert'; values: Readonly<Record<string, unknown>>; conflict: readonly string[]; update: readonly string[] }
|
|
607
|
+
| { op: 'insert-select'; source: string; where?: unknown; select: Readonly<Record<string, string | { $literal: unknown }>>;
|
|
608
|
+
conflict: readonly string[]; onConflict: 'nothing' });
|
|
609
|
+
export interface MutationResult {
|
|
610
|
+
readonly mode: 'native'; readonly affected: number; readonly rows: ReadonlyArray<Readonly<Record<string, unknown>>>;
|
|
611
|
+
readonly admitted: { readonly statements: number; readonly rows: number; readonly bytes: number };
|
|
612
|
+
}
|
|
613
|
+
|
|
597
614
|
export interface EntitySet<T = unknown, I = unknown> {
|
|
598
615
|
/** The provider phantom: a chain over this set infers its item type. */
|
|
599
616
|
readonly __item?: T;
|
|
600
617
|
create(doc: I): Promise<Readonly<T>>;
|
|
601
618
|
get(key: EntityKeyArg): Promise<Readonly<T> | undefined>;
|
|
602
619
|
update(key: EntityKeyArg, changes: Partial<T>): Promise<Readonly<T>>;
|
|
620
|
+
/** One bounded native SQLite column mutation; unsupported shapes refuse JD0038. */
|
|
621
|
+
mutate(document: EntityMutation): Promise<MutationResult>;
|
|
603
622
|
delete(key: EntityKeyArg): Promise<boolean>;
|
|
604
623
|
load(spec?: LoadSpec): Promise<ReadonlyArray<Readonly<T>>>;
|
|
605
624
|
/** The graph cursor: one root graph per pull, its includes attached
|
|
@@ -695,7 +714,7 @@ export interface SyncStore {
|
|
|
695
714
|
* the consumer's words; the handle's writes take it and reads answer it. */
|
|
696
715
|
collection<T = unknown>(name: string): SyncCollection<T>;
|
|
697
716
|
entity(name: string): SyncEntitySet;
|
|
698
|
-
transaction<R>(fn: (store: TransactionStore) => R): R;
|
|
717
|
+
transaction<R>(fn: (store: TransactionStore) => R, options?: { mode?: 'deferred' | 'immediate' }): R;
|
|
699
718
|
execute?<R = unknown>(document: unknown, options?: ExecuteOptions): SequenceResult<R>;
|
|
700
719
|
explain?(document: unknown, options?: ExecuteOptions): unknown;
|
|
701
720
|
/** The entity roots this store-level provider serves (present with
|
|
@@ -813,7 +832,7 @@ export interface TransactionScopeOptions {
|
|
|
813
832
|
* upgrade `SQLITE_BUSY` the busy handler cannot retry — what a claim
|
|
814
833
|
* needs under concurrent writers; `'deferred'` (the default) is the
|
|
815
834
|
* savepoint as always. A nested `tx.transaction()` is a savepoint
|
|
816
|
-
* whichever mode the root chose; the synchronous twin
|
|
835
|
+
* whichever mode the root chose; the root synchronous twin accepts the same mode. */
|
|
817
836
|
mode?: 'deferred' | 'immediate';
|
|
818
837
|
}
|
|
819
838
|
|
|
@@ -845,6 +864,7 @@ export interface SyncSavepointController {
|
|
|
845
864
|
/** The synchronous surface a transaction view carries: the store's,
|
|
846
865
|
* plus the transaction-only savepoint group. */
|
|
847
866
|
export interface TransactionSyncStore extends SyncStore {
|
|
867
|
+
readonly sql: TrustedSyncSql;
|
|
848
868
|
readonly savepoints: SyncSavepointController;
|
|
849
869
|
}
|
|
850
870
|
|
|
@@ -871,6 +891,7 @@ export interface TransactionStore extends Omit<Store,
|
|
|
871
891
|
| 'jobs' | 'replication'> {
|
|
872
892
|
/** The transactional outbox (JOBS-FORMAT §3): no administration here —
|
|
873
893
|
* an admin operation is a root call. */
|
|
894
|
+
readonly sql: TrustedSql;
|
|
874
895
|
readonly jobs?: JobsApi;
|
|
875
896
|
transaction<R>(fn: (store: TransactionStore) => R | Promise<R>): Promise<Awaited<R>>;
|
|
876
897
|
/** Named partial rollback over the transaction's one savepoint stack
|
|
@@ -978,7 +999,7 @@ export interface LiveEventTime {
|
|
|
978
999
|
}
|
|
979
1000
|
|
|
980
1001
|
export interface LiveMode {
|
|
981
|
-
readonly strategy: 'rows' | 'window' | 'accumulator' | 'group'
|
|
1002
|
+
readonly strategy: 'rows' | 'window' | 'accumulator' | 'group' | 'distinct'
|
|
982
1003
|
| 'bucket' | 'rolling' | 'join' | 'graph' | 'nested-group' | 'rerun';
|
|
983
1004
|
readonly mode: 'incremental' | 'rerun';
|
|
984
1005
|
/** Present exactly when the strategy is 'rerun': the named reason. */
|
|
@@ -1051,6 +1072,8 @@ export interface LiveBounds {
|
|
|
1051
1072
|
}
|
|
1052
1073
|
|
|
1053
1074
|
export interface OpenStoreOptions {
|
|
1075
|
+
/** Verify existing objects and create no schema or infrastructure. */
|
|
1076
|
+
adopt?: boolean;
|
|
1054
1077
|
replication?: ReplicationOptions;
|
|
1055
1078
|
driver: Driver;
|
|
1056
1079
|
path?: string;
|
|
@@ -1775,6 +1798,8 @@ export interface JobWorker {
|
|
|
1775
1798
|
}
|
|
1776
1799
|
|
|
1777
1800
|
export interface JobWorkerOptions {
|
|
1801
|
+
/** Admit only when current durable effect policy returns true. */
|
|
1802
|
+
effectSafety?: (job: ClaimedJob, context: { lease(): JobLease; signal: AbortSignal }) => boolean | Promise<boolean>;
|
|
1778
1803
|
/**
|
|
1779
1804
|
* `checkpoints` is bound to THIS attempt and follows its current
|
|
1780
1805
|
* lease, so a renewal does not strand it. `signal` aborts for either
|
|
@@ -1784,6 +1809,9 @@ export interface JobWorkerOptions {
|
|
|
1784
1809
|
*/
|
|
1785
1810
|
handlers: Record<string, (payload: unknown, context: {
|
|
1786
1811
|
job: ClaimedJob;
|
|
1812
|
+
lease(): JobLease;
|
|
1813
|
+
/** Pause as cancelled, permitting explicit requeue for reconciliation. */
|
|
1814
|
+
pause(): Promise<boolean>;
|
|
1787
1815
|
checkpoints: { load(runId: string): unknown;
|
|
1788
1816
|
save(runId: string, nodeId: string, value: unknown): unknown;
|
|
1789
1817
|
complete(runId: string, result: unknown): unknown };
|
|
@@ -1824,6 +1852,8 @@ export interface JobsApi {
|
|
|
1824
1852
|
* expired), never a silent `false`.
|
|
1825
1853
|
*/
|
|
1826
1854
|
renew(lease: JobLease, options?: { leaseMs?: number }): Promise<JobLease>;
|
|
1855
|
+
/** Check the existing token/expiry fence without writing. */
|
|
1856
|
+
assertLease(lease: JobLease): Promise<boolean>;
|
|
1827
1857
|
/** Settle the attempt this lease holds. `true`, or one of the three
|
|
1828
1858
|
* coded refusals above — a caller that cannot tell "already done"
|
|
1829
1859
|
* from "you are stale" guesses, and guesses wrong. */
|
|
@@ -1899,6 +1929,7 @@ export interface JobsOptions {
|
|
|
1899
1929
|
}
|
|
1900
1930
|
|
|
1901
1931
|
export declare function createDagJobRunner(store: Store, options: {
|
|
1932
|
+
effectSafety?: JobWorkerOptions['effectSafety'];
|
|
1902
1933
|
compileDag: Function;
|
|
1903
1934
|
documents: Record<string, unknown>;
|
|
1904
1935
|
tasks?: Record<string, Function | { run: Function; version?: string; taskVersions?: Record<string, string> }>;
|
|
@@ -1982,3 +2013,27 @@ export interface ReplicationSnapshot {
|
|
|
1982
2013
|
receipts: ReplicationEnvelope[];
|
|
1983
2014
|
}
|
|
1984
2015
|
export declare function normalizeReplicationSnapshot(document: unknown): ReplicationSnapshot;
|
|
2016
|
+
|
|
2017
|
+
/** Trusted prepared SQL; statements belong to one transaction scope. */
|
|
2018
|
+
export interface TrustedSql {
|
|
2019
|
+
prepare(sql: string, options: { access: 'read' | 'write'; affects?: readonly string[] }): {
|
|
2020
|
+
run(params?: readonly unknown[]): unknown;
|
|
2021
|
+
get(params?: readonly unknown[]): Record<string, any> | undefined | Promise<Record<string, any> | undefined>;
|
|
2022
|
+
all(params?: readonly unknown[]): Record<string, any>[] | Promise<Record<string, any>[]>;
|
|
2023
|
+
close(): void;
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
export interface TrustedSyncSql {
|
|
2027
|
+
prepare(sql: string, options: { access: 'read' | 'write'; affects?: readonly string[] }): {
|
|
2028
|
+
run(params?: readonly unknown[]): unknown;
|
|
2029
|
+
get(params?: readonly unknown[]): Record<string, any> | undefined;
|
|
2030
|
+
all(params?: readonly unknown[]): Record<string, any>[];
|
|
2031
|
+
close(): void;
|
|
2032
|
+
};
|
|
2033
|
+
}
|
|
2034
|
+
export declare function planInvariants(model: unknown, options: { dialect: Dialect }): {
|
|
2035
|
+
type: 'trigger'; name: string; owner: string; rule: string; sql: string;
|
|
2036
|
+
}[];
|
|
2037
|
+
export declare function planPhysicalMigration(connection: unknown, fromModel: unknown, toModel: unknown,
|
|
2038
|
+
options: { id: string; steps: readonly unknown[]; dispositions: Readonly<Record<string, 'preserve' | 'replace' | 'drop'>>;
|
|
2039
|
+
assertions?: readonly { sql: string; params?: readonly unknown[]; expected: readonly unknown[] }[] }): unknown;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Store } from './index.js';
|
|
2
|
+
import type { LexicalDefinition } from '@jarenjs/core/search';
|
|
3
|
+
|
|
4
|
+
export interface SearchStorage {
|
|
5
|
+
load(id: string): Promise<string | null>;
|
|
6
|
+
save(id: string, payload: string): Promise<{ changes: number }>;
|
|
7
|
+
}
|
|
8
|
+
export declare function createDbSearchStorage(store: Store, collection: string, options?: { maxBytes?: number }): SearchStorage;
|
|
9
|
+
export interface DbSearch {
|
|
10
|
+
readonly sourceRevision: string;
|
|
11
|
+
refresh(): Promise<any>;
|
|
12
|
+
search(text: string, request?: Record<string, unknown>): Promise<any>;
|
|
13
|
+
row(id: string, revision: string): any;
|
|
14
|
+
subscribe(observer: (event: any) => void): () => void;
|
|
15
|
+
explain(): { mode: string; nativeFTS: boolean; reason: string; maxRows: number; maxBytes: number; capture: string; externalChanges: string };
|
|
16
|
+
stats(): any;
|
|
17
|
+
dispose(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export declare function createDbSearch(store: Store, entity: string, definition: LexicalDefinition,
|
|
20
|
+
options: { source: string; maxRows?: number; maxBytes?: number; storage?: SearchStorage; snapshotKey?: string }): Promise<DbSearch>;
|
package/types/typed.d.ts
CHANGED
|
@@ -101,6 +101,7 @@ export interface TypedUntrackedReads<E extends MetaMap<E>, M extends EntityMeta>
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
export interface TypedEntitySet<E extends MetaMap<E>, M extends EntityMeta> {
|
|
104
|
+
mutate(document: import('./index.js').EntityMutation): Promise<import('./index.js').MutationResult>;
|
|
104
105
|
/** The provider phantom: `from(typed.entity('User'))` infers `User`
|
|
105
106
|
* without a cast. */
|
|
106
107
|
readonly __item?: M['doc'];
|