@minnowdb/core 0.10.0 → 0.10.1

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,269 @@
1
+ import { IndexedDbBlockStore } from "./indexeddb.js";
2
+ const NOW = "2026-09-12T12:00:00.000Z";
3
+ async function openStore(indexedDB, name = crypto.randomUUID(), durability) {
4
+ return IndexedDbBlockStore.open({ name, indexedDB, ...durability ? { durability } : {} });
5
+ }
6
+ const EVENTS_TABLE = {
7
+ managed: false,
8
+ id: "events",
9
+ name: "events",
10
+ columns: [{ id: "value", name: "value", type: "number", nullable: false }],
11
+ revision: 0,
12
+ createdAt: NOW
13
+ };
14
+ function activeTransaction(id, snapshotVersion) {
15
+ return {
16
+ id,
17
+ ownerId: `owner-${id}`,
18
+ expiresAt: "2026-09-12T12:30:00.000Z",
19
+ snapshotVersion,
20
+ pendingBlockIds: [],
21
+ pendingSegmentIds: [],
22
+ status: "active",
23
+ revision: 0,
24
+ startedAt: NOW,
25
+ updatedAt: NOW,
26
+ committedVersion: null
27
+ };
28
+ }
29
+ function segment(id, transactionId, blockId, commitOrdinal = 0, rowIdStart = 1n) {
30
+ return {
31
+ id,
32
+ tableId: "events",
33
+ transactionId,
34
+ rowCount: 1,
35
+ rowIdStart,
36
+ rowIdEndExclusive: rowIdStart + 1n,
37
+ columnBlockIds: { value: [blockId] },
38
+ kind: "insert",
39
+ level: 0,
40
+ logicalOrder: 0,
41
+ commitOrdinal,
42
+ rowIdSpans: [],
43
+ createdAt: NOW
44
+ };
45
+ }
46
+ async function rawOpen(indexedDB, name) {
47
+ return new Promise((resolve, reject) => {
48
+ const request = indexedDB.open(name);
49
+ request.onsuccess = () => resolve(request.result);
50
+ request.onerror = () => reject(request.error ?? new Error("raw IndexedDB open failed"));
51
+ });
52
+ }
53
+ async function readRawValue(indexedDB, name, storeName, key) {
54
+ const database = await rawOpen(indexedDB, name);
55
+ try {
56
+ const transaction = database.transaction(storeName, "readonly");
57
+ const value = await new Promise((resolve, reject) => {
58
+ const request = transaction.objectStore(storeName).get(key);
59
+ request.onsuccess = () => resolve(request.result);
60
+ request.onerror = () => reject(request.error ?? new Error("raw IndexedDB read failed"));
61
+ });
62
+ await new Promise((resolve, reject) => {
63
+ transaction.oncomplete = () => resolve();
64
+ transaction.onabort = () => reject(transaction.error ?? new Error("raw read aborted"));
65
+ });
66
+ return value;
67
+ } finally {
68
+ database.close();
69
+ }
70
+ }
71
+ async function readRawKeys(indexedDB, name, storeName) {
72
+ const database = await rawOpen(indexedDB, name);
73
+ try {
74
+ const transaction = database.transaction(storeName, "readonly");
75
+ const keys = await new Promise((resolve, reject) => {
76
+ const request = transaction.objectStore(storeName).getAllKeys();
77
+ request.onsuccess = () => resolve(request.result);
78
+ request.onerror = () => reject(request.error ?? new Error("raw IndexedDB read failed"));
79
+ });
80
+ await new Promise((resolve, reject) => {
81
+ transaction.oncomplete = () => resolve();
82
+ transaction.onabort = () => reject(transaction.error ?? new Error("raw read aborted"));
83
+ });
84
+ return keys;
85
+ } finally {
86
+ database.close();
87
+ }
88
+ }
89
+ function instrumentFactory(indexedDB) {
90
+ const state = {
91
+ factory: indexedDB,
92
+ transactions: [],
93
+ requestCounts: /* @__PURE__ */ new Map(),
94
+ setHook(hook) {
95
+ currentHook = hook;
96
+ },
97
+ reset() {
98
+ state.transactions.length = 0;
99
+ state.requestCounts.clear();
100
+ }
101
+ };
102
+ let currentHook;
103
+ const wrappedScanners = /* @__PURE__ */ new WeakSet();
104
+ const countScans = (target, label) => {
105
+ if (wrappedScanners.has(target))
106
+ return;
107
+ wrappedScanners.add(target);
108
+ for (const method of [
109
+ "openCursor",
110
+ "openKeyCursor",
111
+ "getAll",
112
+ "getAllKeys",
113
+ "count"
114
+ ]) {
115
+ const original = target[method].bind(target);
116
+ Object.defineProperty(target, method, {
117
+ configurable: true,
118
+ value: (...args) => {
119
+ const key = `${label}:${method}`;
120
+ state.requestCounts.set(key, (state.requestCounts.get(key) ?? 0) + 1);
121
+ const request = original(...args);
122
+ if (method === "openCursor" || method === "openKeyCursor") {
123
+ request.addEventListener("success", () => {
124
+ const stepKey = `${label}:cursor-steps`;
125
+ if (request.result !== null && request.result !== void 0) {
126
+ state.requestCounts.set(stepKey, (state.requestCounts.get(stepKey) ?? 0) + 1);
127
+ }
128
+ });
129
+ }
130
+ return request;
131
+ }
132
+ });
133
+ }
134
+ };
135
+ const wrappedStores = /* @__PURE__ */ new WeakSet();
136
+ const originalOpen = indexedDB.open.bind(indexedDB);
137
+ Object.defineProperty(indexedDB, "open", {
138
+ configurable: true,
139
+ value: (name, version) => {
140
+ const request = version === void 0 ? originalOpen(name) : originalOpen(name, version);
141
+ request.addEventListener("success", () => {
142
+ const database = request.result;
143
+ const originalTransaction = database.transaction.bind(database);
144
+ Object.defineProperty(database, "transaction", {
145
+ configurable: true,
146
+ value: (stores, mode, options) => {
147
+ const transaction = originalTransaction(stores, mode, options);
148
+ const entry = {
149
+ transaction,
150
+ stores: typeof stores === "string" ? [stores] : [...stores],
151
+ mode: mode ?? "readonly",
152
+ options,
153
+ completed: false,
154
+ aborted: false
155
+ };
156
+ transaction.addEventListener("complete", () => {
157
+ entry.completed = true;
158
+ });
159
+ transaction.addEventListener("abort", () => {
160
+ entry.aborted = true;
161
+ });
162
+ state.transactions.push(entry);
163
+ const originalObjectStore = transaction.objectStore.bind(transaction);
164
+ Object.defineProperty(transaction, "objectStore", {
165
+ configurable: true,
166
+ value: (storeName) => {
167
+ const store = originalObjectStore(storeName);
168
+ if (wrappedStores.has(store))
169
+ return store;
170
+ wrappedStores.add(store);
171
+ countScans(store, storeName);
172
+ const originalIndex = store.index.bind(store);
173
+ Object.defineProperty(store, "index", {
174
+ configurable: true,
175
+ value: (indexName) => {
176
+ const index = originalIndex(indexName);
177
+ countScans(index, `${storeName}.${indexName}`);
178
+ return index;
179
+ }
180
+ });
181
+ for (const method of ["put", "add", "delete", "get", "getKey"]) {
182
+ const original = store[method].bind(store);
183
+ Object.defineProperty(store, method, {
184
+ configurable: true,
185
+ value: (...args) => {
186
+ state.requestCounts.set(storeName, (state.requestCounts.get(storeName) ?? 0) + 1);
187
+ const [first, second] = args;
188
+ const isWrite = method === "put" || method === "add";
189
+ const decision = currentHook?.({
190
+ transaction,
191
+ storeName,
192
+ method,
193
+ key: isWrite ? second : first,
194
+ value: isWrite ? first : void 0
195
+ });
196
+ if (decision === "throw-quota") {
197
+ const failing = original(...args);
198
+ const queue = transaction._requests;
199
+ const entry2 = queue.find((candidate) => candidate.request === failing);
200
+ if (entry2 === void 0)
201
+ throw new Error("request not queued");
202
+ entry2.operation = () => {
203
+ throw new DOMException("The quota has been exceeded.", "QuotaExceededError");
204
+ };
205
+ return failing;
206
+ }
207
+ const result = original(...args);
208
+ if (decision === "abort") {
209
+ try {
210
+ transaction.abort();
211
+ } catch {
212
+ }
213
+ }
214
+ return result;
215
+ }
216
+ });
217
+ }
218
+ return store;
219
+ }
220
+ });
221
+ return transaction;
222
+ }
223
+ });
224
+ });
225
+ return request;
226
+ }
227
+ });
228
+ return state;
229
+ }
230
+ async function stageBlocks(store, record, ids) {
231
+ let current = record;
232
+ for (let start = 0; start < ids.length; start += 64) {
233
+ const slice = ids.slice(start, start + 64);
234
+ current = await store.stageTransactionArtifacts({
235
+ transactionId: record.id,
236
+ expectedRevision: current.revision,
237
+ blocks: slice.map((id) => ({ id, bytes: Uint8Array.of(1) })),
238
+ segments: [],
239
+ updatedAt: NOW
240
+ });
241
+ }
242
+ return current;
243
+ }
244
+ async function stageSegments(store, record, ids, blockId) {
245
+ let current = record;
246
+ for (let start = 0; start < ids.length; start += 64) {
247
+ const slice = ids.slice(start, start + 64);
248
+ current = await store.stageTransactionArtifacts({
249
+ transactionId: record.id,
250
+ expectedRevision: current.revision,
251
+ blocks: [],
252
+ segments: slice.map((id, offset) => segment(id, record.id, blockId, current.pendingSegmentIds.length + offset, BigInt(current.pendingSegmentIds.length + offset + 1))),
253
+ updatedAt: NOW
254
+ });
255
+ }
256
+ return current;
257
+ }
258
+ export {
259
+ EVENTS_TABLE,
260
+ NOW,
261
+ activeTransaction,
262
+ instrumentFactory,
263
+ openStore,
264
+ readRawKeys,
265
+ readRawValue,
266
+ segment,
267
+ stageBlocks,
268
+ stageSegments
269
+ };