@bitcobblers/wod-wiki-storage 0.0.0-stage → 0.35.2116
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/chunk-DAGT3P7M.mjs +405 -0
- package/dist/chunk-DAGT3P7M.mjs.map +1 -0
- package/dist/index.cjs +623 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +60 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.mjs +206 -0
- package/dist/index.mjs.map +1 -0
- package/dist/sql/index.cjs +1779 -0
- package/dist/sql/index.cjs.map +1 -0
- package/dist/sql/index.d.cts +220 -0
- package/dist/sql/index.d.ts +220 -0
- package/dist/sql/index.mjs +1409 -0
- package/dist/sql/index.mjs.map +1 -0
- package/dist/wire-C7ZtlBKe.d.cts +489 -0
- package/dist/wire-C7ZtlBKe.d.ts +489 -0
- package/package.json +50 -4
- package/README.md +0 -3
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { Client } from '@libsql/client';
|
|
2
|
+
import { a as StoreDef, R as RangeDTO, K as KeyDTO, e as DomainQueryResult, E as TxOp, I as IStorage, S as StoreName, b as IReadOnlyStore, c as StoreType, d as IReadWriteStore, D as DomainQuery, f as IStorageTransaction } from '../wire-C7ZtlBKe.cjs';
|
|
3
|
+
import '@bitcobblers/wod-wiki-core';
|
|
4
|
+
import '@bitcobblers/wod-wiki-lang';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* StorageError — the single error type every shared SQL layer throws.
|
|
8
|
+
* Replaces the API server's HTTP-specific error: `status` keeps the wire
|
|
9
|
+
* mapping for the HTTP surface, `code` carries a stable machine identifier
|
|
10
|
+
* (e.g. 'domain_projection_unavailable') for browser callers.
|
|
11
|
+
*/
|
|
12
|
+
declare class StorageError extends Error {
|
|
13
|
+
readonly status: number;
|
|
14
|
+
readonly code?: string | undefined;
|
|
15
|
+
constructor(status: number, message: string, code?: string | undefined);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Connection contracts + a generic libsql adapter. The adapter takes a
|
|
20
|
+
* *structural* client so the browser passes @libsql/client/web and the server
|
|
21
|
+
* passes @libsql/client (node) — this module never imports either, keeping
|
|
22
|
+
* the browser graph free of native drivers.
|
|
23
|
+
*
|
|
24
|
+
* All operations are serialized through one promise chain: the underlying
|
|
25
|
+
* client shares a single connection, so concurrent transactions/reads could
|
|
26
|
+
* otherwise interleave on it. Transactions run on a transaction-owned
|
|
27
|
+
* connection (libsql interactive transaction), so writes inside a transaction
|
|
28
|
+
* commit or roll back atomically and are not visible outside it.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
type SqlValue = string | number | null;
|
|
32
|
+
type SqlRow = Record<string, unknown>;
|
|
33
|
+
interface SqlConnection {
|
|
34
|
+
query(sql: string, params?: SqlValue[]): Promise<SqlRow[]>;
|
|
35
|
+
exec(sql: string, params?: SqlValue[]): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
interface Database extends SqlConnection {
|
|
38
|
+
readonly dialect: 'sqlite' | 'postgres';
|
|
39
|
+
transaction<T>(fn: (tx: SqlConnection) => Promise<T>): Promise<T>;
|
|
40
|
+
close(): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
/** Minimal surface used from a libsql client (node or web build) — the real
|
|
43
|
+
* @libsql/client types, type-only so the browser graph stays driver-free. */
|
|
44
|
+
type LibsqlClient = Pick<Client, 'execute' | 'transaction' | 'close'>;
|
|
45
|
+
/** Wrap a libsql client (node or web build) into the shared Database surface. */
|
|
46
|
+
declare function libsqlDatabase(client: LibsqlClient): Database;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Row codec for the SQL `data` column. Plain JSON.stringify destroys
|
|
50
|
+
* ArrayBuffer values ({}); attachment rows carry raw blobs, so encoding tags
|
|
51
|
+
* the declared binary field and decoding revives it.
|
|
52
|
+
*
|
|
53
|
+
* Collision safety: tagging applies ONLY to the declared binary field of the
|
|
54
|
+
* declared store (attachments.data) — arbitrary rows (meta, notes, …) are
|
|
55
|
+
* plain JSON in both directions, so authored {$binary:…}-shaped objects
|
|
56
|
+
* anywhere else round-trip byte-for-byte. Within attachments.data the value
|
|
57
|
+
* is typed `ArrayBuffer | string`; strings pass through, and only the exact
|
|
58
|
+
* two-key tag revives.
|
|
59
|
+
*/
|
|
60
|
+
/** Serialize a row; when `binaryField` is set and holds an ArrayBuffer, it is
|
|
61
|
+
* replaced by the lossless tag (shallow copy — the input row is untouched). */
|
|
62
|
+
declare function encodeRowJson(value: unknown, binaryField?: string): string;
|
|
63
|
+
/** Parse a row; when `binaryField` is set, a tagged value there revives as an
|
|
64
|
+
* ArrayBuffer. Every other value — including tag-shaped data in other fields
|
|
65
|
+
* or stores — is returned exactly as authored. */
|
|
66
|
+
declare function decodeRowJson<T = unknown>(text: string, binaryField?: string): T;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Schema-driven store operations: IDB-style stores persisted into SQL tables
|
|
70
|
+
* (one per store, extracted key/index columns + full JSON in `data`). The
|
|
71
|
+
* schema derives entirely from STORE_DEFS / COLUMN_TYPES; JSON payloads stay
|
|
72
|
+
* the source of truth. Shared by the API server and the browser
|
|
73
|
+
* BrowserTursoStorage/SqlStorage backends.
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
declare const SCHEMA_TABLE = "_wodwiki_schema";
|
|
77
|
+
declare function storeDefinition(name: string): StoreDef;
|
|
78
|
+
declare function getRow(conn: SqlConnection, def: StoreDef, key: KeyDTO): Promise<unknown | undefined>;
|
|
79
|
+
declare function listRows(conn: SqlConnection, def: StoreDef, range?: RangeDTO, count?: number, indexName?: string): Promise<unknown[]>;
|
|
80
|
+
declare function countRows(conn: SqlConnection, def: StoreDef, range?: RangeDTO, indexName?: string): Promise<number>;
|
|
81
|
+
declare function putRow(conn: SqlConnection, def: StoreDef, value: unknown): Promise<KeyDTO>;
|
|
82
|
+
declare function deleteRows(conn: SqlConnection, def: StoreDef, keyOrRange: KeyDTO | RangeDTO): Promise<void>;
|
|
83
|
+
declare function clearRows(conn: SqlConnection, def: StoreDef): Promise<void>;
|
|
84
|
+
/** Wipe all user stores; system stores (memberships) survive and the internal
|
|
85
|
+
* schema metadata table is not a store, so the version guard is untouched. */
|
|
86
|
+
declare function wipeRows(conn: SqlConnection): Promise<void>;
|
|
87
|
+
/** Internal schema metadata is not exposed or cleared by the store API. */
|
|
88
|
+
declare function initializeStore(db: Database): Promise<void>;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Domain read models + incremental projection refresh over the shared SQL
|
|
92
|
+
* store tables.
|
|
93
|
+
*
|
|
94
|
+
* Three derived tables, rebuilt from the per-store source tables (source of
|
|
95
|
+
* truth) when incompatible and refreshed per affected key inside the same
|
|
96
|
+
* transaction as every mutation:
|
|
97
|
+
* domain_note — note metadata selector + original payload
|
|
98
|
+
* query_block — one occurrence row per block_index row, parent resolved by
|
|
99
|
+
* exact noteId or sourcePath (ambiguous/orphan → NULL parent,
|
|
100
|
+
* which gates ALL domain reads with 409 until healed)
|
|
101
|
+
* segment_head — MAX(version) per segment id taken BEFORE the isHistory
|
|
102
|
+
* filter, so a superseded revision can never resurrect
|
|
103
|
+
*
|
|
104
|
+
* Rebuilds are guarded: a `_wodwiki_schema` row stamps the projection
|
|
105
|
+
* version, so repeated ready()/boot cycles skip the rebuild while the
|
|
106
|
+
* derived shape is already compatible.
|
|
107
|
+
*
|
|
108
|
+
* Query plans are fixed and fully validated: every value is a bound parameter,
|
|
109
|
+
* names come from this module, counts run before paging, and text matching is
|
|
110
|
+
* a literal substring over JS-lowercased materialized columns (Unicode-exact;
|
|
111
|
+
* instr/strpos never treat % or _ as wildcards).
|
|
112
|
+
*
|
|
113
|
+
* `journal` is materialized in JS (sourceMatches from the wql package — the
|
|
114
|
+
* exact predicate the app applies) so no SQL regex re-implementation exists.
|
|
115
|
+
*/
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Applies ops in order, collecting projection impact per affected key, then
|
|
119
|
+
* refreshes derived rows — all on the caller's transaction. Returns one entry
|
|
120
|
+
* per op: the put key, or null for delete/clear. `opts.deferProjection`
|
|
121
|
+
* skips the per-key refresh pass; the caller MUST rebuild derived state in
|
|
122
|
+
* the same transaction afterwards (see rebuildProjections) or reads will
|
|
123
|
+
* operate on stale projections behind a valid guard.
|
|
124
|
+
*/
|
|
125
|
+
declare function refreshDomain(conn: SqlConnection, ops: TxOp[], opts?: {
|
|
126
|
+
deferProjection?: boolean;
|
|
127
|
+
}): Promise<(KeyDTO | null)[]>;
|
|
128
|
+
/**
|
|
129
|
+
* Rebuild the derived tables from the source-of-truth store rows and stamp
|
|
130
|
+
* the projection guard. Caller owns the transaction (atomicity) — the guard
|
|
131
|
+
* row commits together with the rebuilt rows.
|
|
132
|
+
*/
|
|
133
|
+
declare function rebuildProjections(conn: SqlConnection): Promise<void>;
|
|
134
|
+
/** Boot-time derived state: DDL + rebuild only when the guard is incompatible
|
|
135
|
+
* (first run, projection version bump, or an interrupted prior rebuild). */
|
|
136
|
+
declare function initializeDomain(db: Database): Promise<void>;
|
|
137
|
+
/** Clears derived tables after a data wipe (empty is a consistent state). */
|
|
138
|
+
declare function wipeDomain(conn: SqlConnection): Promise<void>;
|
|
139
|
+
declare const MAX_BODY: number;
|
|
140
|
+
/** Validate, gate, execute on one consistent snapshot (server route and
|
|
141
|
+
* SqlStorage.queryDomain share this entry). */
|
|
142
|
+
declare function handleDomainQuery(db: Database, body: unknown): Promise<DomainQueryResult>;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* SqlStorage — IStorage over the shared SQL store operations (server or
|
|
146
|
+
* browser). Transactions buffer writes and flush them as one refreshDomain
|
|
147
|
+
* pass inside a real SQL transaction: throw → rollback, success → commit,
|
|
148
|
+
* and buffered reads overlay committed rows (read-your-writes). Projections
|
|
149
|
+
* refresh inside the same transaction; bulkTransaction() batches a migration
|
|
150
|
+
* into one rebuild instead of per-key refreshes. verifyWritable() proves the
|
|
151
|
+
* backend accepts writes with a real INSERT that is rolled back.
|
|
152
|
+
*
|
|
153
|
+
* BrowserTursoStorage opens a direct-to-Turso backend via @libsql/client/web;
|
|
154
|
+
* the constructor only builds the client (HTTP is connectionless), `ready()`
|
|
155
|
+
* initializes schema + projections and every operation awaits it, so a
|
|
156
|
+
* transient init failure never wedges the backend — ready() retries.
|
|
157
|
+
*/
|
|
158
|
+
|
|
159
|
+
interface SqlStorageOptions {
|
|
160
|
+
/** Rows in user-owned stores are stamped with this userId on write. Omit
|
|
161
|
+
* for stamp-free writers (data migration preserves ownership verbatim). */
|
|
162
|
+
getUserId?: () => string;
|
|
163
|
+
}
|
|
164
|
+
declare class SqlStorage implements IStorage {
|
|
165
|
+
private readonly db;
|
|
166
|
+
private readonly options;
|
|
167
|
+
private readonly defs;
|
|
168
|
+
private readyPromise?;
|
|
169
|
+
constructor(db: Database, options?: SqlStorageOptions);
|
|
170
|
+
/**
|
|
171
|
+
* Idempotent schema + projection initialization. Cheap when already
|
|
172
|
+
* compatible (both initializers are guard-protected); a failed attempt
|
|
173
|
+
* clears the memo so the next call retries instead of caching the error.
|
|
174
|
+
*/
|
|
175
|
+
ready(): Promise<void>;
|
|
176
|
+
/** Every store operation starts here, so views created before (or during)
|
|
177
|
+
* an in-flight ready() block until initialization actually succeeded. */
|
|
178
|
+
ensureReady(): Promise<void>;
|
|
179
|
+
def(store: StoreName): StoreDef;
|
|
180
|
+
private makeView;
|
|
181
|
+
readonly<K extends StoreName>(store: K): IReadOnlyStore<StoreType<K>>;
|
|
182
|
+
readwrite<K extends StoreName>(store: K): IReadWriteStore<StoreType<K>>;
|
|
183
|
+
queryDomain(query: DomainQuery): Promise<DomainQueryResult | undefined>;
|
|
184
|
+
transaction<K extends StoreName, R>(stores: K[], mode: 'readonly' | 'readwrite', fn: (tx: IStorageTransaction) => Promise<R>): Promise<R>;
|
|
185
|
+
/**
|
|
186
|
+
* Bulk load for migration destinations: like transaction(), but the
|
|
187
|
+
* buffered writes skip per-key projection refresh and derived state is
|
|
188
|
+
* rebuilt once — everything inside one SQL transaction (throw → rollback).
|
|
189
|
+
*/
|
|
190
|
+
bulkTransaction<K extends StoreName, R>(stores: K[], fn: (tx: IStorageTransaction) => Promise<R>): Promise<R>;
|
|
191
|
+
private runTx;
|
|
192
|
+
/**
|
|
193
|
+
* Proves the backend accepts writes: one real INSERT into `meta` inside a
|
|
194
|
+
* transaction that is then deliberately rolled back — zero rows committed.
|
|
195
|
+
* A read-only backend, bad token or transport failure rejects here.
|
|
196
|
+
*/
|
|
197
|
+
verifyWritable(): Promise<void>;
|
|
198
|
+
wipe(): Promise<void>;
|
|
199
|
+
close(): Promise<void>;
|
|
200
|
+
applyOps(ops: TxOp[]): Promise<void>;
|
|
201
|
+
stamp(store: StoreName, value: unknown): unknown;
|
|
202
|
+
}
|
|
203
|
+
interface TursoSettings {
|
|
204
|
+
url: string;
|
|
205
|
+
authToken?: string;
|
|
206
|
+
}
|
|
207
|
+
/** libsql/https/wss only; plain http only for loopback development; tokens
|
|
208
|
+
* travel via authToken, never the URL. Rejects credentials, query strings
|
|
209
|
+
* and fragments (no secret or option injection through the URL). */
|
|
210
|
+
declare function validateTursoUrl(url: string): string;
|
|
211
|
+
/**
|
|
212
|
+
* Direct-from-browser Turso backend. Construction only validates and builds
|
|
213
|
+
* the client; `await storage.ready()` initializes schema + projections.
|
|
214
|
+
* Errors never embed the auth token.
|
|
215
|
+
*/
|
|
216
|
+
declare class BrowserTursoStorage extends SqlStorage {
|
|
217
|
+
constructor(settings: TursoSettings, options?: SqlStorageOptions);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export { BrowserTursoStorage, type Database, type LibsqlClient, MAX_BODY, SCHEMA_TABLE, type SqlConnection, type SqlRow, SqlStorage, type SqlStorageOptions, type SqlValue, StorageError, type TursoSettings, clearRows, countRows, decodeRowJson, deleteRows, encodeRowJson, getRow, handleDomainQuery, initializeDomain, initializeStore, libsqlDatabase, listRows, putRow, rebuildProjections, refreshDomain, storeDefinition, validateTursoUrl, wipeDomain, wipeRows };
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { Client } from '@libsql/client';
|
|
2
|
+
import { a as StoreDef, R as RangeDTO, K as KeyDTO, e as DomainQueryResult, E as TxOp, I as IStorage, S as StoreName, b as IReadOnlyStore, c as StoreType, d as IReadWriteStore, D as DomainQuery, f as IStorageTransaction } from '../wire-C7ZtlBKe.js';
|
|
3
|
+
import '@bitcobblers/wod-wiki-core';
|
|
4
|
+
import '@bitcobblers/wod-wiki-lang';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* StorageError — the single error type every shared SQL layer throws.
|
|
8
|
+
* Replaces the API server's HTTP-specific error: `status` keeps the wire
|
|
9
|
+
* mapping for the HTTP surface, `code` carries a stable machine identifier
|
|
10
|
+
* (e.g. 'domain_projection_unavailable') for browser callers.
|
|
11
|
+
*/
|
|
12
|
+
declare class StorageError extends Error {
|
|
13
|
+
readonly status: number;
|
|
14
|
+
readonly code?: string | undefined;
|
|
15
|
+
constructor(status: number, message: string, code?: string | undefined);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Connection contracts + a generic libsql adapter. The adapter takes a
|
|
20
|
+
* *structural* client so the browser passes @libsql/client/web and the server
|
|
21
|
+
* passes @libsql/client (node) — this module never imports either, keeping
|
|
22
|
+
* the browser graph free of native drivers.
|
|
23
|
+
*
|
|
24
|
+
* All operations are serialized through one promise chain: the underlying
|
|
25
|
+
* client shares a single connection, so concurrent transactions/reads could
|
|
26
|
+
* otherwise interleave on it. Transactions run on a transaction-owned
|
|
27
|
+
* connection (libsql interactive transaction), so writes inside a transaction
|
|
28
|
+
* commit or roll back atomically and are not visible outside it.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
type SqlValue = string | number | null;
|
|
32
|
+
type SqlRow = Record<string, unknown>;
|
|
33
|
+
interface SqlConnection {
|
|
34
|
+
query(sql: string, params?: SqlValue[]): Promise<SqlRow[]>;
|
|
35
|
+
exec(sql: string, params?: SqlValue[]): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
interface Database extends SqlConnection {
|
|
38
|
+
readonly dialect: 'sqlite' | 'postgres';
|
|
39
|
+
transaction<T>(fn: (tx: SqlConnection) => Promise<T>): Promise<T>;
|
|
40
|
+
close(): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
/** Minimal surface used from a libsql client (node or web build) — the real
|
|
43
|
+
* @libsql/client types, type-only so the browser graph stays driver-free. */
|
|
44
|
+
type LibsqlClient = Pick<Client, 'execute' | 'transaction' | 'close'>;
|
|
45
|
+
/** Wrap a libsql client (node or web build) into the shared Database surface. */
|
|
46
|
+
declare function libsqlDatabase(client: LibsqlClient): Database;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Row codec for the SQL `data` column. Plain JSON.stringify destroys
|
|
50
|
+
* ArrayBuffer values ({}); attachment rows carry raw blobs, so encoding tags
|
|
51
|
+
* the declared binary field and decoding revives it.
|
|
52
|
+
*
|
|
53
|
+
* Collision safety: tagging applies ONLY to the declared binary field of the
|
|
54
|
+
* declared store (attachments.data) — arbitrary rows (meta, notes, …) are
|
|
55
|
+
* plain JSON in both directions, so authored {$binary:…}-shaped objects
|
|
56
|
+
* anywhere else round-trip byte-for-byte. Within attachments.data the value
|
|
57
|
+
* is typed `ArrayBuffer | string`; strings pass through, and only the exact
|
|
58
|
+
* two-key tag revives.
|
|
59
|
+
*/
|
|
60
|
+
/** Serialize a row; when `binaryField` is set and holds an ArrayBuffer, it is
|
|
61
|
+
* replaced by the lossless tag (shallow copy — the input row is untouched). */
|
|
62
|
+
declare function encodeRowJson(value: unknown, binaryField?: string): string;
|
|
63
|
+
/** Parse a row; when `binaryField` is set, a tagged value there revives as an
|
|
64
|
+
* ArrayBuffer. Every other value — including tag-shaped data in other fields
|
|
65
|
+
* or stores — is returned exactly as authored. */
|
|
66
|
+
declare function decodeRowJson<T = unknown>(text: string, binaryField?: string): T;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Schema-driven store operations: IDB-style stores persisted into SQL tables
|
|
70
|
+
* (one per store, extracted key/index columns + full JSON in `data`). The
|
|
71
|
+
* schema derives entirely from STORE_DEFS / COLUMN_TYPES; JSON payloads stay
|
|
72
|
+
* the source of truth. Shared by the API server and the browser
|
|
73
|
+
* BrowserTursoStorage/SqlStorage backends.
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
declare const SCHEMA_TABLE = "_wodwiki_schema";
|
|
77
|
+
declare function storeDefinition(name: string): StoreDef;
|
|
78
|
+
declare function getRow(conn: SqlConnection, def: StoreDef, key: KeyDTO): Promise<unknown | undefined>;
|
|
79
|
+
declare function listRows(conn: SqlConnection, def: StoreDef, range?: RangeDTO, count?: number, indexName?: string): Promise<unknown[]>;
|
|
80
|
+
declare function countRows(conn: SqlConnection, def: StoreDef, range?: RangeDTO, indexName?: string): Promise<number>;
|
|
81
|
+
declare function putRow(conn: SqlConnection, def: StoreDef, value: unknown): Promise<KeyDTO>;
|
|
82
|
+
declare function deleteRows(conn: SqlConnection, def: StoreDef, keyOrRange: KeyDTO | RangeDTO): Promise<void>;
|
|
83
|
+
declare function clearRows(conn: SqlConnection, def: StoreDef): Promise<void>;
|
|
84
|
+
/** Wipe all user stores; system stores (memberships) survive and the internal
|
|
85
|
+
* schema metadata table is not a store, so the version guard is untouched. */
|
|
86
|
+
declare function wipeRows(conn: SqlConnection): Promise<void>;
|
|
87
|
+
/** Internal schema metadata is not exposed or cleared by the store API. */
|
|
88
|
+
declare function initializeStore(db: Database): Promise<void>;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Domain read models + incremental projection refresh over the shared SQL
|
|
92
|
+
* store tables.
|
|
93
|
+
*
|
|
94
|
+
* Three derived tables, rebuilt from the per-store source tables (source of
|
|
95
|
+
* truth) when incompatible and refreshed per affected key inside the same
|
|
96
|
+
* transaction as every mutation:
|
|
97
|
+
* domain_note — note metadata selector + original payload
|
|
98
|
+
* query_block — one occurrence row per block_index row, parent resolved by
|
|
99
|
+
* exact noteId or sourcePath (ambiguous/orphan → NULL parent,
|
|
100
|
+
* which gates ALL domain reads with 409 until healed)
|
|
101
|
+
* segment_head — MAX(version) per segment id taken BEFORE the isHistory
|
|
102
|
+
* filter, so a superseded revision can never resurrect
|
|
103
|
+
*
|
|
104
|
+
* Rebuilds are guarded: a `_wodwiki_schema` row stamps the projection
|
|
105
|
+
* version, so repeated ready()/boot cycles skip the rebuild while the
|
|
106
|
+
* derived shape is already compatible.
|
|
107
|
+
*
|
|
108
|
+
* Query plans are fixed and fully validated: every value is a bound parameter,
|
|
109
|
+
* names come from this module, counts run before paging, and text matching is
|
|
110
|
+
* a literal substring over JS-lowercased materialized columns (Unicode-exact;
|
|
111
|
+
* instr/strpos never treat % or _ as wildcards).
|
|
112
|
+
*
|
|
113
|
+
* `journal` is materialized in JS (sourceMatches from the wql package — the
|
|
114
|
+
* exact predicate the app applies) so no SQL regex re-implementation exists.
|
|
115
|
+
*/
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Applies ops in order, collecting projection impact per affected key, then
|
|
119
|
+
* refreshes derived rows — all on the caller's transaction. Returns one entry
|
|
120
|
+
* per op: the put key, or null for delete/clear. `opts.deferProjection`
|
|
121
|
+
* skips the per-key refresh pass; the caller MUST rebuild derived state in
|
|
122
|
+
* the same transaction afterwards (see rebuildProjections) or reads will
|
|
123
|
+
* operate on stale projections behind a valid guard.
|
|
124
|
+
*/
|
|
125
|
+
declare function refreshDomain(conn: SqlConnection, ops: TxOp[], opts?: {
|
|
126
|
+
deferProjection?: boolean;
|
|
127
|
+
}): Promise<(KeyDTO | null)[]>;
|
|
128
|
+
/**
|
|
129
|
+
* Rebuild the derived tables from the source-of-truth store rows and stamp
|
|
130
|
+
* the projection guard. Caller owns the transaction (atomicity) — the guard
|
|
131
|
+
* row commits together with the rebuilt rows.
|
|
132
|
+
*/
|
|
133
|
+
declare function rebuildProjections(conn: SqlConnection): Promise<void>;
|
|
134
|
+
/** Boot-time derived state: DDL + rebuild only when the guard is incompatible
|
|
135
|
+
* (first run, projection version bump, or an interrupted prior rebuild). */
|
|
136
|
+
declare function initializeDomain(db: Database): Promise<void>;
|
|
137
|
+
/** Clears derived tables after a data wipe (empty is a consistent state). */
|
|
138
|
+
declare function wipeDomain(conn: SqlConnection): Promise<void>;
|
|
139
|
+
declare const MAX_BODY: number;
|
|
140
|
+
/** Validate, gate, execute on one consistent snapshot (server route and
|
|
141
|
+
* SqlStorage.queryDomain share this entry). */
|
|
142
|
+
declare function handleDomainQuery(db: Database, body: unknown): Promise<DomainQueryResult>;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* SqlStorage — IStorage over the shared SQL store operations (server or
|
|
146
|
+
* browser). Transactions buffer writes and flush them as one refreshDomain
|
|
147
|
+
* pass inside a real SQL transaction: throw → rollback, success → commit,
|
|
148
|
+
* and buffered reads overlay committed rows (read-your-writes). Projections
|
|
149
|
+
* refresh inside the same transaction; bulkTransaction() batches a migration
|
|
150
|
+
* into one rebuild instead of per-key refreshes. verifyWritable() proves the
|
|
151
|
+
* backend accepts writes with a real INSERT that is rolled back.
|
|
152
|
+
*
|
|
153
|
+
* BrowserTursoStorage opens a direct-to-Turso backend via @libsql/client/web;
|
|
154
|
+
* the constructor only builds the client (HTTP is connectionless), `ready()`
|
|
155
|
+
* initializes schema + projections and every operation awaits it, so a
|
|
156
|
+
* transient init failure never wedges the backend — ready() retries.
|
|
157
|
+
*/
|
|
158
|
+
|
|
159
|
+
interface SqlStorageOptions {
|
|
160
|
+
/** Rows in user-owned stores are stamped with this userId on write. Omit
|
|
161
|
+
* for stamp-free writers (data migration preserves ownership verbatim). */
|
|
162
|
+
getUserId?: () => string;
|
|
163
|
+
}
|
|
164
|
+
declare class SqlStorage implements IStorage {
|
|
165
|
+
private readonly db;
|
|
166
|
+
private readonly options;
|
|
167
|
+
private readonly defs;
|
|
168
|
+
private readyPromise?;
|
|
169
|
+
constructor(db: Database, options?: SqlStorageOptions);
|
|
170
|
+
/**
|
|
171
|
+
* Idempotent schema + projection initialization. Cheap when already
|
|
172
|
+
* compatible (both initializers are guard-protected); a failed attempt
|
|
173
|
+
* clears the memo so the next call retries instead of caching the error.
|
|
174
|
+
*/
|
|
175
|
+
ready(): Promise<void>;
|
|
176
|
+
/** Every store operation starts here, so views created before (or during)
|
|
177
|
+
* an in-flight ready() block until initialization actually succeeded. */
|
|
178
|
+
ensureReady(): Promise<void>;
|
|
179
|
+
def(store: StoreName): StoreDef;
|
|
180
|
+
private makeView;
|
|
181
|
+
readonly<K extends StoreName>(store: K): IReadOnlyStore<StoreType<K>>;
|
|
182
|
+
readwrite<K extends StoreName>(store: K): IReadWriteStore<StoreType<K>>;
|
|
183
|
+
queryDomain(query: DomainQuery): Promise<DomainQueryResult | undefined>;
|
|
184
|
+
transaction<K extends StoreName, R>(stores: K[], mode: 'readonly' | 'readwrite', fn: (tx: IStorageTransaction) => Promise<R>): Promise<R>;
|
|
185
|
+
/**
|
|
186
|
+
* Bulk load for migration destinations: like transaction(), but the
|
|
187
|
+
* buffered writes skip per-key projection refresh and derived state is
|
|
188
|
+
* rebuilt once — everything inside one SQL transaction (throw → rollback).
|
|
189
|
+
*/
|
|
190
|
+
bulkTransaction<K extends StoreName, R>(stores: K[], fn: (tx: IStorageTransaction) => Promise<R>): Promise<R>;
|
|
191
|
+
private runTx;
|
|
192
|
+
/**
|
|
193
|
+
* Proves the backend accepts writes: one real INSERT into `meta` inside a
|
|
194
|
+
* transaction that is then deliberately rolled back — zero rows committed.
|
|
195
|
+
* A read-only backend, bad token or transport failure rejects here.
|
|
196
|
+
*/
|
|
197
|
+
verifyWritable(): Promise<void>;
|
|
198
|
+
wipe(): Promise<void>;
|
|
199
|
+
close(): Promise<void>;
|
|
200
|
+
applyOps(ops: TxOp[]): Promise<void>;
|
|
201
|
+
stamp(store: StoreName, value: unknown): unknown;
|
|
202
|
+
}
|
|
203
|
+
interface TursoSettings {
|
|
204
|
+
url: string;
|
|
205
|
+
authToken?: string;
|
|
206
|
+
}
|
|
207
|
+
/** libsql/https/wss only; plain http only for loopback development; tokens
|
|
208
|
+
* travel via authToken, never the URL. Rejects credentials, query strings
|
|
209
|
+
* and fragments (no secret or option injection through the URL). */
|
|
210
|
+
declare function validateTursoUrl(url: string): string;
|
|
211
|
+
/**
|
|
212
|
+
* Direct-from-browser Turso backend. Construction only validates and builds
|
|
213
|
+
* the client; `await storage.ready()` initializes schema + projections.
|
|
214
|
+
* Errors never embed the auth token.
|
|
215
|
+
*/
|
|
216
|
+
declare class BrowserTursoStorage extends SqlStorage {
|
|
217
|
+
constructor(settings: TursoSettings, options?: SqlStorageOptions);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export { BrowserTursoStorage, type Database, type LibsqlClient, MAX_BODY, SCHEMA_TABLE, type SqlConnection, type SqlRow, SqlStorage, type SqlStorageOptions, type SqlValue, StorageError, type TursoSettings, clearRows, countRows, decodeRowJson, deleteRows, encodeRowJson, getRow, handleDomainQuery, initializeDomain, initializeStore, libsqlDatabase, listRows, putRow, rebuildProjections, refreshDomain, storeDefinition, validateTursoUrl, wipeDomain, wipeRows };
|