sqlite-zod-orm 3.26.0 → 3.26.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.
Files changed (3) hide show
  1. package/README.md +15 -0
  2. package/dist/index.js +79 -30
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -228,6 +228,21 @@ db.exec('UPDATE users SET score = 0 WHERE role = ?', 'guest');
228
228
  - Debug mode (SQL logging)
229
229
  - Raw SQL escape hatch
230
230
 
231
+ ## Contributing
232
+
233
+ SatiDB is opinionated by design. Before proposing new features, understand what it intentionally does **not** do:
234
+
235
+ | ❌ Don't add | Why |
236
+ |---|---|
237
+ | Tagged SQL templates | The whole point is "zero SQL" — `db.raw()` is the escape hatch |
238
+ | FTS5 wrapper | Wrapping it poorly is worse than not wrapping it — use `db.raw()` |
239
+ | Query middleware | `measure-fn` handles observability, `hooks` handle lifecycle |
240
+ | Cursor pagination | Offset pagination covers SQLite's single-process use case |
241
+ | Schema introspection API | Zod schemas are compile-time known — runtime reflection invites dynamic queries |
242
+ | Migration CLI | Auto-migration handles additive changes; use `db.exec()` for destructive ones |
243
+
244
+ **The rule:** if the query builder can already do it, don't add a new API surface for it.
245
+
231
246
  ## Requirements
232
247
 
233
248
  - **Bun** ≥ 1.0 (uses `bun:sqlite` native bindings)
package/dist/index.js CHANGED
@@ -23,8 +23,9 @@ var toAlpha = (num) => {
23
23
  } while (n >= 0);
24
24
  return result;
25
25
  };
