@bitcobblers/wod-wiki-storage 0.35.2116 → 0.35.2117

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.
@@ -0,0 +1,207 @@
1
+ import { Note, BlockIndexRow, Tag, PageNote, Page, TagTypeRecord, NoteTag, BlockRevision, Session, Attachment, EventRecord, FieldCatalogEntry, FieldSourceRecord, FieldValueRecord, CatalogBackfillState, BlockEffort, Membership } from '@bitcobblers/wod-wiki-core';
2
+ import { IEffort } from '@bitcobblers/wod-wiki-lang';
3
+
4
+ declare const DOMAIN_PROJECTION_VERSION = 2;
5
+ type DomainPredicate = {
6
+ field: 'id' | 'noteId' | 'type' | 'catalog' | 'source' | 'tags' | 'effort';
7
+ values: string[];
8
+ negate?: boolean;
9
+ } | {
10
+ field: 'text';
11
+ value: string;
12
+ } | {
13
+ field: 'date';
14
+ start: number;
15
+ end: number;
16
+ endExclusive?: boolean;
17
+ } | {
18
+ field: 'page';
19
+ value: boolean;
20
+ } | {
21
+ field: 'defaultNotes';
22
+ feeds: boolean;
23
+ } | {
24
+ field: 'sourceFence';
25
+ };
26
+ type DomainOrder = {
27
+ field: 'id' | 'createdAt' | 'date' | 'title' | 'noteTitle' | 'dataType' | 'position';
28
+ direction: 'asc' | 'desc';
29
+ };
30
+ type DomainQuery = {
31
+ plan: 'notes' | 'blocks' | 'entries';
32
+ selection?: DomainPredicate[];
33
+ filters?: DomainPredicate[];
34
+ order?: DomainOrder[];
35
+ offset?: number;
36
+ limit?: number;
37
+ };
38
+ interface DomainEntry {
39
+ note: Note;
40
+ tags: Tag[];
41
+ links: PageNote[];
42
+ pages: Page[];
43
+ }
44
+ type DomainQueryResult = {
45
+ projectionVersion: typeof DOMAIN_PROJECTION_VERSION;
46
+ selectedCount: number;
47
+ matchedCount: number;
48
+ } & ({
49
+ plan: 'notes';
50
+ rows: Note[];
51
+ } | {
52
+ plan: 'blocks';
53
+ rows: BlockIndexRow[];
54
+ } | {
55
+ plan: 'entries';
56
+ rows: DomainEntry[];
57
+ });
58
+ declare function parseDomainQueryResult(value: unknown, plan: DomainQuery['plan']): DomainQueryResult;
59
+
60
+ /**
61
+ * IStorage — backend-neutral storage contract (moved verbatim from
62
+ * apps/playground/src/services/storage/IStorage.ts). Implemented by
63
+ * IndexedDBStorage / InMemoryStorage in the app and ApiStorage here.
64
+ */
65
+
66
+ interface StorageSchema {
67
+ notes: Note;
68
+ page: Page;
69
+ page_notes: PageNote;
70
+ tags: Tag;
71
+ tag_types: TagTypeRecord;
72
+ note_tags: NoteTag;
73
+ block_revisions: BlockRevision;
74
+ sessions: Session;
75
+ attachments: Attachment;
76
+ events: EventRecord;
77
+ field_catalog: FieldCatalogEntry;
78
+ field_sources: FieldSourceRecord;
79
+ field_values: FieldValueRecord;
80
+ field_catalog_meta: CatalogBackfillState;
81
+ efforts: IEffort;
82
+ block_index: BlockIndexRow;
83
+ block_efforts: BlockEffort;
84
+ meta: unknown;
85
+ memberships: Membership;
86
+ }
87
+ type StoreName = keyof StorageSchema;
88
+ type StoreType<K extends StoreName> = StorageSchema[K];
89
+ interface IReadOnlyStore<T> {
90
+ get(key: IDBValidKey): Promise<T | undefined>;
91
+ getMany(keys: IDBValidKey[]): Promise<(T | undefined)[]>;
92
+ getAll(query?: IDBValidKey | IDBKeyRange, count?: number): Promise<T[]>;
93
+ getAllFromIndex(indexName: string, query?: IDBValidKey | IDBKeyRange, count?: number): Promise<T[]>;
94
+ getAllFromIndexMany(indexName: string, keys: IDBValidKey[]): Promise<T[][]>;
95
+ count(query?: IDBValidKey | IDBKeyRange): Promise<number>;
96
+ countFromIndex(indexName: string, query?: IDBValidKey | IDBKeyRange): Promise<number>;
97
+ }
98
+ interface IReadWriteStore<T> extends IReadOnlyStore<T> {
99
+ put(value: T, key?: IDBValidKey): Promise<IDBValidKey>;
100
+ putMany(values: T[], keys?: IDBValidKey[]): Promise<IDBValidKey[]>;
101
+ delete(key: IDBValidKey | IDBKeyRange): Promise<void>;
102
+ clear(): Promise<void>;
103
+ }
104
+ interface IStorageTransaction {
105
+ readonly<K extends StoreName>(store: K): IReadOnlyStore<StoreType<K>>;
106
+ readwrite<K extends StoreName>(store: K): IReadWriteStore<StoreType<K>>;
107
+ }
108
+ interface IStorage {
109
+ queryDomain?(query: DomainQuery): Promise<DomainQueryResult | undefined>;
110
+ readonly<K extends StoreName>(store: K): IReadOnlyStore<StoreType<K>>;
111
+ readwrite<K extends StoreName>(store: K): IReadWriteStore<StoreType<K>>;
112
+ transaction<K extends StoreName, R>(stores: K[], mode: 'readonly' | 'readwrite', fn: (tx: IStorageTransaction) => Promise<R>): Promise<R>;
113
+ wipe(): Promise<void>;
114
+ close(): Promise<void>;
115
+ }
116
+
117
+ /** Fresh schema shared by browser, SQL and in-memory adapters. */
118
+
119
+ declare const DB_VERSION = 29;
120
+ /**
121
+ * Typed field metadata — column type per extracted field path, shared with
122
+ * the API so its SQL DDL derives DOUBLE PRECISION / INTEGER / BOOLEAN /
123
+ * TEXT from this table instead of a hardcoded per-index allowlist.
124
+ *
125
+ * Extraction rule (API side): a primary-key column `k<i>` takes the type of
126
+ * STORE_DEFS store's keyPath[i]; an index column `i_<index>_<part>` takes the
127
+ * type of that index's keyPath[part] here. Anything absent defaults to TEXT.
128
+ * Civil-date strings stay TEXT on purpose (page.date is 'YYYY-MM-DD');
129
+ * instants are epoch-ms numbers (notes.date, createdAt, timestamp).
130
+ */
131
+ type StorageColumnType = 'number' | 'text' | 'boolean';
132
+ declare const COLUMN_TYPES: Record<StoreName, Record<string, StorageColumnType>>;
133
+ interface IndexDef {
134
+ name: string;
135
+ keyPath: string[];
136
+ unique?: boolean;
137
+ multiEntry?: boolean;
138
+ }
139
+ interface StoreDef {
140
+ name: StoreName;
141
+ keyPath: string[];
142
+ indexes: IndexDef[];
143
+ /** System stores survive wipe() (identity must not be reset with data). */
144
+ system?: boolean;
145
+ }
146
+ declare const STORE_DEFS: StoreDef[];
147
+
148
+ /**
149
+ * Wire DTOs + key helpers shared by the storage client (ApiStorage) and the
150
+ * API server. Keys are scalars or scalar tuples (keyPath order); ranges bound
151
+ * them. Over HTTP, keys/ranges travel as urlencoded JSON query params.
152
+ *
153
+ * Range-typed helpers accept real IDBKeyRange instances AND plain RangeDTO
154
+ * literals — non-IDB runtimes (Bun, node, tests) have no IDBKeyRange global,
155
+ * so everything duck-types on field shape.
156
+ */
157
+
158
+ type Scalar = string | number;
159
+ /** Primary key: scalar for single-property keyPaths, tuple for compound. */
160
+ type KeyDTO = Scalar | Scalar[];
161
+ interface RangeDTO {
162
+ only?: KeyDTO;
163
+ /** Inclusive lower bound unless lowerOpen. Scalar or tuple (keyPath order). */
164
+ lower?: KeyDTO;
165
+ /** Inclusive upper bound unless upperOpen. Scalar or tuple (keyPath order). */
166
+ upper?: KeyDTO;
167
+ lowerOpen?: boolean;
168
+ upperOpen?: boolean;
169
+ }
170
+ type TxOp = {
171
+ op: 'check';
172
+ store: StoreName;
173
+ key: KeyDTO;
174
+ value: unknown | null;
175
+ } | {
176
+ op: 'put';
177
+ store: StoreName;
178
+ value: unknown;
179
+ } | {
180
+ op: 'delete';
181
+ store: StoreName;
182
+ key: KeyDTO;
183
+ } | {
184
+ op: 'clear';
185
+ store: StoreName;
186
+ };
187
+ /** Anything shaped like a range: real IDBKeyRange or a plain RangeDTO literal. */
188
+ declare function isRange(arg: unknown): arg is RangeDTO;
189
+ /** Reads one dotted property path ('a.b.c') off a row. */
190
+ declare function extractKeyPart(value: unknown, dottedPath: string): Scalar | undefined;
191
+ /** Extracts a row's primary key per the store's inline keyPath. */
192
+ declare function keyFromValue(def: StoreDef, value: unknown): KeyDTO;
193
+ /** Canonical url-safe string form of a key (also the buffer/tx match key). */
194
+ declare function encodeKey(key: KeyDTO): string;
195
+ /**
196
+ * Converts an IndexedDB query — bare key, IDBKeyRange, or an already-wire
197
+ * RangeDTO — to its wire form. Duck-types: objects with lower/upper/
198
+ * lowerOpen/upperOpen fields (real ranges or plain literals) pass through;
199
+ * scalars and tuple arrays become {only}.
200
+ */
201
+ declare function rangeFromIDB(query?: IDBValidKey | IDBKeyRange | RangeDTO): RangeDTO | undefined;
202
+ /** IndexedDB key ordering: numbers < strings < tuples; tuples lexicographic. */
203
+ declare function compareKeys(a: KeyDTO, b: KeyDTO): number;
204
+ /** True when a key matches a wire range (IDB bound semantics). */
205
+ declare function keyInRange(key: KeyDTO, range: RangeDTO): boolean;
206
+
207
+ export { COLUMN_TYPES as C, type DomainQuery as D, type IStorage as I, type KeyDTO as K, type RangeDTO as R, type StoreName as S, type TxOp as T, type StoreDef as a, type IReadOnlyStore as b, type StoreType as c, type IReadWriteStore as d, type DomainQueryResult as e, type IStorageTransaction as f, DB_VERSION as g, DOMAIN_PROJECTION_VERSION as h, type DomainEntry as i, type DomainOrder as j, type DomainPredicate as k, type IndexDef as l, STORE_DEFS as m, type Scalar as n, type StorageColumnType as o, type StorageSchema as p, compareKeys as q, encodeKey as r, extractKeyPart as s, isRange as t, keyFromValue as u, keyInRange as v, parseDomainQueryResult as w, rangeFromIDB as x };
@@ -0,0 +1,207 @@
1
+ import { Note, BlockIndexRow, Tag, PageNote, Page, TagTypeRecord, NoteTag, BlockRevision, Session, Attachment, EventRecord, FieldCatalogEntry, FieldSourceRecord, FieldValueRecord, CatalogBackfillState, BlockEffort, Membership } from '@bitcobblers/wod-wiki-core';
2
+ import { IEffort } from '@bitcobblers/wod-wiki-lang';
3
+
4
+ declare const DOMAIN_PROJECTION_VERSION = 2;
5
+ type DomainPredicate = {
6
+ field: 'id' | 'noteId' | 'type' | 'catalog' | 'source' | 'tags' | 'effort';
7
+ values: string[];
8
+ negate?: boolean;
9
+ } | {
10
+ field: 'text';
11
+ value: string;
12
+ } | {
13
+ field: 'date';
14
+ start: number;
15
+ end: number;
16
+ endExclusive?: boolean;
17
+ } | {
18
+ field: 'page';
19
+ value: boolean;
20
+ } | {
21
+ field: 'defaultNotes';
22
+ feeds: boolean;
23
+ } | {
24
+ field: 'sourceFence';
25
+ };
26
+ type DomainOrder = {
27
+ field: 'id' | 'createdAt' | 'date' | 'title' | 'noteTitle' | 'dataType' | 'position';
28
+ direction: 'asc' | 'desc';
29
+ };
30
+ type DomainQuery = {
31
+ plan: 'notes' | 'blocks' | 'entries';
32
+ selection?: DomainPredicate[];
33
+ filters?: DomainPredicate[];
34
+ order?: DomainOrder[];
35
+ offset?: number;
36
+ limit?: number;
37
+ };
38
+ interface DomainEntry {
39
+ note: Note;
40
+ tags: Tag[];
41
+ links: PageNote[];
42
+ pages: Page[];
43
+ }
44
+ type DomainQueryResult = {
45
+ projectionVersion: typeof DOMAIN_PROJECTION_VERSION;
46
+ selectedCount: number;
47
+ matchedCount: number;
48
+ } & ({
49
+ plan: 'notes';
50
+ rows: Note[];
51
+ } | {
52
+ plan: 'blocks';
53
+ rows: BlockIndexRow[];
54
+ } | {
55
+ plan: 'entries';
56
+ rows: DomainEntry[];
57
+ });
58
+ declare function parseDomainQueryResult(value: unknown, plan: DomainQuery['plan']): DomainQueryResult;
59
+
60
+ /**
61
+ * IStorage — backend-neutral storage contract (moved verbatim from
62
+ * apps/playground/src/services/storage/IStorage.ts). Implemented by
63
+ * IndexedDBStorage / InMemoryStorage in the app and ApiStorage here.
64
+ */
65
+
66
+ interface StorageSchema {
67
+ notes: Note;
68
+ page: Page;
69
+ page_notes: PageNote;
70
+ tags: Tag;
71
+ tag_types: TagTypeRecord;
72
+ note_tags: NoteTag;
73
+ block_revisions: BlockRevision;
74
+ sessions: Session;
75
+ attachments: Attachment;
76
+ events: EventRecord;
77
+ field_catalog: FieldCatalogEntry;
78
+ field_sources: FieldSourceRecord;
79
+ field_values: FieldValueRecord;
80
+ field_catalog_meta: CatalogBackfillState;
81
+ efforts: IEffort;
82
+ block_index: BlockIndexRow;
83
+ block_efforts: BlockEffort;
84
+ meta: unknown;
85
+ memberships: Membership;
86
+ }
87
+ type StoreName = keyof StorageSchema;
88
+ type StoreType<K extends StoreName> = StorageSchema[K];
89
+ interface IReadOnlyStore<T> {
90
+ get(key: IDBValidKey): Promise<T | undefined>;
91
+ getMany(keys: IDBValidKey[]): Promise<(T | undefined)[]>;
92
+ getAll(query?: IDBValidKey | IDBKeyRange, count?: number): Promise<T[]>;
93
+ getAllFromIndex(indexName: string, query?: IDBValidKey | IDBKeyRange, count?: number): Promise<T[]>;
94
+ getAllFromIndexMany(indexName: string, keys: IDBValidKey[]): Promise<T[][]>;
95
+ count(query?: IDBValidKey | IDBKeyRange): Promise<number>;
96
+ countFromIndex(indexName: string, query?: IDBValidKey | IDBKeyRange): Promise<number>;
97
+ }
98
+ interface IReadWriteStore<T> extends IReadOnlyStore<T> {
99
+ put(value: T, key?: IDBValidKey): Promise<IDBValidKey>;
100
+ putMany(values: T[], keys?: IDBValidKey[]): Promise<IDBValidKey[]>;
101
+ delete(key: IDBValidKey | IDBKeyRange): Promise<void>;
102
+ clear(): Promise<void>;
103
+ }
104
+ interface IStorageTransaction {
105
+ readonly<K extends StoreName>(store: K): IReadOnlyStore<StoreType<K>>;
106
+ readwrite<K extends StoreName>(store: K): IReadWriteStore<StoreType<K>>;
107
+ }
108
+ interface IStorage {
109
+ queryDomain?(query: DomainQuery): Promise<DomainQueryResult | undefined>;
110
+ readonly<K extends StoreName>(store: K): IReadOnlyStore<StoreType<K>>;
111
+ readwrite<K extends StoreName>(store: K): IReadWriteStore<StoreType<K>>;
112
+ transaction<K extends StoreName, R>(stores: K[], mode: 'readonly' | 'readwrite', fn: (tx: IStorageTransaction) => Promise<R>): Promise<R>;
113
+ wipe(): Promise<void>;
114
+ close(): Promise<void>;
115
+ }
116
+
117
+ /** Fresh schema shared by browser, SQL and in-memory adapters. */
118
+
119
+ declare const DB_VERSION = 29;
120
+ /**
121
+ * Typed field metadata — column type per extracted field path, shared with
122
+ * the API so its SQL DDL derives DOUBLE PRECISION / INTEGER / BOOLEAN /
123
+ * TEXT from this table instead of a hardcoded per-index allowlist.
124
+ *
125
+ * Extraction rule (API side): a primary-key column `k<i>` takes the type of
126
+ * STORE_DEFS store's keyPath[i]; an index column `i_<index>_<part>` takes the
127
+ * type of that index's keyPath[part] here. Anything absent defaults to TEXT.
128
+ * Civil-date strings stay TEXT on purpose (page.date is 'YYYY-MM-DD');
129
+ * instants are epoch-ms numbers (notes.date, createdAt, timestamp).
130
+ */
131
+ type StorageColumnType = 'number' | 'text' | 'boolean';
132
+ declare const COLUMN_TYPES: Record<StoreName, Record<string, StorageColumnType>>;
133
+ interface IndexDef {
134
+ name: string;
135
+ keyPath: string[];
136
+ unique?: boolean;
137
+ multiEntry?: boolean;
138
+ }
139
+ interface StoreDef {
140
+ name: StoreName;
141
+ keyPath: string[];
142
+ indexes: IndexDef[];
143
+ /** System stores survive wipe() (identity must not be reset with data). */
144
+ system?: boolean;
145
+ }
146
+ declare const STORE_DEFS: StoreDef[];
147
+
148
+ /**
149
+ * Wire DTOs + key helpers shared by the storage client (ApiStorage) and the
150
+ * API server. Keys are scalars or scalar tuples (keyPath order); ranges bound
151
+ * them. Over HTTP, keys/ranges travel as urlencoded JSON query params.
152
+ *
153
+ * Range-typed helpers accept real IDBKeyRange instances AND plain RangeDTO
154
+ * literals — non-IDB runtimes (Bun, node, tests) have no IDBKeyRange global,
155
+ * so everything duck-types on field shape.
156
+ */
157
+
158
+ type Scalar = string | number;
159
+ /** Primary key: scalar for single-property keyPaths, tuple for compound. */
160
+ type KeyDTO = Scalar | Scalar[];
161
+ interface RangeDTO {
162
+ only?: KeyDTO;
163
+ /** Inclusive lower bound unless lowerOpen. Scalar or tuple (keyPath order). */
164
+ lower?: KeyDTO;
165
+ /** Inclusive upper bound unless upperOpen. Scalar or tuple (keyPath order). */
166
+ upper?: KeyDTO;
167
+ lowerOpen?: boolean;
168
+ upperOpen?: boolean;
169
+ }
170
+ type TxOp = {
171
+ op: 'check';
172
+ store: StoreName;
173
+ key: KeyDTO;
174
+ value: unknown | null;
175
+ } | {
176
+ op: 'put';
177
+ store: StoreName;
178
+ value: unknown;
179
+ } | {
180
+ op: 'delete';
181
+ store: StoreName;
182
+ key: KeyDTO;
183
+ } | {
184
+ op: 'clear';
185
+ store: StoreName;
186
+ };
187
+ /** Anything shaped like a range: real IDBKeyRange or a plain RangeDTO literal. */
188
+ declare function isRange(arg: unknown): arg is RangeDTO;
189
+ /** Reads one dotted property path ('a.b.c') off a row. */
190
+ declare function extractKeyPart(value: unknown, dottedPath: string): Scalar | undefined;
191
+ /** Extracts a row's primary key per the store's inline keyPath. */
192
+ declare function keyFromValue(def: StoreDef, value: unknown): KeyDTO;
193
+ /** Canonical url-safe string form of a key (also the buffer/tx match key). */
194
+ declare function encodeKey(key: KeyDTO): string;
195
+ /**
196
+ * Converts an IndexedDB query — bare key, IDBKeyRange, or an already-wire
197
+ * RangeDTO — to its wire form. Duck-types: objects with lower/upper/
198
+ * lowerOpen/upperOpen fields (real ranges or plain literals) pass through;
199
+ * scalars and tuple arrays become {only}.
200
+ */
201
+ declare function rangeFromIDB(query?: IDBValidKey | IDBKeyRange | RangeDTO): RangeDTO | undefined;
202
+ /** IndexedDB key ordering: numbers < strings < tuples; tuples lexicographic. */
203
+ declare function compareKeys(a: KeyDTO, b: KeyDTO): number;
204
+ /** True when a key matches a wire range (IDB bound semantics). */
205
+ declare function keyInRange(key: KeyDTO, range: RangeDTO): boolean;
206
+
207
+ export { COLUMN_TYPES as C, type DomainQuery as D, type IStorage as I, type KeyDTO as K, type RangeDTO as R, type StoreName as S, type TxOp as T, type StoreDef as a, type IReadOnlyStore as b, type StoreType as c, type IReadWriteStore as d, type DomainQueryResult as e, type IStorageTransaction as f, DB_VERSION as g, DOMAIN_PROJECTION_VERSION as h, type DomainEntry as i, type DomainOrder as j, type DomainPredicate as k, type IndexDef as l, STORE_DEFS as m, type Scalar as n, type StorageColumnType as o, type StorageSchema as p, compareKeys as q, encodeKey as r, extractKeyPart as s, isRange as t, keyFromValue as u, keyInRange as v, parseDomainQueryResult as w, rangeFromIDB as x };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitcobblers/wod-wiki-storage",
3
- "version": "0.35.2116",
3
+ "version": "0.35.2117",
4
4
  "description": "Storage contract for WOD Wiki — entity types, IStorage, IndexedDB schema spec, wire DTOs, the HTTP ApiStorage backend, and the shared SQL (SqlStorage/BrowserTursoStorage) backend",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -30,21 +30,18 @@
