allez-orm 1.0.8 → 1.0.10
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/allez-orm.mjs +75 -12
- package/index.d.ts +11 -1
- package/package.json +1 -1
package/allez-orm.mjs
CHANGED
|
@@ -25,18 +25,18 @@
|
|
|
25
25
|
|
|
26
26
|
const DEFAULT_DB_NAME = "allez.db";
|
|
27
27
|
const DEFAULT_AUTOSAVE_MS = 1500;
|
|
28
|
+
const isBrowser = typeof window !== "undefined";
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
// -------- sql.js loader (browser-safe, no node core deps) --------
|
|
30
31
|
async function loadSqlJs(opts = {}) {
|
|
31
32
|
// 1) If user included <script src="https://sql.js.org/dist/sql-wasm.js">, use it.
|
|
32
|
-
if (
|
|
33
|
+
if (isBrowser && window.initSqlJs) {
|
|
33
34
|
return await window.initSqlJs({
|
|
34
35
|
locateFile: opts.wasmLocateFile ?? (f => `https://sql.js.org/dist/${f}`)
|
|
35
36
|
});
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
// 2) Try
|
|
39
|
-
// Dynamic URL import requires CORS; the official CDN allows it.
|
|
39
|
+
// 2) Try CDN ESM (CORS-enabled). Bundlers won’t rewrite this because of webpackIgnore.
|
|
40
40
|
try {
|
|
41
41
|
// @ts-ignore
|
|
42
42
|
const mod = await import(/* webpackIgnore: true */ "https://sql.js.org/dist/sql-wasm.js");
|
|
@@ -48,8 +48,7 @@ async function loadSqlJs(opts = {}) {
|
|
|
48
48
|
// continue to step 3
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
// 3)
|
|
52
|
-
// resolve.alias OR fallbacks to disable node core modules in their bundler.
|
|
51
|
+
// 3) Fallback to local dist entry. (Only works if project resolves to browser build.)
|
|
53
52
|
const mod = await import("sql.js/dist/sql-wasm.js"); // never import "sql.js"
|
|
54
53
|
const initSqlJs = mod.default || mod;
|
|
55
54
|
return await initSqlJs({
|
|
@@ -57,6 +56,7 @@ async function loadSqlJs(opts = {}) {
|
|
|
57
56
|
});
|
|
58
57
|
}
|
|
59
58
|
|
|
59
|
+
// ------------------------- Core ORM -------------------------
|
|
60
60
|
export class AllezORM {
|
|
61
61
|
/** @param {any} SQL @param {any} db @param {InitOptions} opts */
|
|
62
62
|
constructor(SQL, db, opts) {
|
|
@@ -88,11 +88,10 @@ export class AllezORM {
|
|
|
88
88
|
|
|
89
89
|
/** @param {InitOptions=} opts */
|
|
90
90
|
static async init(opts = {}) {
|
|
91
|
-
// Always use the WASM/browser build loader above.
|
|
92
91
|
const SQL = await loadSqlJs(opts);
|
|
93
92
|
|
|
94
93
|
// Restore DB from IndexedDB, or create fresh
|
|
95
|
-
const saved = await idbGet(opts.dbName ?? DEFAULT_DB_NAME);
|
|
94
|
+
const saved = isBrowser ? (await idbGet(opts.dbName ?? DEFAULT_DB_NAME)) : null;
|
|
96
95
|
const db = saved ? new SQL.Database(saved) : new SQL.Database();
|
|
97
96
|
|
|
98
97
|
const orm = new AllezORM(SQL, db, opts);
|
|
@@ -100,7 +99,9 @@ export class AllezORM {
|
|
|
100
99
|
await orm.#ensureMeta();
|
|
101
100
|
|
|
102
101
|
const schemas = collectSchemas(opts);
|
|
103
|
-
|
|
102
|
+
if (schemas.length) {
|
|
103
|
+
await orm.registerSchemas(schemas);
|
|
104
|
+
}
|
|
104
105
|
db.exec("PRAGMA foreign_keys = ON;");
|
|
105
106
|
|
|
106
107
|
return orm;
|
|
@@ -169,7 +170,12 @@ export class AllezORM {
|
|
|
169
170
|
);
|
|
170
171
|
},
|
|
171
172
|
async deleteSoft(id, ts = new Date().toISOString()) {
|
|
172
|
-
|
|
173
|
+
// keep naming consistent across projects
|
|
174
|
+
try {
|
|
175
|
+
await self.execute(`UPDATE ${table} SET deletedAt=? WHERE id=?`, [ts, id]);
|
|
176
|
+
} catch {
|
|
177
|
+
await self.execute(`UPDATE ${table} SET deleted_at=? WHERE id=?`, [ts, id]);
|
|
178
|
+
}
|
|
173
179
|
},
|
|
174
180
|
async remove(id) {
|
|
175
181
|
await self.execute(`DELETE FROM ${table} WHERE id=?`, [id]);
|
|
@@ -195,6 +201,10 @@ export class AllezORM {
|
|
|
195
201
|
async registerSchemas(schemas) {
|
|
196
202
|
const meta = await this.#currentVersions();
|
|
197
203
|
for (const s of schemas) {
|
|
204
|
+
if (!s?.table || !s?.createSQL) {
|
|
205
|
+
// skip invalid schema silently to avoid breaking init
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
198
208
|
const exists = await this.get(
|
|
199
209
|
`SELECT name FROM sqlite_master WHERE type='table' AND name=?`,
|
|
200
210
|
[s.table]
|
|
@@ -225,7 +235,7 @@ export class AllezORM {
|
|
|
225
235
|
|
|
226
236
|
async saveNow() {
|
|
227
237
|
const data = this.db.export(); // Uint8Array
|
|
228
|
-
await idbSet(this.dbName, data);
|
|
238
|
+
if (isBrowser) await idbSet(this.dbName, data);
|
|
229
239
|
}
|
|
230
240
|
|
|
231
241
|
// ---------------- internals ----------------
|
|
@@ -247,6 +257,7 @@ export class AllezORM {
|
|
|
247
257
|
}
|
|
248
258
|
|
|
249
259
|
#scheduleSave() {
|
|
260
|
+
if (!isBrowser) return;
|
|
250
261
|
clearTimeout(this.saveTimer);
|
|
251
262
|
this.saveTimer = setTimeout(() => { void this.saveNow(); }, this.autoSaveMs);
|
|
252
263
|
}
|
|
@@ -260,10 +271,11 @@ function collectSchemas(opts) {
|
|
|
260
271
|
? Object.values(opts.schemaModules).map(m => m.default)
|
|
261
272
|
: [];
|
|
262
273
|
const fromArray = opts.schemas ?? [];
|
|
263
|
-
return [...fromModules, ...fromArray];
|
|
274
|
+
return [...fromModules, ...fromArray].filter(Boolean);
|
|
264
275
|
}
|
|
265
276
|
|
|
266
277
|
function openIdb() {
|
|
278
|
+
if (!isBrowser) throw new Error('IndexedDB not available in this environment.');
|
|
267
279
|
return new Promise((resolve, reject) => {
|
|
268
280
|
const req = indexedDB.open("allez-orm-store", 1);
|
|
269
281
|
req.onupgradeneeded = () => req.result.createObjectStore("dbs");
|
|
@@ -273,6 +285,7 @@ function openIdb() {
|
|
|
273
285
|
}
|
|
274
286
|
|
|
275
287
|
async function idbGet(key) {
|
|
288
|
+
if (!isBrowser) return null;
|
|
276
289
|
const db = await openIdb();
|
|
277
290
|
return new Promise((resolve, reject) => {
|
|
278
291
|
const tx = db.transaction("dbs", "readonly");
|
|
@@ -284,6 +297,7 @@ async function idbGet(key) {
|
|
|
284
297
|
}
|
|
285
298
|
|
|
286
299
|
async function idbSet(key, value) {
|
|
300
|
+
if (!isBrowser) return;
|
|
287
301
|
const db = await openIdb();
|
|
288
302
|
return new Promise((resolve, reject) => {
|
|
289
303
|
const tx = db.transaction("dbs", "readwrite");
|
|
@@ -292,3 +306,52 @@ async function idbSet(key, value) {
|
|
|
292
306
|
tx.onerror = () => reject(tx.error);
|
|
293
307
|
});
|
|
294
308
|
}
|
|
309
|
+
|
|
310
|
+
// ---------------- Browser-friendly convenience exports ----------------
|
|
311
|
+
// These provide the API your Angular app expects: openDb/applySchemas/query/exec
|
|
312
|
+
|
|
313
|
+
/** Cache instances per DB name so consumers can share a handle. */
|
|
314
|
+
const _instances = new Map();
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Open (or reuse) a browser DB by name. Returns an AllezORM instance.
|
|
318
|
+
* @param {string} name
|
|
319
|
+
* @param {InitOptions=} opts
|
|
320
|
+
*/
|
|
321
|
+
export async function openBrowserDb(name, opts = {}) {
|
|
322
|
+
const key = name || (opts.dbName ?? DEFAULT_DB_NAME);
|
|
323
|
+
if (_instances.has(key)) return _instances.get(key);
|
|
324
|
+
const p = AllezORM.init({ ...opts, dbName: key });
|
|
325
|
+
_instances.set(key, p);
|
|
326
|
+
return p;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** Alias kept for compatibility with consumers requesting openDb */
|
|
330
|
+
export const openDb = openBrowserDb;
|
|
331
|
+
|
|
332
|
+
/** Apply an array of Schema objects on an opened AllezORM instance. */
|
|
333
|
+
export async function applySchemas(db, schemas) {
|
|
334
|
+
if (!db || typeof db.registerSchemas !== 'function') {
|
|
335
|
+
throw new Error('applySchemas: invalid db instance; expected AllezORM.');
|
|
336
|
+
}
|
|
337
|
+
await db.registerSchemas(schemas ?? []);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** Run a SELECT and return rows as plain objects. */
|
|
341
|
+
export async function query(db, sql, params = []) {
|
|
342
|
+
if (!db || typeof db.query !== 'function') {
|
|
343
|
+
throw new Error('query: invalid db instance; expected AllezORM.');
|
|
344
|
+
}
|
|
345
|
+
return db.query(sql, params);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/** Execute DDL/DML. */
|
|
349
|
+
export async function exec(db, sql, params = []) {
|
|
350
|
+
if (!db || typeof db.execute !== 'function') {
|
|
351
|
+
throw new Error('exec: invalid db instance; expected AllezORM.');
|
|
352
|
+
}
|
|
353
|
+
await db.execute(sql, params);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Keep a default export for advanced consumers.
|
|
357
|
+
export default AllezORM;
|
package/index.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export interface TableHelper<T extends Row = Row> {
|
|
|
21
21
|
insert(obj: Partial<T>): Promise<void>;
|
|
22
22
|
upsert(obj: Partial<T>): Promise<void>;
|
|
23
23
|
update(id: any, patch: Partial<T>): Promise<void>;
|
|
24
|
+
/** Soft delete; implementation will try `deletedAt` then `deleted_at`. */
|
|
24
25
|
deleteSoft(id: any, ts?: string): Promise<void>;
|
|
25
26
|
remove(id: any): Promise<void>;
|
|
26
27
|
findById(id: any): Promise<T | null>;
|
|
@@ -38,4 +39,13 @@ export class AllezORM {
|
|
|
38
39
|
get<T = Row>(sql: string, params?: any[]): Promise<T | null>;
|
|
39
40
|
table<T extends Row = Row>(table: string): TableHelper<T>;
|
|
40
41
|
registerSchemas(schemas: Schema[]): Promise<void>;
|
|
41
|
-
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Browser helpers + Angular-friendly surface */
|
|
45
|
+
export function openBrowserDb(name: string, opts?: InitOptions): Promise<AllezORM>;
|
|
46
|
+
export const openDb: typeof openBrowserDb;
|
|
47
|
+
export function applySchemas(db: AllezORM, schemas?: Schema[]): Promise<void>;
|
|
48
|
+
export function query<T = Row>(db: AllezORM, sql: string, params?: any[]): Promise<T[]>;
|
|
49
|
+
export function exec(db: AllezORM, sql: string, params?: any[]): Promise<void>;
|
|
50
|
+
|
|
51
|
+
export default AllezORM;
|