sqlite-zod-orm 3.23.0 → 3.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +398 -10
- package/package.json +57 -56
- package/src/database.ts +24 -9
package/dist/index.js
CHANGED
|
@@ -13,6 +13,389 @@ var __export = (target, all) => {
|
|
|
13
13
|
// src/database.ts
|
|
14
14
|
import { Database as SqliteDatabase } from "bun:sqlite";
|
|
15
15
|
|
|
16
|
+
// node_modules/measure-fn/index.ts
|
|
17
|
+
var toAlpha = (num) => {
|
|
18
|
+
let result = "";
|
|
19
|
+
let n = num;
|
|
20
|
+
do {
|
|
21
|
+
result = String.fromCharCode(97 + n % 26) + result;
|
|
22
|
+
n = Math.floor(n / 26) - 1;
|
|
23
|
+
} while (n >= 0);
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
var maxResultLen = 80;
|
|
27
|
+
var safeStringify = (value) => {
|
|
28
|
+
if (value === undefined)
|
|
29
|
+
return "";
|
|
30
|
+
if (value === null)
|
|
31
|
+
return "null";
|
|
32
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
33
|
+
return String(value);
|
|
34
|
+
if (typeof value === "function")
|
|
35
|
+
return `[Function: ${value.name || "anonymous"}]`;
|
|
36
|
+
if (typeof value === "symbol")
|
|
37
|
+
return value.toString();
|
|
38
|
+
if (typeof value === "string") {
|
|
39
|
+
const q = JSON.stringify(value);
|
|
40
|
+
return q.length > maxResultLen ? q.slice(0, maxResultLen - 1) + '\u2026"' : q;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const seen = new WeakSet;
|
|
44
|
+
const str = JSON.stringify(value, (_key, val) => {
|
|
45
|
+
if (typeof val === "object" && val !== null) {
|
|
46
|
+
if (seen.has(val))
|
|
47
|
+
return "[Circular]";
|
|
48
|
+
seen.add(val);
|
|
49
|
+
}
|
|
50
|
+
if (typeof val === "function")
|
|
51
|
+
return `[Function: ${val.name || "anonymous"}]`;
|
|
52
|
+
if (typeof val === "bigint")
|
|
53
|
+
return `${val}n`;
|
|
54
|
+
return val;
|
|
55
|
+
});
|
|
56
|
+
return str.length > maxResultLen ? str.slice(0, maxResultLen) + "\u2026" : str;
|
|
57
|
+
} catch {
|
|
58
|
+
return String(value);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var formatDuration = (ms) => {
|
|
62
|
+
if (ms < 1000)
|
|
63
|
+
return `${ms.toFixed(2)}ms`;
|
|
64
|
+
if (ms < 60000)
|
|
65
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
66
|
+
const mins = Math.floor(ms / 60000);
|
|
67
|
+
const secs = Math.round(ms % 60000 / 1000);
|
|
68
|
+
return `${mins}m ${secs}s`;
|
|
69
|
+
};
|
|
70
|
+
var timestamps = process.env.MEASURE_TIMESTAMPS === "1" || process.env.MEASURE_TIMESTAMPS === "true";
|
|
71
|
+
var ts = () => {
|
|
72
|
+
if (!timestamps)
|
|
73
|
+
return "";
|
|
74
|
+
const now = new Date;
|
|
75
|
+
const h = String(now.getHours()).padStart(2, "0");
|
|
76
|
+
const m = String(now.getMinutes()).padStart(2, "0");
|
|
77
|
+
const s = String(now.getSeconds()).padStart(2, "0");
|
|
78
|
+
const ms = String(now.getMilliseconds()).padStart(3, "0");
|
|
79
|
+
return `[${h}:${m}:${s}.${ms}] `;
|
|
80
|
+
};
|
|
81
|
+
var silent = process.env.MEASURE_SILENT === "1" || process.env.MEASURE_SILENT === "true";
|
|
82
|
+
var logger = null;
|
|
83
|
+
var buildActionLabel = (actionInternal) => {
|
|
84
|
+
return typeof actionInternal === "object" && actionInternal !== null && "label" in actionInternal ? String(actionInternal.label) : String(actionInternal);
|
|
85
|
+
};
|
|
86
|
+
var extractBudget = (actionInternal) => {
|
|
87
|
+
if (typeof actionInternal !== "object" || actionInternal === null)
|
|
88
|
+
return;
|
|
89
|
+
if ("budget" in actionInternal)
|
|
90
|
+
return Number(actionInternal.budget);
|
|
91
|
+
return;
|
|
92
|
+
};
|
|
93
|
+
var extractMeta = (actionInternal) => {
|
|
94
|
+
if (typeof actionInternal !== "object" || actionInternal === null)
|
|
95
|
+
return;
|
|
96
|
+
const details = { ...actionInternal };
|
|
97
|
+
if ("label" in details)
|
|
98
|
+
delete details.label;
|
|
99
|
+
if ("budget" in details)
|
|
100
|
+
delete details.budget;
|
|
101
|
+
if (Object.keys(details).length === 0)
|
|
102
|
+
return;
|
|
103
|
+
return details;
|
|
104
|
+
};
|
|
105
|
+
var formatMeta = (meta) => {
|
|
106
|
+
if (!meta)
|
|
107
|
+
return "";
|
|
108
|
+
const params = Object.entries(meta).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join(" ");
|
|
109
|
+
return ` (${params})`;
|
|
110
|
+
};
|
|
111
|
+
var emit = (event, prefix) => {
|
|
112
|
+
if (silent)
|
|
113
|
+
return;
|
|
114
|
+
if (logger) {
|
|
115
|
+
logger(event);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
defaultLogger(event, prefix);
|
|
119
|
+
};
|
|
120
|
+
var defaultLogger = (event, prefix) => {
|
|
121
|
+
const pfx = prefix ? `${prefix}:` : "";
|
|
122
|
+
const id = `[${pfx}${event.id}]`;
|
|
123
|
+
const t = ts();
|
|
124
|
+
switch (event.type) {
|
|
125
|
+
case "start":
|
|
126
|
+
console.log(`${t}${id} ... ${event.label}${formatMeta(event.meta)}`);
|
|
127
|
+
break;
|
|
128
|
+
case "success": {
|
|
129
|
+
const resultStr = event.result !== undefined ? safeStringify(event.result) : "";
|
|
130
|
+
const arrow = resultStr ? ` \u2192 ${resultStr}` : "";
|
|
131
|
+
const budgetWarn = event.budget && event.duration > event.budget ? ` \u26A0 OVER BUDGET (${formatDuration(event.budget)})` : "";
|
|
132
|
+
console.log(`${t}${id} \u2713 ${event.label} ${formatDuration(event.duration)}${arrow}${budgetWarn}`);
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
case "error": {
|
|
136
|
+
const errorMsg = event.error instanceof Error ? event.error.message : String(event.error);
|
|
137
|
+
const budgetWarn = event.budget && event.duration > event.budget ? ` \u26A0 OVER BUDGET (${formatDuration(event.budget)})` : "";
|
|
138
|
+
console.log(`${t}${id} \u2717 ${event.label} ${formatDuration(event.duration)} (${errorMsg})${budgetWarn}`);
|
|
139
|
+
if (event.error instanceof Error) {
|
|
140
|
+
console.error(`${id}`, event.error.stack ?? event.error.message);
|
|
141
|
+
if (event.error.cause) {
|
|
142
|
+
console.error(`${id} Cause:`, event.error.cause);
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
console.error(`${id}`, event.error);
|
|
146
|
+
}
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
case "annotation":
|
|
150
|
+
console.log(`${t}${id} = ${event.label}${formatMeta(event.meta)}`);
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
var createNestedResolver = (isAsync, fullIdChain, childCounterRef, depth, resolver, prefix) => {
|
|
155
|
+
return (...args) => {
|
|
156
|
+
const label = args[0];
|
|
157
|
+
const fn = args[1];
|
|
158
|
+
if (typeof fn === "function") {
|
|
159
|
+
const childParentChain = [...fullIdChain, childCounterRef.value++];
|
|
160
|
+
return resolver(fn, label, childParentChain, depth + 1);
|
|
161
|
+
} else {
|
|
162
|
+
emit({
|
|
163
|
+
type: "annotation",
|
|
164
|
+
id: fullIdChain.join("-"),
|
|
165
|
+
label: buildActionLabel(label),
|
|
166
|
+
depth: depth + 1,
|
|
167
|
+
meta: extractMeta(label)
|
|
168
|
+
}, prefix);
|
|
169
|
+
return isAsync ? Promise.resolve(null) : null;
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
var globalRootCounter = 0;
|
|
174
|
+
var createMeasureImpl = (prefix, counterRef) => {
|
|
175
|
+
const counter = counterRef ?? { get value() {
|
|
176
|
+
return globalRootCounter;
|
|
177
|
+
}, set value(v) {
|
|
178
|
+
globalRootCounter = v;
|
|
179
|
+
} };
|
|
180
|
+
let _lastError = null;
|
|
181
|
+
const _measureInternal = async (fnInternal, actionInternal, parentIdChain, depth) => {
|
|
182
|
+
const start = performance.now();
|
|
183
|
+
const childCounterRef = { value: 0 };
|
|
184
|
+
const label = buildActionLabel(actionInternal);
|
|
185
|
+
const budget = extractBudget(actionInternal);
|
|
186
|
+
const currentId = toAlpha(parentIdChain.pop() ?? 0);
|
|
187
|
+
const fullIdChain = [...parentIdChain, currentId];
|
|
188
|
+
const idStr = fullIdChain.join("-");
|
|
189
|
+
emit({
|
|
190
|
+
type: "start",
|
|
191
|
+
id: idStr,
|
|
192
|
+
label,
|
|
193
|
+
depth,
|
|
194
|
+
meta: extractMeta(actionInternal)
|
|
195
|
+
}, prefix);
|
|
196
|
+
const measureForNextLevel = createNestedResolver(true, fullIdChain, childCounterRef, depth, _measureInternal, prefix);
|
|
197
|
+
try {
|
|
198
|
+
const result = await fnInternal(measureForNextLevel);
|
|
199
|
+
const duration = performance.now() - start;
|
|
200
|
+
emit({ type: "success", id: idStr, label, depth, duration, result, budget }, prefix);
|
|
201
|
+
return result;
|
|
202
|
+
} catch (error) {
|
|
203
|
+
const duration = performance.now() - start;
|
|
204
|
+
emit({ type: "error", id: idStr, label, depth, duration, error, budget }, prefix);
|
|
205
|
+
_lastError = error;
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
const _measureInternalSync = (fnInternal, actionInternal, parentIdChain, depth) => {
|
|
210
|
+
const start = performance.now();
|
|
211
|
+
const childCounterRef = { value: 0 };
|
|
212
|
+
const label = buildActionLabel(actionInternal);
|
|
213
|
+
const hasNested = fnInternal.length > 0;
|
|
214
|
+
const budget = extractBudget(actionInternal);
|
|
215
|
+
const currentId = toAlpha(parentIdChain.pop() ?? 0);
|
|
216
|
+
const fullIdChain = [...parentIdChain, currentId];
|
|
217
|
+
const idStr = fullIdChain.join("-");
|
|
218
|
+
if (hasNested) {
|
|
219
|
+
emit({
|
|
220
|
+
type: "start",
|
|
221
|
+
id: idStr,
|
|
222
|
+
label,
|
|
223
|
+
depth,
|
|
224
|
+
meta: extractMeta(actionInternal)
|
|
225
|
+
}, prefix);
|
|
226
|
+
}
|
|
227
|
+
const measureForNextLevel = createNestedResolver(false, fullIdChain, childCounterRef, depth, _measureInternalSync, prefix);
|
|
228
|
+
try {
|
|
229
|
+
const result = fnInternal(measureForNextLevel);
|
|
230
|
+
const duration = performance.now() - start;
|
|
231
|
+
emit({ type: "success", id: idStr, label, depth, duration, result, budget }, prefix);
|
|
232
|
+
return result;
|
|
233
|
+
} catch (error) {
|
|
234
|
+
const duration = performance.now() - start;
|
|
235
|
+
emit({ type: "error", id: idStr, label, depth, duration, error, budget }, prefix);
|
|
236
|
+
_lastError = error;
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
const measureFn = async (arg1, arg2) => {
|
|
241
|
+
if (typeof arg2 === "function") {
|
|
242
|
+
return _measureInternal(arg2, arg1, [counter.value++], 0);
|
|
243
|
+
} else {
|
|
244
|
+
const currentId = toAlpha(counter.value++);
|
|
245
|
+
emit({
|
|
246
|
+
type: "annotation",
|
|
247
|
+
id: currentId,
|
|
248
|
+
label: buildActionLabel(arg1),
|
|
249
|
+
depth: 0,
|
|
250
|
+
meta: extractMeta(arg1)
|
|
251
|
+
}, prefix);
|
|
252
|
+
return Promise.resolve(null);
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
measureFn.timed = async (arg1, arg2) => {
|
|
256
|
+
const start = performance.now();
|
|
257
|
+
const result = await measureFn(arg1, arg2);
|
|
258
|
+
const duration = performance.now() - start;
|
|
259
|
+
return { result, duration };
|
|
260
|
+
};
|
|
261
|
+
measureFn.retry = async (label, opts, fn) => {
|
|
262
|
+
const attempts = opts.attempts ?? 3;
|
|
263
|
+
const delay = opts.delay ?? 1000;
|
|
264
|
+
const backoff = opts.backoff ?? 1;
|
|
265
|
+
const lbl = buildActionLabel(label);
|
|
266
|
+
const budget = extractBudget(label);
|
|
267
|
+
for (let i = 0;i < attempts; i++) {
|
|
268
|
+
const attempt = i + 1;
|
|
269
|
+
const attemptLabel = `${lbl} [${attempt}/${attempts}]`;
|
|
270
|
+
const start = performance.now();
|
|
271
|
+
const currentId = toAlpha(counter.value++);
|
|
272
|
+
emit({
|
|
273
|
+
type: "start",
|
|
274
|
+
id: currentId,
|
|
275
|
+
label: attemptLabel,
|
|
276
|
+
depth: 0,
|
|
277
|
+
meta: extractMeta(label)
|
|
278
|
+
}, prefix);
|
|
279
|
+
try {
|
|
280
|
+
const result = await fn();
|
|
281
|
+
const duration = performance.now() - start;
|
|
282
|
+
emit({ type: "success", id: currentId, label: attemptLabel, depth: 0, duration, result, budget }, prefix);
|
|
283
|
+
return result;
|
|
284
|
+
} catch (error) {
|
|
285
|
+
const duration = performance.now() - start;
|
|
286
|
+
emit({ type: "error", id: currentId, label: attemptLabel, depth: 0, duration, error, budget }, prefix);
|
|
287
|
+
if (attempt < attempts) {
|
|
288
|
+
await new Promise((r) => setTimeout(r, delay * Math.pow(backoff, i)));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return null;
|
|
293
|
+
};
|
|
294
|
+
measureFn.assert = async (arg1, arg2) => {
|
|
295
|
+
const result = await measureFn(arg1, arg2);
|
|
296
|
+
if (result === null) {
|
|
297
|
+
const cause = _lastError;
|
|
298
|
+
_lastError = null;
|
|
299
|
+
throw new Error(`measure.assert: "${buildActionLabel(arg1)}" failed`, { cause });
|
|
300
|
+
}
|
|
301
|
+
return result;
|
|
302
|
+
};
|
|
303
|
+
measureFn.wrap = (label, fn) => {
|
|
304
|
+
return (...args) => measureFn(label, () => fn(...args));
|
|
305
|
+
};
|
|
306
|
+
measureFn.batch = async (label, items, fn, opts) => {
|
|
307
|
+
const lbl = buildActionLabel(label);
|
|
308
|
+
const total = items.length;
|
|
309
|
+
const every = opts?.every ?? Math.max(1, Math.ceil(total / 5));
|
|
310
|
+
const currentId = toAlpha(counter.value++);
|
|
311
|
+
const startTime = performance.now();
|
|
312
|
+
emit({
|
|
313
|
+
type: "start",
|
|
314
|
+
id: currentId,
|
|
315
|
+
label: `${lbl} (${total} items)`,
|
|
316
|
+
depth: 0,
|
|
317
|
+
meta: extractMeta(label)
|
|
318
|
+
}, prefix);
|
|
319
|
+
const results = [];
|
|
320
|
+
for (let i = 0;i < items.length; i++) {
|
|
321
|
+
try {
|
|
322
|
+
results.push(await fn(items[i], i));
|
|
323
|
+
} catch {
|
|
324
|
+
results.push(null);
|
|
325
|
+
}
|
|
326
|
+
if ((i + 1) % every === 0 && i + 1 < total) {
|
|
327
|
+
const elapsed = (performance.now() - startTime) / 1000;
|
|
328
|
+
const rate = ((i + 1) / elapsed).toFixed(0);
|
|
329
|
+
emit({
|
|
330
|
+
type: "annotation",
|
|
331
|
+
id: currentId,
|
|
332
|
+
label: `${i + 1}/${total} (${elapsed.toFixed(1)}s, ${rate}/s)`,
|
|
333
|
+
depth: 0
|
|
334
|
+
}, prefix);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const duration = performance.now() - startTime;
|
|
338
|
+
const budget = extractBudget(label);
|
|
339
|
+
emit({
|
|
340
|
+
type: "success",
|
|
341
|
+
id: currentId,
|
|
342
|
+
label: `${lbl} (${total} items)`,
|
|
343
|
+
depth: 0,
|
|
344
|
+
duration,
|
|
345
|
+
result: `${results.filter((r) => r !== null).length}/${total} ok`,
|
|
346
|
+
budget
|
|
347
|
+
}, prefix);
|
|
348
|
+
return results;
|
|
349
|
+
};
|
|
350
|
+
const measureSyncFn = (arg1, arg2) => {
|
|
351
|
+
if (typeof arg2 === "function") {
|
|
352
|
+
return _measureInternalSync(arg2, arg1, [counter.value++], 0);
|
|
353
|
+
} else {
|
|
354
|
+
const currentId = toAlpha(counter.value++);
|
|
355
|
+
emit({
|
|
356
|
+
type: "annotation",
|
|
357
|
+
id: currentId,
|
|
358
|
+
label: buildActionLabel(arg1),
|
|
359
|
+
depth: 0,
|
|
360
|
+
meta: extractMeta(arg1)
|
|
361
|
+
}, prefix);
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
measureSyncFn.timed = (arg1, arg2) => {
|
|
366
|
+
const start = performance.now();
|
|
367
|
+
const result = measureSyncFn(arg1, arg2);
|
|
368
|
+
const duration = performance.now() - start;
|
|
369
|
+
return { result, duration };
|
|
370
|
+
};
|
|
371
|
+
measureSyncFn.assert = (arg1, arg2) => {
|
|
372
|
+
const result = measureSyncFn(arg1, arg2);
|
|
373
|
+
if (result === null) {
|
|
374
|
+
const cause = _lastError;
|
|
375
|
+
_lastError = null;
|
|
376
|
+
throw new Error(`measureSync.assert: "${buildActionLabel(arg1)}" failed`, { cause });
|
|
377
|
+
}
|
|
378
|
+
return result;
|
|
379
|
+
};
|
|
380
|
+
measureSyncFn.wrap = (label, fn) => {
|
|
381
|
+
return (...args) => measureSyncFn(label, () => fn(...args));
|
|
382
|
+
};
|
|
383
|
+
return { measure: measureFn, measureSync: measureSyncFn };
|
|
384
|
+
};
|
|
385
|
+
var globalInstance = createMeasureImpl();
|
|
386
|
+
var measure = globalInstance.measure;
|
|
387
|
+
var measureSync = globalInstance.measureSync;
|
|
388
|
+
var createMeasure = (scopePrefix) => {
|
|
389
|
+
const scopeCounter = { value: 0 };
|
|
390
|
+
const scoped = createMeasureImpl(scopePrefix, scopeCounter);
|
|
391
|
+
return {
|
|
392
|
+
...scoped,
|
|
393
|
+
resetCounter: () => {
|
|
394
|
+
scopeCounter.value = 0;
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
};
|
|
398
|
+
|
|
16
399
|
// node_modules/zod/v3/external.js
|
|
17
400
|
var exports_external = {};
|
|
18
401
|
__export(exports_external, {
|
|
@@ -5265,7 +5648,15 @@ class _Database {
|
|
|
5265
5648
|
_changeWatermark = 0;
|
|
5266
5649
|
_pollTimer = null;
|
|
5267
5650
|
_pollInterval;
|
|
5651
|
+
_measure;
|
|
5652
|
+
_m(label, fn) {
|
|
5653
|
+
if (this._debug)
|
|
5654
|
+
return this._measure.measureSync.assert(label, fn);
|
|
5655
|
+
return fn();
|
|
5656
|
+
}
|
|
5268
5657
|
constructor(dbFile, schemas, options = {}) {
|
|
5658
|
+
this._debug = options.debug === true;
|
|
5659
|
+
this._measure = createMeasure("satidb");
|
|
5269
5660
|
this.db = new SqliteDatabase(dbFile);
|
|
5270
5661
|
if (options.wal !== false)
|
|
5271
5662
|
this.db.run("PRAGMA journal_mode = WAL");
|
|
@@ -5275,7 +5666,6 @@ class _Database {
|
|
|
5275
5666
|
this._reactive = options.reactive !== false;
|
|
5276
5667
|
this._timestamps = options.timestamps === true;
|
|
5277
5668
|
this._softDeletes = options.softDeletes === true;
|
|
5278
|
-
this._debug = options.debug === true;
|
|
5279
5669
|
this._pollInterval = options.pollInterval ?? 100;
|
|
5280
5670
|
this.relationships = options.relations ? parseRelationsConfig(options.relations, schemas) : [];
|
|
5281
5671
|
this._ctx = {
|
|
@@ -5291,19 +5681,19 @@ class _Database {
|
|
|
5291
5681
|
computed: options.computed ?? {},
|
|
5292
5682
|
cascade: options.cascade ?? {}
|
|
5293
5683
|
};
|
|
5294
|
-
this.initializeTables();
|
|
5684
|
+
this._m("Init tables", () => this.initializeTables());
|
|
5295
5685
|
if (this._reactive)
|
|
5296
|
-
this.initializeChangeTracking();
|
|
5297
|
-
this.runMigrations();
|
|
5686
|
+
this._m("Change tracking", () => this.initializeChangeTracking());
|
|
5687
|
+
this._m("Run migrations", () => this.runMigrations());
|
|
5298
5688
|
if (options.indexes)
|
|
5299
|
-
this.createIndexes(options.indexes);
|
|
5689
|
+
this._m("Create indexes", () => this.createIndexes(options.indexes));
|
|
5300
5690
|
if (options.unique)
|
|
5301
|
-
this.createUniqueConstraints(options.unique);
|
|
5691
|
+
this._m("Unique constraints", () => this.createUniqueConstraints(options.unique));
|
|
5302
5692
|
for (const entityName of Object.keys(schemas)) {
|
|
5303
5693
|
const key = entityName;
|
|
5304
5694
|
const accessor = {
|
|
5305
|
-
insert: (data) => insert(this._ctx, entityName, data),
|
|
5306
|
-
insertMany: (rows) => insertMany(this._ctx, entityName, rows),
|
|
5695
|
+
insert: (data) => this._m(`${entityName}.insert`, () => insert(this._ctx, entityName, data)),
|
|
5696
|
+
insertMany: (rows) => this._m(`${entityName}.insertMany(${rows.length})`, () => insertMany(this._ctx, entityName, rows)),
|
|
5307
5697
|
update: (idOrData, data) => {
|
|
5308
5698
|
if (typeof idOrData === "number")
|
|
5309
5699
|
return update(this._ctx, entityName, idOrData, data);
|
|
@@ -5348,8 +5738,6 @@ class _Database {
|
|
|
5348
5738
|
restore: (id) => {
|
|
5349
5739
|
if (!this._softDeletes)
|
|
5350
5740
|
throw new Error("restore() requires softDeletes: true");
|
|
5351
|
-
if (this._debug)
|
|
5352
|
-
console.log("[satidb]", `UPDATE "${entityName}" SET "deletedAt" = NULL WHERE id = ?`, [id]);
|
|
5353
5741
|
this.db.query(`UPDATE "${entityName}" SET "deletedAt" = NULL WHERE id = ?`).run(id);
|
|
5354
5742
|
},
|
|
5355
5743
|
select: (...cols) => createQueryBuilder(this._ctx, entityName, cols),
|
package/package.json
CHANGED
|
@@ -1,57 +1,58 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "sqlite-zod-orm",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "Type-safe SQLite ORM for Bun — Zod schemas, fluent queries, auto relationships, zero SQL",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./dist/index.js",
|
|
7
|
-
"module": "./dist/index.js",
|
|
8
|
-
"types": "./src/index.ts",
|
|
9
|
-
"exports": {
|
|
10
|
-
".": {
|
|
11
|
-
"import": "./dist/index.js",
|
|
12
|
-
"types": "./src/index.ts"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
"scripts": {
|
|
16
|
-
"build": "bun build ./src/index.ts --outdir ./dist --target bun --format esm",
|
|
17
|
-
"clean": "rm -rf dist",
|
|
18
|
-
"test": "bun test",
|
|
19
|
-
"bench": "bun bench/triggers-vs-naive.ts && bun bench/poll-strategy.ts && bun bench/indexes.ts",
|
|
20
|
-
"prepublishOnly": "bun run build"
|
|
21
|
-
},
|
|
22
|
-
"files": [
|
|
23
|
-
"src",
|
|
24
|
-
"dist",
|
|
25
|
-
"README.md"
|
|
26
|
-
],
|
|
27
|
-
"keywords": [
|
|
28
|
-
"sqlite",
|
|
29
|
-
"database",
|
|
30
|
-
"bun",
|
|
31
|
-
"typescript",
|
|
32
|
-
"type-safe",
|
|
33
|
-
"orm",
|
|
34
|
-
"zod",
|
|
35
|
-
"sql",
|
|
36
|
-
"query-builder",
|
|
37
|
-
"relationships"
|
|
38
|
-
],
|
|
39
|
-
"author": "7flash",
|
|
40
|
-
"license": "MIT",
|
|
41
|
-
"repository": {
|
|
42
|
-
"type": "git",
|
|
43
|
-
"url": "git@github.com:7flash/sqlite-zod-orm.git"
|
|
44
|
-
},
|
|
45
|
-
"devDependencies": {
|
|
46
|
-
"bun-types": "latest"
|
|
47
|
-
},
|
|
48
|
-
"peerDependencies": {
|
|
49
|
-
"typescript": "^5.0.0"
|
|
50
|
-
},
|
|
51
|
-
"dependencies": {
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "sqlite-zod-orm",
|
|
3
|
+
"version": "3.24.0",
|
|
4
|
+
"description": "Type-safe SQLite ORM for Bun — Zod schemas, fluent queries, auto relationships, zero SQL",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./src/index.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./src/index.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target bun --format esm",
|
|
17
|
+
"clean": "rm -rf dist",
|
|
18
|
+
"test": "bun test",
|
|
19
|
+
"bench": "bun bench/triggers-vs-naive.ts && bun bench/poll-strategy.ts && bun bench/indexes.ts",
|
|
20
|
+
"prepublishOnly": "bun run build"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"src",
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"keywords": [
|
|
28
|
+
"sqlite",
|
|
29
|
+
"database",
|
|
30
|
+
"bun",
|
|
31
|
+
"typescript",
|
|
32
|
+
"type-safe",
|
|
33
|
+
"orm",
|
|
34
|
+
"zod",
|
|
35
|
+
"sql",
|
|
36
|
+
"query-builder",
|
|
37
|
+
"relationships"
|
|
38
|
+
],
|
|
39
|
+
"author": "7flash",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git@github.com:7flash/sqlite-zod-orm.git"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"bun-types": "latest"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"typescript": "^5.0.0"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"measure-fn": "^3.3.0",
|
|
53
|
+
"zod": "^3.25.67"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"bun": ">=1.0.0"
|
|
57
|
+
}
|
|
57
58
|
}
|
package/src/database.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* focused modules.
|
|
7
7
|
*/
|
|
8
8
|
import { Database as SqliteDatabase } from 'bun:sqlite';
|
|
9
|
+
import { createMeasure } from 'measure-fn';
|
|
9
10
|
import { z } from 'zod';
|
|
10
11
|
import { QueryBuilder, executeProxyQuery, createQueryBuilder, type ProxyQueryResult } from './query';
|
|
11
12
|
import type {
|
|
@@ -63,7 +64,22 @@ class _Database<Schemas extends SchemaMap> {
|
|
|
63
64
|
/** Poll interval in ms. */
|
|
64
65
|
private _pollInterval: number;
|
|
65
66
|
|
|
67
|
+
/** Scoped measure-fn instance for instrumentation. */
|
|
68
|
+
private _measure: ReturnType<typeof createMeasure>;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Conditional measurement helper — wraps with measure-fn only when debug is on.
|
|
72
|
+
* When debug is off, executes fn directly with zero overhead.
|
|
73
|
+
*/
|
|
74
|
+
private _m<T>(label: string, fn: () => T): T {
|
|
75
|
+
if (this._debug) return this._measure.measureSync.assert(label, fn);
|
|
76
|
+
return fn();
|
|
77
|
+
}
|
|
78
|
+
|
|
66
79
|
constructor(dbFile: string, schemas: Schemas, options: DatabaseOptions = {}) {
|
|
80
|
+
this._debug = options.debug === true;
|
|
81
|
+
this._measure = createMeasure('satidb');
|
|
82
|
+
|
|
67
83
|
this.db = new SqliteDatabase(dbFile);
|
|
68
84
|
if (options.wal !== false) this.db.run('PRAGMA journal_mode = WAL');
|
|
69
85
|
this.db.run('PRAGMA foreign_keys = ON');
|
|
@@ -72,7 +88,6 @@ class _Database<Schemas extends SchemaMap> {
|
|
|
72
88
|
this._reactive = options.reactive !== false; // default true
|
|
73
89
|
this._timestamps = options.timestamps === true;
|
|
74
90
|
this._softDeletes = options.softDeletes === true;
|
|
75
|
-
this._debug = options.debug === true;
|
|
76
91
|
this._pollInterval = options.pollInterval ?? 100;
|
|
77
92
|
this.relationships = options.relations ? parseRelationsConfig(options.relations, schemas) : [];
|
|
78
93
|
|
|
@@ -91,18 +106,18 @@ class _Database<Schemas extends SchemaMap> {
|
|
|
91
106
|
cascade: options.cascade ?? {},
|
|
92
107
|
};
|
|
93
108
|
|
|
94
|
-
this.initializeTables();
|
|
95
|
-
if (this._reactive) this.initializeChangeTracking();
|
|
96
|
-
this.runMigrations();
|
|
97
|
-
if (options.indexes) this.createIndexes(options.indexes);
|
|
98
|
-
if (options.unique) this.createUniqueConstraints(options.unique);
|
|
109
|
+
this._m('Init tables', () => this.initializeTables());
|
|
110
|
+
if (this._reactive) this._m('Change tracking', () => this.initializeChangeTracking());
|
|
111
|
+
this._m('Run migrations', () => this.runMigrations());
|
|
112
|
+
if (options.indexes) this._m('Create indexes', () => this.createIndexes(options.indexes!));
|
|
113
|
+
if (options.unique) this._m('Unique constraints', () => this.createUniqueConstraints(options.unique!));
|
|
99
114
|
|
|
100
115
|
// Create typed entity accessors (db.users, db.posts, etc.)
|
|
101
116
|
for (const entityName of Object.keys(schemas)) {
|
|
102
117
|
const key = entityName as keyof Schemas;
|
|
103
118
|
const accessor: EntityAccessor<Schemas[typeof key]> = {
|
|
104
|
-
insert: (data) => insert(this._ctx, entityName, data),
|
|
105
|
-
insertMany: (rows: any[]) => insertMany(this._ctx, entityName, rows),
|
|
119
|
+
insert: (data) => this._m(`${entityName}.insert`, () => insert(this._ctx, entityName, data)),
|
|
120
|
+
insertMany: (rows: any[]) => this._m(`${entityName}.insertMany(${rows.length})`, () => insertMany(this._ctx, entityName, rows)),
|
|
106
121
|
update: (idOrData: any, data?: any) => {
|
|
107
122
|
if (typeof idOrData === 'number') return update(this._ctx, entityName, idOrData, data);
|
|
108
123
|
return createUpdateBuilder(this._ctx, entityName, idOrData);
|
|
@@ -150,7 +165,7 @@ class _Database<Schemas extends SchemaMap> {
|
|
|
150
165
|
}) as any,
|
|
151
166
|
restore: ((id: number) => {
|
|
152
167
|
if (!this._softDeletes) throw new Error('restore() requires softDeletes: true');
|
|
153
|
-
|
|
168
|
+
// debug log replaced by measure-fn instrumentation
|
|
154
169
|
this.db.query(`UPDATE "${entityName}" SET "deletedAt" = NULL WHERE id = ?`).run(id);
|
|
155
170
|
}) as any,
|
|
156
171
|
select: (...cols: string[]) => createQueryBuilder(this._ctx, entityName, cols),
|