30
30
  "typecheck": "tsc --noEmit"
31
31
  },
32
32
  "dependencies": {
33
- "@bitcobblers/wod-wiki-wql": "^0.35.2116",
33
+ "@bitcobblers/wod-wiki-wql": "^0.35.2117",
34
34
  "@libsql/client": "^0.18.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@bitcobblers/wod-wiki-core": "^0.35.2116",
38
- "@bitcobblers/wod-wiki-lang": "^0.35.2116"
37
+ "@bitcobblers/wod-wiki-core": "^0.35.2117",
38
+ "@bitcobblers/wod-wiki-lang": "^0.35.2117"
39
39
  },
40
40
  "peerDependencies": {
41
- "@bitcobblers/wod-wiki-core": "^0.35.2116",
42
- "@bitcobblers/wod-wiki-lang": "^0.35.2116"
41
+ "@bitcobblers/wod-wiki-core": "^0.35.2117",
42
+ "@bitcobblers/wod-wiki-lang": "^0.35.2117"
43
43
  },
44
44
  "peerDependenciesMeta": {
45
- "@bitcobblers/wod-wiki-core": {
46
- "optional": true
47
- },
48
45
  "@bitcobblers/wod-wiki-lang": {
49
46
  "optional": true
50
47
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/schema.ts","../src/membership.ts","../src/wire.ts","../src/domain.ts","../src/overlay.ts"],"names":[],"mappings":"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","file":"chunk-DAGT3P7M.mjs","sourcesContent":["/**\n * STORE_DEFS — runtime store/index spec for the wodwiki database, derived\n * verbatim from IndexedDBStorage.open()'s upgrade callback (DB_VERSION 25,\n * apps/playground/src/services/storage/IndexedDBStorage.ts). The API server\n * builds its SQL schema from this spec.\n *\n * V25 adds the memberships store (system) and the by-user ownership index\n * on every store that can hold user-created rows (see USER_OWNED_STORES in\n * membership.ts — the ownership crosswalk).\n *\n * keyPath convention: array of property paths in key order. A single-element\n * array is a simple (scalar) key; multiple elements form a compound key.\n * Dotted paths ('baseAttributes.discipline') address nested properties.\n */\n\nimport type { StoreName } from './contract';\n\n// DOMAIN_PROJECTION_VERSION lives in ./domain (re-exported by the package\n// index) — deliberately not re-exported here to avoid a star-export\n// ambiguity between ./schema and ./domain.\n\n/**\n * Shared IndexedDB schema version — the browser database opens at exactly\n * this version (apps/playground/src/services/storage/IndexedDBStorage.ts\n * imports it), so store/index drift between the app and this spec is a type\n * error, not a runtime surprise. Bump on every store/index change and add\n * the matching guarded upgrade step.\n *\n * 27 supersedes 26: real databases already upgraded to the first 26 cut\n * (before by-source / by-created / the unique note-tag pair) exist in the\n * wild, so \"version 26\" is ambiguous — 27's presence-guarded repairs bring\n * every mixed DB to the same shape.\n */\nexport const DB_VERSION = 28;\n\n/**\n * Typed field metadata — column type per extracted field path, shared with\n * the API so its SQL DDL derives DOUBLE PRECISION / INTEGER / BOOLEAN /\n * TEXT from this table instead of a hardcoded per-index allowlist.\n *\n * Extraction rule (API side): a primary-key column `k<i>` takes the type of\n * STORE_DEFS store's keyPath[i]; an index column `i_<index>_<part>` takes the\n * type of that index's keyPath[part] here. Anything absent defaults to TEXT.\n * Civil-date strings stay TEXT on purpose (page.date is 'YYYY-MM-DD');\n * instants are epoch-ms numbers (notes.date, createdAt, timestamp).\n */\nexport type StorageColumnType = 'number' | 'text' | 'boolean';\n\nexport const COLUMN_TYPES: Record<StoreName, Record<string, StorageColumnType>> = {\n notes: { date: 'number', createdAt: 'number' },\n page: { createdAt: 'number' }, // page.date is a civil 'YYYY-MM-DD' string → TEXT\n page_notes: { position: 'number', createdAt: 'number' },\n tags: { createdAt: 'number' },\n tag_types: { createdAt: 'number' },\n note_tags: {},\n segments: {\n version: 'number',\n position: 'number',\n createdAt: 'number',\n updatedAt: 'number',\n isHistory: 'boolean',\n },\n sessions: {\n segmentVersion: 'number',\n version: 'number',\n startTime: 'number',\n endTime: 'number',\n duration: 'number',\n roundsCompleted: 'number',\n totalRounds: 'number',\n repsCompleted: 'number',\n completed: 'boolean',\n createdAt: 'number',\n },\n attachments: { createdAt: 'number' },\n events: {\n timestamp: 'number',\n segmentVersion: 'number',\n line: 'number',\n sourceStatementId: 'number',\n stackLevel: 'number',\n 'timeSpan.started': 'number',\n 'timeSpan.ended': 'number',\n },\n field_catalog: {},\n field_sources: {},\n field_values: {},\n field_catalog_meta: {},\n efforts: { 'baseAttributes.met': 'number' },\n block_index: {\n segmentVersion: 'number',\n position: 'number',\n createdAt: 'number',\n isStatic: 'boolean',\n },\n block_efforts: { createdAt: 'number', isStatic: 'boolean' },\n meta: {},\n memberships: { createdAt: 'number' },\n};\n\nexport interface IndexDef {\n name: string;\n keyPath: string[];\n unique?: boolean;\n multiEntry?: boolean;\n}\n\nexport interface StoreDef {\n name: StoreName;\n keyPath: string[];\n indexes: IndexDef[];\n /** System stores survive wipe() (identity must not be reset with data). */\n system?: boolean;\n}\n\nexport const STORE_DEFS: StoreDef[] = [\n // 1. notes\n { name: 'notes', keyPath: ['id'], indexes: [\n { name: 'by-date', keyPath: ['date'] },\n { name: 'by-created', keyPath: ['createdAt'] },\n { name: 'by-user', keyPath: ['userId'] },\n ] },\n // 2. page\n { name: 'page', keyPath: ['id'], indexes: [\n { name: 'by-date', keyPath: ['date'], unique: true },\n { name: 'by-slug', keyPath: ['slug'], unique: true },\n { name: 'by-user', keyPath: ['userId'] },\n ] },\n // 2b. page_notes (V22)\n { name: 'page_notes', keyPath: ['id'], indexes: [\n { name: 'by-page', keyPath: ['pageId'] },\n { name: 'by-note', keyPath: ['noteId'] },\n { name: 'by-page-note', keyPath: ['pageId', 'noteId'], unique: true },\n { name: 'by-user', keyPath: ['userId'] },\n ] },\n // 3. tags\n { name: 'tags', keyPath: ['id'], indexes: [\n { name: 'by-label', keyPath: ['label'], unique: true },\n { name: 'by-type', keyPath: ['type'] },\n { name: 'by-user', keyPath: ['userId'] },\n ] },\n // 3a. tag_types (V23)\n { name: 'tag_types', keyPath: ['id'], indexes: [\n { name: 'by-name', keyPath: ['name'], unique: true },\n { name: 'by-user', keyPath: ['userId'] },\n ] },\n // 4. note_tags\n { name: 'note_tags', keyPath: ['id'], indexes: [\n { name: 'by-note', keyPath: ['noteId'] },\n { name: 'by-tag', keyPath: ['tagId'] },\n { name: 'by-note-tag', keyPath: ['noteId', 'tagId'], unique: true },\n { name: 'by-user', keyPath: ['userId'] },\n ] },\n // 5. segments\n { name: 'segments', keyPath: ['id', 'version'], indexes: [\n { name: 'by-note', keyPath: ['noteId'] },\n { name: 'by-type', keyPath: ['dataType'] },\n { name: 'by-page', keyPath: ['pageId'] },\n { name: 'by-history', keyPath: ['isHistory'] },\n { name: 'by-user', keyPath: ['userId'] },\n ] },\n // 6. sessions\n { name: 'sessions', keyPath: ['id'], indexes: [\n { name: 'by-segment', keyPath: ['segmentId'] },\n { name: 'by-note', keyPath: ['noteId'] },\n { name: 'by-completed', keyPath: ['createdAt'] },\n { name: 'by-content', keyPath: ['blockContentId'] },\n { name: 'by-block', keyPath: ['blockId'] },\n { name: 'by-page', keyPath: ['pageId'] },\n { name: 'by-origin', keyPath: ['origin'] },\n { name: 'by-user', keyPath: ['userId'] },\n ] },\n // 7. results — removed in V26: legacy alias of sessions. The V26 upgrade\n // migrates any residual rows into `sessions` (projecting pre-V21 data.logs\n // into event rows) before dropping the store; no compatibility alias.\n // 8. attachments\n { name: 'attachments', keyPath: ['id'], indexes: [\n { name: 'by-note', keyPath: ['noteId'] },\n { name: 'by-time', keyPath: ['createdAt'] },\n { name: 'by-page', keyPath: ['pageId'] },\n { name: 'by-result', keyPath: ['resultId'] },\n { name: 'by-user', keyPath: ['userId'] },\n ] },\n // 9. events\n { name: 'events', keyPath: ['id'], indexes: [\n { name: 'by-timestamp', keyPath: ['timestamp'] },\n { name: 'by-result-grain', keyPath: ['resultId', 'grain'] },\n { name: 'by-content-grain', keyPath: ['blockContentId', 'grain'] },\n { name: 'by-effort', keyPath: ['effortSlug'] },\n { name: 'by-outputType', keyPath: ['outputType'] },\n { name: 'by-grain', keyPath: ['grain'] },\n { name: 'by-metric-date', keyPath: ['metricDateKeys'], multiEntry: true },\n { name: 'by-user', keyPath: ['userId'] },\n ] },\n // 10. field catalog\n { name: 'field_catalog', keyPath: ['id'], indexes: [\n { name: 'by-path', keyPath: ['path'] },\n ] },\n { name: 'field_sources', keyPath: ['id'], indexes: [] },\n { name: 'field_values', keyPath: ['key'], indexes: [\n { name: 'by-field', keyPath: ['fieldId'] },\n ] },\n { name: 'field_catalog_meta', keyPath: ['id'], indexes: [] },\n // 11. efforts\n { name: 'efforts', keyPath: ['slug'], indexes: [\n { name: 'by-discipline', keyPath: ['baseAttributes.discipline'] },\n { name: 'by-source', keyPath: ['registrySource'] },\n { name: 'by-user', keyPath: ['userId'] },\n ] },\n // 12. block_index\n { name: 'block_index', keyPath: ['id'], indexes: [\n { name: 'by-note', keyPath: ['noteId'] },\n { name: 'by-content', keyPath: ['blockContentId'] },\n { name: 'by-type', keyPath: ['dataType'] },\n { name: 'by-source', keyPath: ['sourceId'] },\n { name: 'by-user', keyPath: ['userId'] },\n ] },\n // 13. block_efforts\n { name: 'block_efforts', keyPath: ['id'], indexes: [\n { name: 'by-note', keyPath: ['noteId'] },\n { name: 'by-effort', keyPath: ['effortSlug'] },\n { name: 'by-block', keyPath: ['blockContentId'] },\n { name: 'by-user', keyPath: ['userId'] },\n ] },\n // 14. meta\n { name: 'meta', keyPath: ['key'], indexes: [] },\n // 15. memberships (V25, system — survives wipe; truth lives in\n // localStorage locally, in the SQL table on the API)\n { name: 'memberships', keyPath: ['id'], indexes: [], system: true },\n];\n","/**\n * membership.ts — identity contract shared by both backends.\n *\n * The single local membership (id 'default') is the owner of every\n * user-created record: adapters stamp `userId` onto rows on write (see\n * stampUserOwned), and the by-user index in STORE_DEFS is the ownership\n * crosswalk per store. Seed rows (seedOrigin === 'seed') stay unowned —\n * they are bundled content, not user data.\n */\n\nimport type { StoreName } from './contract';\nimport type { Membership } from './entities';\n\nexport const DEFAULT_MEMBERSHIP_ID = 'default';\n\nexport function createDefaultMembership(now: number = Date.now()): Membership {\n return { id: DEFAULT_MEMBERSHIP_ID, displayName: 'Anonymous', createdAt: now };\n}\n\n/** Stores that can hold user-created rows. Excluded: the pure seed catalog\n * (field_*), meta, and memberships itself. The by-user index in STORE_DEFS\n * mirrors this list — keep in lockstep. */\nexport const USER_OWNED_STORES: readonly StoreName[] = [\n 'notes', 'page', 'page_notes', 'tags', 'tag_types', 'note_tags',\n 'segments', 'sessions', 'attachments', 'events',\n 'efforts', 'block_index', 'block_efforts',\n];\n\nconst STAMPABLE: Record<string, true> = Object.fromEntries(\n USER_OWNED_STORES.map((s) => [s, true as const])\n);\n\n/** Stamps `userId` onto a user-owned row. Seed rows pass through untouched;\n * already-stamped rows are returned as-is (no churn on re-put). Values in\n * non-user-owned stores are returned unchanged. */\nexport function stampUserOwned(store: StoreName, value: unknown, userId: string): unknown {\n if (!STAMPABLE[store]) return value;\n if (value === null || typeof value !== 'object' || Array.isArray(value)) return value;\n const row = value as Record<string, unknown>;\n if (row.seedOrigin === 'seed') return value;\n if (row.userId === userId) return value;\n return { ...row, userId };\n}\n","/**\n * Wire DTOs + key helpers shared by the storage client (ApiStorage) and the\n * API server. Keys are scalars or scalar tuples (keyPath order); ranges bound\n * them. Over HTTP, keys/ranges travel as urlencoded JSON query params.\n *\n * Range-typed helpers accept real IDBKeyRange instances AND plain RangeDTO\n * literals — non-IDB runtimes (Bun, node, tests) have no IDBKeyRange global,\n * so everything duck-types on field shape.\n */\n\nimport type { StoreName } from './contract';\nimport type { StoreDef } from './schema';\n\nexport type Scalar = string | number;\n\n/** Primary key: scalar for single-property keyPaths, tuple for compound. */\nexport type KeyDTO = Scalar | Scalar[];\n\nexport interface RangeDTO {\n only?: KeyDTO;\n /** Inclusive lower bound unless lowerOpen. Scalar or tuple (keyPath order). */\n lower?: KeyDTO;\n /** Inclusive upper bound unless upperOpen. Scalar or tuple (keyPath order). */\n upper?: KeyDTO;\n lowerOpen?: boolean;\n upperOpen?: boolean;\n}\n\nexport type TxOp =\n | { op: 'put'; store: StoreName; value: unknown }\n | { op: 'delete'; store: StoreName; key: KeyDTO }\n | { op: 'clear'; store: StoreName };\n\n/** Anything shaped like a range: real IDBKeyRange or a plain RangeDTO literal. */\nexport function isRange(arg: unknown): arg is RangeDTO {\n return arg !== null && typeof arg === 'object' && !Array.isArray(arg) &&\n ('only' in arg || 'lower' in arg || 'upper' in arg || 'lowerOpen' in arg || 'upperOpen' in arg);\n}\n\n/** Reads one dotted property path ('a.b.c') off a row. */\nexport function extractKeyPart(value: unknown, dottedPath: string): Scalar | undefined {\n let cur: unknown = value;\n for (const part of dottedPath.split('.')) {\n if (cur === null || typeof cur !== 'object') return undefined;\n // Dynamic key on an untyped row: index-signature view is the only way to read it.\n const row = cur as Record<string, unknown>;\n if (!(part in row)) return undefined;\n cur = row[part];\n }\n if (cur === null || typeof cur === 'object' || cur === undefined) return undefined;\n return cur as Scalar; // string/number/boolean leaf narrowed to the wire subset\n}\n\n/** Extracts a row's primary key per the store's inline keyPath. */\nexport function keyFromValue(def: StoreDef, value: unknown): KeyDTO {\n const parts = def.keyPath.map((p) => extractKeyPart(value, p));\n for (const [i, part] of parts.entries()) {\n if (part === undefined) {\n throw new Error(`keyFromValue(${def.name}): keyPath part '${def.keyPath[i]}' missing on value`);\n }\n }\n // Loop above proved every element defined.\n const key = parts as Scalar[];\n return key.length === 1 ? key[0] : key;\n}\n\n/** Canonical url-safe string form of a key (also the buffer/tx match key). */\nexport function encodeKey(key: KeyDTO): string {\n return encodeURIComponent(JSON.stringify(key));\n}\n\n/**\n * Converts an IndexedDB query — bare key, IDBKeyRange, or an already-wire\n * RangeDTO — to its wire form. Duck-types: objects with lower/upper/\n * lowerOpen/upperOpen fields (real ranges or plain literals) pass through;\n * scalars and tuple arrays become {only}.\n */\nexport function rangeFromIDB(query?: IDBValidKey | IDBKeyRange | RangeDTO): RangeDTO | undefined {\n if (query === undefined) return undefined;\n if (!isRange(query)) {\n // Bare IDBValidKey (scalar/tuple; exotic keys like Date pass through lossy).\n const key = query as KeyDTO;\n return { only: key };\n }\n if (query.only !== undefined) return query;\n const wire: RangeDTO = {};\n if (query.lower !== undefined && query.lower !== null) {\n wire.lower = query.lower;\n wire.lowerOpen = query.lowerOpen === true;\n }\n if (query.upper !== undefined && query.upper !== null) {\n wire.upper = query.upper;\n wire.upperOpen = query.upperOpen === true;\n }\n return wire.lower !== undefined || wire.upper !== undefined ? wire : undefined;\n}\n\n/** IndexedDB key ordering: numbers < strings < tuples; tuples lexicographic. */\nexport function compareKeys(a: KeyDTO, b: KeyDTO): number {\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b)) return Array.isArray(a) ? 1 : -1;\n for (let i = 0; i < Math.min(a.length, b.length); i++) {\n const c = compareKeys(a[i], b[i]);\n if (c !== 0) return c;\n }\n return a.length - b.length;\n }\n if (typeof a === 'number' && typeof b === 'number') {\n return a < b ? -1 : a > b ? 1 : 0;\n }\n if (typeof a === 'number' || typeof b === 'number') return typeof a === 'number' ? -1 : 1;\n return a < b ? -1 : a > b ? 1 : 0;\n}\n\n/** True when a key matches a wire range (IDB bound semantics). */\nexport function keyInRange(key: KeyDTO, range: RangeDTO): boolean {\n if (range.only !== undefined) return compareKeys(key, range.only) === 0;\n if (range.lower !== undefined) {\n const c = compareKeys(key, range.lower);\n if (c < 0 || (c === 0 && range.lowerOpen === true)) return false;\n }\n if (range.upper !== undefined) {\n const c = compareKeys(key, range.upper);\n if (c > 0 || (c === 0 && range.upperOpen === true)) return false;\n }\n return true;\n}\n","import type { BlockIndexRow, Note, NoteSegment, Page, PageNote, Tag } from './entities';\n\nexport const DOMAIN_PROJECTION_VERSION = 1;\n\nexport type DomainPredicate =\n | { field: 'id' | 'noteId' | 'type' | 'catalog' | 'source' | 'tags' | 'effort'; values: string[]; negate?: boolean }\n | { field: 'text'; value: string }\n | { field: 'date'; start: number; end: number; endExclusive?: boolean }\n | { field: 'page'; value: boolean }\n | { field: 'defaultNotes'; feeds: boolean }\n | { field: 'sourceFence' };\n\nexport type DomainOrder = {\n field: 'id' | 'createdAt' | 'date' | 'title' | 'noteTitle' | 'dataType' | 'position';\n direction: 'asc' | 'desc';\n};\n\nexport type DomainQuery = {\n plan: 'notes' | 'blocks' | 'entries';\n selection?: DomainPredicate[];\n filters?: DomainPredicate[];\n order?: DomainOrder[];\n offset?: number;\n limit?: number;\n};\n\nexport interface DomainEntry {\n note: Note;\n segments: NoteSegment[];\n tags: Tag[];\n links: PageNote[];\n pages: Page[];\n}\n\nexport type DomainQueryResult = {\n projectionVersion: typeof DOMAIN_PROJECTION_VERSION;\n selectedCount: number;\n matchedCount: number;\n} & (\n | { plan: 'notes'; rows: Note[] }\n | { plan: 'blocks'; rows: BlockIndexRow[] }\n | { plan: 'entries'; rows: DomainEntry[] }\n);\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction optionalStrings(row: Record<string, unknown>, fields: string[]): boolean {\n return fields.every(field => row[field] === undefined || typeof row[field] === 'string');\n}\n\nfunction finiteNumbers(row: Record<string, unknown>, fields: string[]): boolean {\n return fields.every(field => typeof row[field] === 'number' && Number.isFinite(row[field]));\n}\n\nfunction isNote(value: unknown): value is Note {\n return isObject(value) && typeof value.id === 'string' && typeof value.title === 'string'\n && finiteNumbers(value, ['createdAt'])\n && (value.date === undefined || finiteNumbers(value, ['date']))\n && optionalStrings(value, ['type', 'sourceId', 'catalog', 'sourcePath', 'seedOrigin', 'seedChunkId'])\n && (value.tags === undefined || (Array.isArray(value.tags) && value.tags.every(tag => typeof tag === 'string')));\n}\n\nfunction isBlock(value: unknown): value is BlockIndexRow {\n return isObject(value) && typeof value.id === 'string' && typeof value.noteId === 'string'\n && typeof value.segmentId === 'string' && finiteNumbers(value, ['segmentVersion', 'createdAt'])\n && typeof value.dataType === 'string' && typeof value.rawContent === 'string'\n && typeof value.noteTitle === 'string'\n && optionalStrings(value, ['blockContentId', 'sourceId', 'sourcePath', 'routeId'])\n && (value.position === undefined || finiteNumbers(value, ['position']))\n && (value.isStatic === undefined || typeof value.isStatic === 'boolean');\n}\n\nfunction isSegment(value: unknown): value is NoteSegment {\n return isObject(value) && typeof value.id === 'string' && typeof value.noteId === 'string'\n && finiteNumbers(value, ['version', 'createdAt']) && typeof value.dataType === 'string'\n && typeof value.rawContent === 'string' && (value.data === null || isObject(value.data))\n && optionalStrings(value, ['sourceContent', 'pageId'])\n && (value.position === undefined || finiteNumbers(value, ['position']))\n && (value.isHistory === undefined || typeof value.isHistory === 'boolean');\n}\n\nfunction isTag(value: unknown): value is Tag {\n return isObject(value) && typeof value.id === 'string' && typeof value.label === 'string'\n && finiteNumbers(value, ['createdAt']) && optionalStrings(value, ['type']);\n}\n\nfunction isPage(value: unknown): value is Page {\n return isObject(value) && typeof value.id === 'string' && finiteNumbers(value, ['createdAt'])\n && optionalStrings(value, ['date', 'slug', 'title']);\n}\n\nfunction isLink(value: unknown): value is PageNote {\n return isObject(value) && typeof value.id === 'string' && typeof value.noteId === 'string'\n && typeof value.pageId === 'string' && finiteNumbers(value, ['createdAt'])\n && (value.position === undefined || finiteNumbers(value, ['position']));\n}\n\nfunction isEntry(value: unknown): value is DomainEntry {\n return isObject(value) && isNote(value.note)\n && Array.isArray(value.segments) && value.segments.every(isSegment)\n && Array.isArray(value.tags) && value.tags.every(isTag)\n && Array.isArray(value.links) && value.links.every(isLink)\n && Array.isArray(value.pages) && value.pages.every(isPage);\n}\n\nexport function parseDomainQueryResult(value: unknown, plan: DomainQuery['plan']): DomainQueryResult {\n if (!isObject(value) || value.projectionVersion !== DOMAIN_PROJECTION_VERSION\n || value.plan !== plan || !Array.isArray(value.rows)\n || typeof value.selectedCount !== 'number' || !Number.isSafeInteger(value.selectedCount) || value.selectedCount < 0\n || typeof value.matchedCount !== 'number' || !Number.isSafeInteger(value.matchedCount) || value.matchedCount < 0\n || value.matchedCount > value.selectedCount || value.rows.length > value.matchedCount) {\n throw new Error('ApiStorage: invalid or stale domain projection response');\n }\n const counts = { projectionVersion: DOMAIN_PROJECTION_VERSION, selectedCount: value.selectedCount, matchedCount: value.matchedCount } as const;\n switch (plan) {\n case 'notes':\n if (value.rows.every(isNote)) return { ...counts, plan, rows: value.rows };\n break;\n case 'blocks':\n if (value.rows.every(isBlock)) return { ...counts, plan, rows: value.rows };\n break;\n case 'entries':\n if (value.rows.every(isEntry)) return { ...counts, plan, rows: value.rows };\n break;\n }\n throw new Error('ApiStorage: invalid domain query rows');\n}\n","/**\n * Transaction write-buffer + read-overlay helpers shared by the HTTP\n * ApiStorage and the SQL SqlStorage backends. A transaction buffers puts and\n * deletes; reads overlay the buffer on backend rows in op order, so\n * read-your-writes holds without round-tripping uncommitted data.\n */\n\nimport type { StoreName } from './contract';\nimport {\n compareKeys,\n extractKeyPart,\n keyFromValue,\n keyInRange,\n type KeyDTO,\n type RangeDTO,\n type TxOp,\n} from './wire';\nimport type { StoreDef } from './schema';\n\nexport interface BufferedPut { kind: 'put'; value: unknown }\nexport interface BufferedDelete { kind: 'delete'; key: KeyDTO }\nexport type BufferEntry = BufferedPut | BufferedDelete;\n\n// Buffer slot key: store + \\u0000 + raw JSON of the key (JSON text never\n// contains a literal \\u0000, so the separator is unambiguous).\nexport const bufferSlot = (store: StoreName, key: KeyDTO): string => `${store}\\u0000${JSON.stringify(key)}`;\n\n/** True when this tx has buffered writes (puts, deletes or a clear) for the store. */\nexport function isDirty(store: StoreName, buffer?: Map<string, BufferEntry>, cleared?: Set<StoreName>): boolean {\n if (cleared?.has(store)) return true;\n if (!buffer) return false;\n const prefix = `${store}\\u0000`;\n for (const slot of buffer.keys()) {\n if (slot.startsWith(prefix)) return true;\n }\n return false;\n}\n\n/** Point lookup against the buffer; undefined means consult the backend. */\nexport function bufferedGet<T>(store: StoreName, key: KeyDTO, buffer?: Map<string, BufferEntry>):\n | { hit: true; value: T }\n | { hit: false }\n | undefined {\n if (!buffer) return undefined;\n const entry = buffer.get(bufferSlot(store, key));\n if (!entry) return undefined;\n return entry.kind === 'put' ? { hit: true, value: entry.value as T } : { hit: false };\n}\n\n/**\n * Applies buffered puts and deletes before range filtering, ordering and\n * paging. Rows missing an index field are not in the index (IDB semantics);\n * multi-entry keys match when any entry is in range.\n */\nexport function overlayRows<T>(\n def: StoreDef,\n store: StoreName,\n buffer: Map<string, BufferEntry> | undefined,\n cleared: Set<StoreName> | undefined,\n rows: T[],\n count?: number,\n range?: RangeDTO,\n indexName?: string,\n): T[] {\n if (!buffer || !isDirty(store, buffer, cleared)) return count !== undefined ? rows.slice(0, count) : rows;\n const out = new Map<string, T>();\n if (!cleared?.has(store)) {\n // Out-map keys are raw JSON key text — same form as buffer slot suffixes.\n for (const row of rows) out.set(JSON.stringify(keyFromValue(def, row)), row);\n }\n const prefix = `${store}\\u0000`;\n for (const [slot, entry] of buffer) {\n if (!slot.startsWith(prefix)) continue;\n if (entry.kind === 'put') out.set(slot.slice(prefix.length), entry.value as T);\n else out.delete(slot.slice(prefix.length));\n }\n // Dirty reads fetch the complete candidate range: buffered deletes can\n // remove the remote head, and puts can change index membership or order.\n const index = indexName ? def.indexes.find(i => i.name === indexName) : undefined;\n if (indexName && !index) throw new Error(`unknown index '${indexName}' for ${store}`);\n const keyOf = (row: T): KeyDTO | undefined => {\n if (!index) return keyFromValue(def, row);\n const parts = index.keyPath.map(path => extractKeyPart(row, path));\n if (parts.some(part => part === undefined)) return undefined;\n const keys = parts.filter((part): part is string | number => part !== undefined);\n return keys.length === 1 ? keys[0] : keys;\n };\n const matches = (row: T): boolean => {\n if (index?.multiEntry) {\n let values: unknown = row;\n for (const part of index.keyPath[0].split('.')) {\n if (values === null || typeof values !== 'object' || !(part in values)) return false;\n values = Reflect.get(values, part);\n }\n return Array.isArray(values) && values.some(value =>\n (typeof value === 'string' || typeof value === 'number') && (!range || keyInRange(value, range)));\n }\n const key = keyOf(row);\n return key !== undefined && (!range || keyInRange(key, range));\n };\n const merged = [...out.values()].filter(matches);\n merged.sort((a, b) => {\n const primary = compareKeys(keyFromValue(def, a), keyFromValue(def, b));\n if (index?.multiEntry) return primary;\n const ak = keyOf(a), bk = keyOf(b);\n return ak !== undefined && bk !== undefined ? compareKeys(ak, bk) || primary : primary;\n });\n return count !== undefined ? merged.slice(0, count) : merged;\n}\n\n/** Drains the tx buffer into wire ops (clears first, then puts/deletes in op order). */\nexport function bufferToOps(storeNames: Iterable<StoreName>, buffer: Map<string, BufferEntry>, cleared: Set<StoreName>): TxOp[] {\n const known = new Set(storeNames);\n const ops: TxOp[] = [];\n for (const store of cleared) {\n if (known.has(store)) ops.push({ op: 'clear', store });\n }\n for (const [slot, entry] of buffer) {\n const sep = slot.indexOf('\\u0000');\n const store = slot.slice(0, sep) as StoreName;\n if (!known.has(store)) continue;\n if (entry.kind === 'put') ops.push({ op: 'put', store, value: entry.value });\n else ops.push({ op: 'delete', store, key: JSON.parse(slot.slice(sep + 1)) as KeyDTO });\n }\n return ops;\n}\n"]}