26
- var maxResultLen = 80;
27
- var safeStringify = (value) => {
26
+ var maxResultLen = 0;
27
+ var safeStringify = (value, limit) => {
28
+ const cap = limit ?? maxResultLen;
28
29
  if (value === undefined)
29
30
  return "";
30
31
  if (value === null)
@@ -37,7 +38,9 @@ var safeStringify = (value) => {
37
38
  return value.toString();
38
39
  if (typeof value === "string") {
39
40
  const q = JSON.stringify(value);
40
- return q.length > maxResultLen ? q.slice(0, maxResultLen - 1) + '\u2026"' : q;
41
+ if (cap === 0)
42
+ return q;
43
+ return q.length > cap ? q.slice(0, cap - 1) + '\u2026"' : q;
41
44
  }
42
45
  try {
43
46
  const seen = new WeakSet;
@@ -53,7 +56,9 @@ var safeStringify = (value) => {
53
56
  return `${val}n`;
54
57
  return val;
55
58
  });
56
- return str.length > maxResultLen ? str.slice(0, maxResultLen) + "\u2026" : str;
59
+ if (cap === 0)
60
+ return str;
61
+ return str.length > cap ? str.slice(0, cap) + "\u2026" : str;
57
62
  } catch {
58
63
  return String(value);
59
64
  }
@@ -67,7 +72,7 @@ var formatDuration = (ms) => {
67
72
  const secs = Math.round(ms % 60000 / 1000);
68
73
  return `${mins}m ${secs}s`;
69
74
  };
70
- var timestamps = process.env.MEASURE_TIMESTAMPS === "1" || process.env.MEASURE_TIMESTAMPS === "true";
75
+ var timestamps = typeof process !== "undefined" && (process.env.MEASURE_TIMESTAMPS === "1" || process.env.MEASURE_TIMESTAMPS === "true");
71
76
  var ts = () => {
72
77
  if (!timestamps)
73
78
  return "";
@@ -78,7 +83,9 @@ var ts = () => {
78
83
  const ms = String(now.getMilliseconds()).padStart(3, "0");
79
84
  return `[${h}:${m}:${s}.${ms}] `;
80
85
  };
81
- var silent = process.env.MEASURE_SILENT === "1" || process.env.MEASURE_SILENT === "true";
86
+ var silent = typeof process !== "undefined" && (process.env.MEASURE_SILENT === "1" || process.env.MEASURE_SILENT === "true");
87
+ var dotEndLabel = true;
88
+ var dotChar = "\xB7";
82
89
  var logger = null;
83
90
  var buildActionLabel = (actionInternal) => {
84
91
  return typeof actionInternal === "object" && actionInternal !== null && "label" in actionInternal ? String(actionInternal.label) : String(actionInternal);
@@ -90,6 +97,20 @@ var extractBudget = (actionInternal) => {
90
97
  return Number(actionInternal.budget);
91
98
  return;
92
99
  };
100
+ var extractTimeout = (actionInternal) => {
101
+ if (typeof actionInternal !== "object" || actionInternal === null)
102
+ return;
103
+ if ("timeout" in actionInternal)
104
+ return Number(actionInternal.timeout);
105
+ return;
106
+ };
107
+ var extractMaxResultLength = (actionInternal) => {
108
+ if (typeof actionInternal !== "object" || actionInternal === null)
109
+ return;
110
+ if ("maxResultLength" in actionInternal)
111
+ return Number(actionInternal.maxResultLength);
112
+ return;
113
+ };
93
114
  var extractMeta = (actionInternal) => {
94
115
  if (typeof actionInternal !== "object" || actionInternal === null)
95
116
  return;
@@ -98,6 +119,8 @@ var extractMeta = (actionInternal) => {
98
119
  delete details.label;
99
120
  if ("budget" in details)
100
121
  delete details.budget;
122
+ if ("maxResultLength" in details)
123
+ delete details.maxResultLength;
101
124
  if (Object.keys(details).length === 0)
102
125
  return;
103
126
  return details;
@@ -126,16 +149,18 @@ var defaultLogger = (event, prefix) => {
126
149
  console.log(`${t}${id} ... ${event.label}${formatMeta(event.meta)}`);
127
150
  break;
128
151
  case "success": {
129
- const resultStr = event.result !== undefined ? safeStringify(event.result) : "";
152
+ const endLabel = dotEndLabel ? dotChar.repeat(event.label.length) : event.label;
153
+ const resultStr = event.result !== undefined ? safeStringify(event.result, event.maxResultLength) : "";
130
154
  const arrow = resultStr ? ` \u2192 ${resultStr}` : "";
131
155
  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}`);
156
+ console.log(`${t}${id} ${endLabel} ${formatDuration(event.duration)}${arrow}${budgetWarn}`);
133
157
  break;
134
158
  }
135
159
  case "error": {
160
+ const endLabel = dotEndLabel ? dotChar.repeat(event.label.length) : event.label;
136
161
  const errorMsg = event.error instanceof Error ? event.error.message : String(event.error);
137
162
  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}`);
163
+ console.log(`${t}${id} \u2717 ${endLabel} ${formatDuration(event.duration)} (${errorMsg})${budgetWarn}`);
139
164
  if (event.error instanceof Error) {
140
165
  console.error(`${id}`, event.error.stack ?? event.error.message);
141
166
  if (event.error.cause) {
@@ -151,13 +176,14 @@ var defaultLogger = (event, prefix) => {
151
176
  break;
152
177
  }
153
178
  };
154
- var createNestedResolver = (isAsync, fullIdChain, childCounterRef, depth, resolver, prefix) => {
179
+ var createNestedResolver = (isAsync, fullIdChain, childCounterRef, depth, resolver, prefix, inheritedMaxLen) => {
155
180
  return (...args) => {
156
181
  const label = args[0];
157
182
  const fn = args[1];
183
+ const onError = args[2];
158
184
  if (typeof fn === "function") {
159
185
  const childParentChain = [...fullIdChain, childCounterRef.value++];
160
- return resolver(fn, label, childParentChain, depth + 1);
186
+ return resolver(fn, label, childParentChain, depth + 1, typeof onError === "function" ? onError : undefined, inheritedMaxLen);
161
187
  } else {
162
188
  emit({
163
189
  type: "annotation",
@@ -171,20 +197,24 @@ var createNestedResolver = (isAsync, fullIdChain, childCounterRef, depth, resolv
171
197
  };
172
198
  };
173
199
  var globalRootCounter = 0;
174
- var createMeasureImpl = (prefix, counterRef) => {
200
+ var createMeasureImpl = (prefix, counterRef, scopeOpts) => {
175
201
  const counter = counterRef ?? { get value() {
176
202
  return globalRootCounter;
177
203
  }, set value(v) {
178
204
  globalRootCounter = v;
179
205
  } };
206
+ const scopeMaxLen = scopeOpts?.maxResultLength;
180
207
  let _lastError = null;
181
- const _measureInternal = async (fnInternal, actionInternal, parentIdChain, depth) => {
208
+ const _measureInternal = async (fnInternal, actionInternal, parentIdChain, depth, onError, inheritedMaxLen) => {
182
209
  const start = performance.now();
183
210
  const childCounterRef = { value: 0 };
184
211
  const label = buildActionLabel(actionInternal);
185
212
  const budget = extractBudget(actionInternal);
186
- const currentId = toAlpha(parentIdChain.pop() ?? 0);
187
- const fullIdChain = [...parentIdChain, currentId];
213
+ const timeout = extractTimeout(actionInternal);
214
+ const localMaxLen = extractMaxResultLength(actionInternal);
215
+ const effectiveMaxLen = localMaxLen ?? inheritedMaxLen;
216
+ const currentId = toAlpha(Number(parentIdChain.pop() ?? 0));
217
+ const fullIdChain = [...parentIdChain.map(String), currentId];
188
218
  const idStr = fullIdChain.join("-");
189
219
  emit({
190
220
  type: "start",
@@ -193,27 +223,46 @@ var createMeasureImpl = (prefix, counterRef) => {
193
223
  depth,
194
224
  meta: extractMeta(actionInternal)
195
225
  }, prefix);
196
- const measureForNextLevel = createNestedResolver(true, fullIdChain, childCounterRef, depth, _measureInternal, prefix);
226
+ const measureForNextLevel = createNestedResolver(true, fullIdChain, childCounterRef, depth, _measureInternal, prefix, effectiveMaxLen);
197
227
  try {
198
- const result = await fnInternal(measureForNextLevel);
228
+ let result;
229
+ if (timeout && timeout > 0) {
230
+ result = await Promise.race([
231
+ fnInternal(measureForNextLevel),
232
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`Timeout (${formatDuration(timeout)})`)), timeout))
233
+ ]);
234
+ } else {
235
+ result = await fnInternal(measureForNextLevel);
236
+ }
199
237
  const duration = performance.now() - start;
200
- emit({ type: "success", id: idStr, label, depth, duration, result, budget }, prefix);
238
+ emit({ type: "success", id: idStr, label, depth, duration, result, budget, maxResultLength: effectiveMaxLen }, prefix);
201
239
  return result;
202
240
  } catch (error) {
203
241
  const duration = performance.now() - start;
204
- emit({ type: "error", id: idStr, label, depth, duration, error, budget }, prefix);
242
+ emit({ type: "error", id: idStr, label, depth, duration, error, budget, maxResultLength: effectiveMaxLen }, prefix);
205
243
  _lastError = error;
244
+ if (onError) {
245
+ try {
246
+ return onError(error);
247
+ } catch (onErrorError) {
248
+ emit({ type: "error", id: idStr, label: `${label} (onError)`, depth, duration: performance.now() - start, error: onErrorError, budget, maxResultLength: effectiveMaxLen }, prefix);
249
+ _lastError = onErrorError;
250
+ return null;
251
+ }
252
+ }
206
253
  return null;
207
254
  }
208
255
  };
209
- const _measureInternalSync = (fnInternal, actionInternal, parentIdChain, depth) => {
256
+ const _measureInternalSync = (fnInternal, actionInternal, parentIdChain, depth, _onError, inheritedMaxLen) => {
210
257
  const start = performance.now();
211
258
  const childCounterRef = { value: 0 };
212
259
  const label = buildActionLabel(actionInternal);
213
260
  const hasNested = fnInternal.length > 0;
214
261
  const budget = extractBudget(actionInternal);
215
- const currentId = toAlpha(parentIdChain.pop() ?? 0);
216
- const fullIdChain = [...parentIdChain, currentId];
262
+ const localMaxLen = extractMaxResultLength(actionInternal);
263
+ const effectiveMaxLen = localMaxLen ?? inheritedMaxLen;
264
+ const currentId = toAlpha(Number(parentIdChain.pop() ?? 0));
265
+ const fullIdChain = [...parentIdChain.map(String), currentId];
217
266
  const idStr = fullIdChain.join("-");
218
267
  if (hasNested) {
219
268
  emit({
@@ -224,22 +273,22 @@ var createMeasureImpl = (prefix, counterRef) => {
224
273
  meta: extractMeta(actionInternal)
225
274
  }, prefix);
226
275
  }
227
- const measureForNextLevel = createNestedResolver(false, fullIdChain, childCounterRef, depth, _measureInternalSync, prefix);
276
+ const measureForNextLevel = createNestedResolver(false, fullIdChain, childCounterRef, depth, _measureInternalSync, prefix, effectiveMaxLen);
228
277
  try {
229
278
  const result = fnInternal(measureForNextLevel);
230
279
  const duration = performance.now() - start;
231
- emit({ type: "success", id: idStr, label, depth, duration, result, budget }, prefix);
280
+ emit({ type: "success", id: idStr, label, depth, duration, result, budget, maxResultLength: effectiveMaxLen }, prefix);
232
281
  return result;
233
282
  } catch (error) {
234
283
  const duration = performance.now() - start;
235
- emit({ type: "error", id: idStr, label, depth, duration, error, budget }, prefix);
284
+ emit({ type: "error", id: idStr, label, depth, duration, error, budget, maxResultLength: effectiveMaxLen }, prefix);
236
285
  _lastError = error;
237
286
  return null;
238
287
  }
239
288
  };
240
- const measureFn = async (arg1, arg2) => {
289
+ const measureFn = async (arg1, arg2, arg3) => {
241
290
  if (typeof arg2 === "function") {
242
- return _measureInternal(arg2, arg1, [counter.value++], 0);
291
+ return _measureInternal(arg2, arg1, [counter.value++], 0, arg3, scopeMaxLen);
243
292
  } else {
244
293
  const currentId = toAlpha(counter.value++);
245
294
  emit({
@@ -349,7 +398,7 @@ var createMeasureImpl = (prefix, counterRef) => {
349
398
  };
350
399
  const measureSyncFn = (arg1, arg2) => {
351
400
  if (typeof arg2 === "function") {
352
- return _measureInternalSync(arg2, arg1, [counter.value++], 0);
401
+ return _measureInternalSync(arg2, arg1, [counter.value++], 0, undefined, scopeMaxLen);
353
402
  } else {
354
403
  const currentId = toAlpha(counter.value++);
355
404
  emit({
@@ -385,9 +434,9 @@ var createMeasureImpl = (prefix, counterRef) => {
385
434
  var globalInstance = createMeasureImpl();
386
435
  var measure = globalInstance.measure;
387
436
  var measureSync = globalInstance.measureSync;
388
- var createMeasure = (scopePrefix) => {
437
+ var createMeasure = (scopePrefix, opts) => {
389
438
  const scopeCounter = { value: 0 };
390
- const scoped = createMeasureImpl(scopePrefix, scopeCounter);
439
+ const scoped = createMeasureImpl(scopePrefix, scopeCounter, opts);
391
440
  return {
392
441
  ...scoped,
393
442
  resetCounter: () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sqlite-zod-orm",
3
- "version": "3.26.0",
3
+ "version": "3.26.1",
4
4
  "description": "Type-safe SQLite ORM for Bun — Zod schemas, fluent queries, auto relationships, zero SQL",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -49,10 +49,10 @@
49
49
  "typescript": "^5.0.0"
50
50
  },
51
51
  "dependencies": {
52
- "measure-fn": "^3.3.0",
52
+ "measure-fn": "^3.10.0",
53
53
  "zod": "^3.25.67"
54
54
  },
55
55
  "engines": {
56
56
  "bun": ">=1.0.0"
57
57
  }
58
- }
58
+ }