sqlite-zod-orm 3.6.1 → 3.7.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 +0 -91
- package/package.json +1 -1
- package/src/database.ts +0 -151
- package/src/types.ts +0 -30
package/dist/index.js
CHANGED
|
@@ -4714,13 +4714,6 @@ class _Database {
|
|
|
4714
4714
|
upsert: (conditions, data) => this.upsert(entityName, data, conditions),
|
|
4715
4715
|
delete: (id) => this.delete(entityName, id),
|
|
4716
4716
|
select: (...cols) => this._createQueryBuilder(entityName, cols),
|
|
4717
|
-
on: (event, callback, options2) => {
|
|
4718
|
-
if (event === "insert")
|
|
4719
|
-
return this._createOnStream(entityName, callback, options2?.interval);
|
|
4720
|
-
if (event === "update" || event === "delete")
|
|
4721
|
-
return this._createChangeStream(entityName, event, callback, options2?.interval);
|
|
4722
|
-
throw new Error(`Unknown event type: '${event}'. Supported: 'insert', 'update', 'delete'`);
|
|
4723
|
-
},
|
|
4724
4717
|
_tableName: entityName
|
|
4725
4718
|
};
|
|
4726
4719
|
this[key] = accessor;
|
|
@@ -4770,90 +4763,6 @@ class _Database {
|
|
|
4770
4763
|
const dataVersion = this.db.query("PRAGMA data_version").get()?.data_version ?? 0;
|
|
4771
4764
|
return `${rev}:${dataVersion}`;
|
|
4772
4765
|
}
|
|
4773
|
-
_createOnStream(entityName, callback, intervalOverride) {
|
|
4774
|
-
const interval = intervalOverride ?? this.pollInterval;
|
|
4775
|
-
const maxRow = this.db.query(`SELECT MAX(id) as _max FROM "${entityName}"`).get();
|
|
4776
|
-
let lastMaxId = maxRow?._max ?? 0;
|
|
4777
|
-
let lastRevision = this._getRevision(entityName);
|
|
4778
|
-
let stopped = false;
|
|
4779
|
-
const poll = async () => {
|
|
4780
|
-
if (stopped)
|
|
4781
|
-
return;
|
|
4782
|
-
const currentRevision = this._getRevision(entityName);
|
|
4783
|
-
if (currentRevision !== lastRevision) {
|
|
4784
|
-
lastRevision = currentRevision;
|
|
4785
|
-
const newRows = this.db.query(`SELECT * FROM "${entityName}" WHERE id > ? ORDER BY id ASC`).all(lastMaxId);
|
|
4786
|
-
for (const rawRow of newRows) {
|
|
4787
|
-
if (stopped)
|
|
4788
|
-
return;
|
|
4789
|
-
const entity = this._attachMethods(entityName, transformFromStorage(rawRow, this.schemas[entityName]));
|
|
4790
|
-
await callback(entity);
|
|
4791
|
-
lastMaxId = rawRow.id;
|
|
4792
|
-
}
|
|
4793
|
-
}
|
|
4794
|
-
if (!stopped)
|
|
4795
|
-
setTimeout(poll, interval);
|
|
4796
|
-
};
|
|
4797
|
-
setTimeout(poll, interval);
|
|
4798
|
-
return () => {
|
|
4799
|
-
stopped = true;
|
|
4800
|
-
};
|
|
4801
|
-
}
|
|
4802
|
-
_createChangeStream(entityName, eventType, callback, intervalOverride) {
|
|
4803
|
-
const interval = intervalOverride ?? this.pollInterval;
|
|
4804
|
-
const allRows = this.db.query(`SELECT * FROM "${entityName}" ORDER BY id ASC`).all();
|
|
4805
|
-
let snapshot = new Map;
|
|
4806
|
-
let snapshotEntities = new Map;
|
|
4807
|
-
for (const row of allRows) {
|
|
4808
|
-
snapshot.set(row.id, JSON.stringify(row));
|
|
4809
|
-
snapshotEntities.set(row.id, row);
|
|
4810
|
-
}
|
|
4811
|
-
let lastRevision = this._getRevision(entityName);
|
|
4812
|
-
let stopped = false;
|
|
4813
|
-
const poll = async () => {
|
|
4814
|
-
if (stopped)
|
|
4815
|
-
return;
|
|
4816
|
-
const currentRevision = this._getRevision(entityName);
|
|
4817
|
-
if (currentRevision !== lastRevision) {
|
|
4818
|
-
lastRevision = currentRevision;
|
|
4819
|
-
const currentRows = this.db.query(`SELECT * FROM "${entityName}" ORDER BY id ASC`).all();
|
|
4820
|
-
const currentMap = new Map;
|
|
4821
|
-
const currentEntities = new Map;
|
|
4822
|
-
for (const row of currentRows) {
|
|
4823
|
-
currentMap.set(row.id, JSON.stringify(row));
|
|
4824
|
-
currentEntities.set(row.id, row);
|
|
4825
|
-
}
|
|
4826
|
-
if (eventType === "update") {
|
|
4827
|
-
for (const [id, json] of currentMap) {
|
|
4828
|
-
if (stopped)
|
|
4829
|
-
return;
|
|
4830
|
-
if (snapshot.has(id) && snapshot.get(id) !== json) {
|
|
4831
|
-
const entity = this._attachMethods(entityName, transformFromStorage(currentEntities.get(id), this.schemas[entityName]));
|
|
4832
|
-
const oldEntity = this._attachMethods(entityName, transformFromStorage(snapshotEntities.get(id), this.schemas[entityName]));
|
|
4833
|
-
await callback(entity, oldEntity);
|
|
4834
|
-
}
|
|
4835
|
-
}
|
|
4836
|
-
} else if (eventType === "delete") {
|
|
4837
|
-
for (const [id] of snapshot) {
|
|
4838
|
-
if (stopped)
|
|
4839
|
-
return;
|
|
4840
|
-
if (!currentMap.has(id)) {
|
|
4841
|
-
const oldEntity = this._attachMethods(entityName, transformFromStorage(snapshotEntities.get(id), this.schemas[entityName]));
|
|
4842
|
-
await callback(oldEntity);
|
|
4843
|
-
}
|
|
4844
|
-
}
|
|
4845
|
-
}
|
|
4846
|
-
snapshot = currentMap;
|
|
4847
|
-
snapshotEntities = currentEntities;
|
|
4848
|
-
}
|
|
4849
|
-
if (!stopped)
|
|
4850
|
-
setTimeout(poll, interval);
|
|
4851
|
-
};
|
|
4852
|
-
setTimeout(poll, interval);
|
|
4853
|
-
return () => {
|
|
4854
|
-
stopped = true;
|
|
4855
|
-
};
|
|
4856
|
-
}
|
|
4857
4766
|
insert(entityName, data) {
|
|
4858
4767
|
const schema = this.schemas[entityName];
|
|
4859
4768
|
const validatedData = asZodObject(schema).passthrough().parse(data);
|
package/package.json
CHANGED
package/src/database.ts
CHANGED
|
@@ -59,11 +59,6 @@ class _Database<Schemas extends SchemaMap> {
|
|
|
59
59
|
upsert: (conditions, data) => this.upsert(entityName, data, conditions),
|
|
60
60
|
delete: (id) => this.delete(entityName, id),
|
|
61
61
|
select: (...cols: string[]) => this._createQueryBuilder(entityName, cols),
|
|
62
|
-
on: (event: string, callback: (...args: any[]) => void | Promise<void>, options?: { interval?: number }) => {
|
|
63
|
-
if (event === 'insert') return this._createOnStream(entityName, callback, options?.interval);
|
|
64
|
-
if (event === 'update' || event === 'delete') return this._createChangeStream(entityName, event, callback, options?.interval);
|
|
65
|
-
throw new Error(`Unknown event type: '${event}'. Supported: 'insert', 'update', 'delete'`);
|
|
66
|
-
},
|
|
67
62
|
_tableName: entityName,
|
|
68
63
|
};
|
|
69
64
|
(this as any)[key] = accessor;
|
|
@@ -144,152 +139,6 @@ class _Database<Schemas extends SchemaMap> {
|
|
|
144
139
|
return `${rev}:${dataVersion}`;
|
|
145
140
|
}
|
|
146
141
|
|
|
147
|
-
// ===========================================================================
|
|
148
|
-
// Row Stream — .on(callback)
|
|
149
|
-
// ===========================================================================
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Stream new rows one at a time, in insertion order.
|
|
153
|
-
*
|
|
154
|
-
* Uses a watermark (last seen id) to query only `WHERE id > ?`.
|
|
155
|
-
* Checks revision + data_version first to avoid unnecessary queries.
|
|
156
|
-
*/
|
|
157
|
-
public _createOnStream(
|
|
158
|
-
entityName: string,
|
|
159
|
-
callback: (row: any) => void | Promise<void>,
|
|
160
|
-
intervalOverride?: number,
|
|
161
|
-
): () => void {
|
|
162
|
-
const interval = intervalOverride ?? this.pollInterval;
|
|
163
|
-
|
|
164
|
-
// Initialize watermark to current max id (only emit NEW rows)
|
|
165
|
-
const maxRow = this.db.query(`SELECT MAX(id) as _max FROM "${entityName}"`).get() as any;
|
|
166
|
-
let lastMaxId: number = maxRow?._max ?? 0;
|
|
167
|
-
let lastRevision: string = this._getRevision(entityName);
|
|
168
|
-
let stopped = false;
|
|
169
|
-
|
|
170
|
-
// Self-scheduling async loop: guarantees strict ordering
|
|
171
|
-
// - Each callback (sync or async) completes before the next row is emitted
|
|
172
|
-
// - Next poll only starts after the current batch is fully processed
|
|
173
|
-
const poll = async () => {
|
|
174
|
-
if (stopped) return;
|
|
175
|
-
|
|
176
|
-
// Fast check: did anything change?
|
|
177
|
-
const currentRevision = this._getRevision(entityName);
|
|
178
|
-
if (currentRevision !== lastRevision) {
|
|
179
|
-
lastRevision = currentRevision;
|
|
180
|
-
|
|
181
|
-
// Fetch new rows since watermark
|
|
182
|
-
const newRows = this.db.query(
|
|
183
|
-
`SELECT * FROM "${entityName}" WHERE id > ? ORDER BY id ASC`
|
|
184
|
-
).all(lastMaxId) as any[];
|
|
185
|
-
|
|
186
|
-
for (const rawRow of newRows) {
|
|
187
|
-
if (stopped) return; // bail if unsubscribed mid-batch
|
|
188
|
-
const entity = this._attachMethods(
|
|
189
|
-
entityName,
|
|
190
|
-
transformFromStorage(rawRow, this.schemas[entityName]!)
|
|
191
|
-
);
|
|
192
|
-
await callback(entity); // await async callbacks
|
|
193
|
-
lastMaxId = rawRow.id;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Schedule next poll only after this one is done
|
|
198
|
-
if (!stopped) setTimeout(poll, interval);
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
// Start the loop
|
|
202
|
-
setTimeout(poll, interval);
|
|
203
|
-
|
|
204
|
-
return () => { stopped = true; };
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Stream specific mutations (update or delete) with natural callback signatures.
|
|
209
|
-
*
|
|
210
|
-
* Maintains a full snapshot and diffs on each poll.
|
|
211
|
-
* Only fires for the subscribed event type.
|
|
212
|
-
*
|
|
213
|
-
* - 'update': callback(row, oldRow)
|
|
214
|
-
* - 'delete': callback(row)
|
|
215
|
-
*/
|
|
216
|
-
public _createChangeStream(
|
|
217
|
-
entityName: string,
|
|
218
|
-
eventType: 'update' | 'delete',
|
|
219
|
-
callback: (...args: any[]) => void | Promise<void>,
|
|
220
|
-
intervalOverride?: number,
|
|
221
|
-
): () => void {
|
|
222
|
-
const interval = intervalOverride ?? this.pollInterval;
|
|
223
|
-
|
|
224
|
-
// Build initial snapshot: Map<id, serialized row>
|
|
225
|
-
const allRows = this.db.query(`SELECT * FROM "${entityName}" ORDER BY id ASC`).all() as any[];
|
|
226
|
-
let snapshot = new Map<number, string>();
|
|
227
|
-
let snapshotEntities = new Map<number, any>();
|
|
228
|
-
for (const row of allRows) {
|
|
229
|
-
snapshot.set(row.id, JSON.stringify(row));
|
|
230
|
-
snapshotEntities.set(row.id, row);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
let lastRevision: string = this._getRevision(entityName);
|
|
234
|
-
let stopped = false;
|
|
235
|
-
|
|
236
|
-
const poll = async () => {
|
|
237
|
-
if (stopped) return;
|
|
238
|
-
|
|
239
|
-
const currentRevision = this._getRevision(entityName);
|
|
240
|
-
if (currentRevision !== lastRevision) {
|
|
241
|
-
lastRevision = currentRevision;
|
|
242
|
-
|
|
243
|
-
const currentRows = this.db.query(`SELECT * FROM "${entityName}" ORDER BY id ASC`).all() as any[];
|
|
244
|
-
const currentMap = new Map<number, string>();
|
|
245
|
-
const currentEntities = new Map<number, any>();
|
|
246
|
-
for (const row of currentRows) {
|
|
247
|
-
currentMap.set(row.id, JSON.stringify(row));
|
|
248
|
-
currentEntities.set(row.id, row);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (eventType === 'update') {
|
|
252
|
-
// Detect updates: existing rows whose JSON changed
|
|
253
|
-
for (const [id, json] of currentMap) {
|
|
254
|
-
if (stopped) return;
|
|
255
|
-
if (snapshot.has(id) && snapshot.get(id) !== json) {
|
|
256
|
-
const entity = this._attachMethods(
|
|
257
|
-
entityName,
|
|
258
|
-
transformFromStorage(currentEntities.get(id), this.schemas[entityName]!)
|
|
259
|
-
);
|
|
260
|
-
const oldEntity = this._attachMethods(
|
|
261
|
-
entityName,
|
|
262
|
-
transformFromStorage(snapshotEntities.get(id), this.schemas[entityName]!)
|
|
263
|
-
);
|
|
264
|
-
await callback(entity, oldEntity);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
} else if (eventType === 'delete') {
|
|
268
|
-
// Detect deletes: rows in snapshot but not in current
|
|
269
|
-
for (const [id] of snapshot) {
|
|
270
|
-
if (stopped) return;
|
|
271
|
-
if (!currentMap.has(id)) {
|
|
272
|
-
const oldEntity = this._attachMethods(
|
|
273
|
-
entityName,
|
|
274
|
-
transformFromStorage(snapshotEntities.get(id), this.schemas[entityName]!)
|
|
275
|
-
);
|
|
276
|
-
await callback(oldEntity);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
snapshot = currentMap;
|
|
282
|
-
snapshotEntities = currentEntities;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (!stopped) setTimeout(poll, interval);
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
setTimeout(poll, interval);
|
|
289
|
-
|
|
290
|
-
return () => { stopped = true; };
|
|
291
|
-
}
|
|
292
|
-
|
|
293
142
|
// ===========================================================================
|
|
294
143
|
// CRUD
|
|
295
144
|
// ===========================================================================
|
package/src/types.ts
CHANGED
|
@@ -149,21 +149,6 @@ export type NavEntityAccessor<
|
|
|
149
149
|
upsert: (conditions?: Partial<z.infer<S[Table & keyof S]>>, data?: Partial<z.infer<S[Table & keyof S]>>) => NavEntity<S, R, Table>;
|
|
150
150
|
delete: (id: number) => void;
|
|
151
151
|
select: (...cols: (keyof z.infer<S[Table & keyof S]> & string)[]) => QueryBuilder<NavEntity<S, R, Table>>;
|
|
152
|
-
/**
|
|
153
|
-
* Listen for table events.
|
|
154
|
-
*
|
|
155
|
-
* `'insert'` — streams new rows, one at a time.
|
|
156
|
-
* `'update'` — fires on row changes with (newRow, oldRow).
|
|
157
|
-
* `'delete'` — fires when a row is removed.
|
|
158
|
-
*
|
|
159
|
-
* Callbacks are awaited — strict ordering guaranteed even with async handlers.
|
|
160
|
-
* @returns Unsubscribe function.
|
|
161
|
-
*/
|
|
162
|
-
on: {
|
|
163
|
-
(event: 'insert', callback: (row: NavEntity<S, R, Table>) => void | Promise<void>, options?: { interval?: number }): () => void;
|
|
164
|
-
(event: 'update', callback: (row: NavEntity<S, R, Table>, oldRow: NavEntity<S, R, Table>) => void | Promise<void>, options?: { interval?: number }): () => void;
|
|
165
|
-
(event: 'delete', callback: (row: NavEntity<S, R, Table>) => void | Promise<void>, options?: { interval?: number }): () => void;
|
|
166
|
-
};
|
|
167
152
|
_tableName: string;
|
|
168
153
|
readonly _schema?: S[Table & keyof S];
|
|
169
154
|
};
|
|
@@ -189,21 +174,6 @@ export type EntityAccessor<S extends z.ZodType<any>> = {
|
|
|
189
174
|
upsert: (conditions?: Partial<InferSchema<S>>, data?: Partial<InferSchema<S>>) => AugmentedEntity<S>;
|
|
190
175
|
delete: (id: number) => void;
|
|
191
176
|
select: (...cols: (keyof InferSchema<S> & string)[]) => QueryBuilder<AugmentedEntity<S>>;
|
|
192
|
-
/**
|
|
193
|
-
* Listen for table events.
|
|
194
|
-
*
|
|
195
|
-
* `'insert'` — streams new rows, one at a time.
|
|
196
|
-
* `'update'` — fires on row changes with (newRow, oldRow).
|
|
197
|
-
* `'delete'` — fires when a row is removed.
|
|
198
|
-
*
|
|
199
|
-
* Callbacks are awaited — strict ordering guaranteed even with async handlers.
|
|
200
|
-
* @returns Unsubscribe function.
|
|
201
|
-
*/
|
|
202
|
-
on: {
|
|
203
|
-
(event: 'insert', callback: (row: AugmentedEntity<S>) => void | Promise<void>, options?: { interval?: number }): () => void;
|
|
204
|
-
(event: 'update', callback: (row: AugmentedEntity<S>, oldRow: AugmentedEntity<S>) => void | Promise<void>, options?: { interval?: number }): () => void;
|
|
205
|
-
(event: 'delete', callback: (row: AugmentedEntity<S>) => void | Promise<void>, options?: { interval?: number }): () => void;
|
|
206
|
-
};
|
|
207
177
|
_tableName: string;
|
|
208
178
|
readonly _schema?: S;
|
|
209
179
|
};
|