allez-orm 1.0.6 → 1.0.9
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 +76 -12
- package/index.d.ts +43 -1
- package/package.json +2 -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,13 @@ export class AllezORM {
|
|
|
169
170
|
);
|
|
170
171
|
},
|
|
171
172
|
async deleteSoft(id, ts = new Date().toISOString()) {
|
|
172
|
-
|
|
173
|
+
// keep naming consistent with your schema (deletedAt)
|
|
174
|
+
// adjust if your table uses deleted_at instead
|
|
175
|
+
try {
|
|
176
|
+
await self.execute(`UPDATE ${table} SET deletedAt=? WHERE id=?`, [ts, id]);
|
|
177
|
+
} catch {
|
|
178
|
+
await self.execute(`UPDATE ${table} SET deleted_at=? WHERE id=?`, [ts, id]);
|
|
179
|
+
}
|
|
173
180
|
},
|
|
174
181
|
async remove(id) {
|
|
175
182
|
await self.execute(`DELETE FROM ${table} WHERE id=?`, [id]);
|
|
@@ -195,6 +202,10 @@ export class AllezORM {
|
|
|
195
202
|
async registerSchemas(schemas) {
|
|
196
203
|
const meta = await this.#currentVersions();
|
|
197
204
|
for (const s of schemas) {
|
|
205
|
+
if (!s?.table || !s?.createSQL) {
|
|
206
|
+
// skip invalid schema silently to avoid breaking init
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
198
209
|
const exists = await this.get(
|
|
199
210
|
`SELECT name FROM sqlite_master WHERE type='table' AND name=?`,
|
|
200
211
|
[s.table]
|
|
@@ -225,7 +236,7 @@ export class AllezORM {
|
|
|
225
236
|
|
|
226
237
|
async saveNow() {
|
|
227
238
|
const data = this.db.export(); // Uint8Array
|
|
228
|
-
await idbSet(this.dbName, data);
|
|
239
|
+
if (isBrowser) await idbSet(this.dbName, data);
|
|
229
240
|
}
|
|
230
241
|
|
|
231
242
|
// ---------------- internals ----------------
|
|
@@ -247,6 +258,7 @@ export class AllezORM {
|
|
|
247
258
|
}
|
|
248
259
|
|
|
249
260
|
#scheduleSave() {
|
|
261
|
+
if (!isBrowser) return;
|
|
250
262
|
clearTimeout(this.saveTimer);
|
|
251
263
|
this.saveTimer = setTimeout(() => { void this.saveNow(); }, this.autoSaveMs);
|
|
252
264
|
}
|
|
@@ -260,10 +272,11 @@ function collectSchemas(opts) {
|
|
|
260
272
|
? Object.values(opts.schemaModules).map(m => m.default)
|
|
261
273
|
: [];
|
|
262
274
|
const fromArray = opts.schemas ?? [];
|
|
263
|
-
return [...fromModules, ...fromArray];
|
|
275
|
+
return [...fromModules, ...fromArray].filter(Boolean);
|
|
264
276
|
}
|
|
265
277
|
|
|
266
278
|
function openIdb() {
|
|
279
|
+
if (!isBrowser) throw new Error('IndexedDB not available in this environment.');
|
|
267
280
|
return new Promise((resolve, reject) => {
|
|
268
281
|
const req = indexedDB.open("allez-orm-store", 1);
|
|
269
282
|
req.onupgradeneeded = () => req.result.createObjectStore("dbs");
|
|
@@ -273,6 +286,7 @@ function openIdb() {
|
|
|
273
286
|
}
|
|
274
287
|
|
|
275
288
|
async function idbGet(key) {
|
|
289
|
+
if (!isBrowser) return null;
|
|
276
290
|
const db = await openIdb();
|
|
277
291
|
return new Promise((resolve, reject) => {
|
|
278
292
|
const tx = db.transaction("dbs", "readonly");
|
|
@@ -284,6 +298,7 @@ async function idbGet(key) {
|
|
|
284
298
|
}
|
|
285
299
|
|
|
286
300
|
async function idbSet(key, value) {
|
|
301
|
+
if (!isBrowser) return;
|
|
287
302
|
const db = await openIdb();
|
|
288
303
|
return new Promise((resolve, reject) => {
|
|
289
304
|
const tx = db.transaction("dbs", "readwrite");
|
|
@@ -292,3 +307,52 @@ async function idbSet(key, value) {
|
|
|
292
307
|
tx.onerror = () => reject(tx.error);
|
|
293
308
|
});
|
|
294
309
|
}
|
|
310
|
+
|
|
311
|
+
// ---------------- Browser-friendly convenience exports ----------------
|
|
312
|
+
// These provide the API your Angular app expects: openDb/applySchemas/query/exec
|
|
313
|
+
|
|
314
|
+
/** Cache instances per DB name so consumers can share a handle. */
|
|
315
|
+
const _instances = new Map();
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Open (or reuse) a browser DB by name. Returns an AllezORM instance.
|
|
319
|
+
* @param {string} name
|
|
320
|
+
* @param {InitOptions=} opts
|
|
321
|
+
*/
|
|
322
|
+
export async function openBrowserDb(name, opts = {}) {
|
|
323
|
+
const key = name || (opts.dbName ?? DEFAULT_DB_NAME);
|
|
324
|
+
if (_instances.has(key)) return _instances.get(key);
|
|
325
|
+
const p = AllezORM.init({ ...opts, dbName: key });
|
|
326
|
+
_instances.set(key, p);
|
|
327
|
+
return p;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** Alias kept for compatibility with consumers requesting openDb */
|
|
331
|
+
export const openDb = openBrowserDb;
|
|
332
|
+
|
|
333
|
+
/** Apply an array of Schema objects on an opened AllezORM instance. */
|
|
334
|
+
export async function applySchemas(db, schemas) {
|
|
335
|
+
if (!db || typeof db.registerSchemas !== 'function') {
|
|
336
|
+
throw new Error('applySchemas: invalid db instance; expected AllezORM.');
|
|
337
|
+
}
|
|
338
|
+
await db.registerSchemas(schemas ?? []);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** Run a SELECT and return rows as plain objects. */
|
|
342
|
+
export async function query(db, sql, params = []) {
|
|
343
|
+
if (!db || typeof db.query !== 'function') {
|
|
344
|
+
throw new Error('query: invalid db instance; expected AllezORM.');
|
|
345
|
+
}
|
|
346
|
+
return db.query(sql, params);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** Execute DDL/DML. */
|
|
350
|
+
export async function exec(db, sql, params = []) {
|
|
351
|
+
if (!db || typeof db.execute !== 'function') {
|
|
352
|
+
throw new Error('exec: invalid db instance; expected AllezORM.');
|
|
353
|
+
}
|
|
354
|
+
await db.execute(sql, params);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Keep a default export for advanced consumers.
|
|
358
|
+
export default AllezORM;
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// index.d.ts
|
|
2
|
+
|
|
2
3
|
export interface Schema {
|
|
3
4
|
table: string;
|
|
4
5
|
createSQL: string;
|
|
@@ -12,6 +13,7 @@ export interface InitOptions {
|
|
|
12
13
|
autoSaveMs?: number;
|
|
13
14
|
wasmLocateFile?(file: string): string;
|
|
14
15
|
schemas?: Schema[];
|
|
16
|
+
/** Modules that default-export a Schema (useful for tree-shaking/auto-collect). */
|
|
15
17
|
schemaModules?: Record<string, { default: Schema }>;
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -21,6 +23,7 @@ export interface TableHelper<T extends Row = Row> {
|
|
|
21
23
|
insert(obj: Partial<T>): Promise<void>;
|
|
22
24
|
upsert(obj: Partial<T>): Promise<void>;
|
|
23
25
|
update(id: any, patch: Partial<T>): Promise<void>;
|
|
26
|
+
/** Soft delete (tries `deletedAt`; falls back to `deleted_at`) */
|
|
24
27
|
deleteSoft(id: any, ts?: string): Promise<void>;
|
|
25
28
|
remove(id: any): Promise<void>;
|
|
26
29
|
findById(id: any): Promise<T | null>;
|
|
@@ -29,13 +32,52 @@ export interface TableHelper<T extends Row = Row> {
|
|
|
29
32
|
|
|
30
33
|
export class AllezORM {
|
|
31
34
|
constructor(SQL: any, db: any, opts: InitOptions);
|
|
35
|
+
/** Initialize an AllezORM instance (loads sql.js, restores from IndexedDB, applies schemas). */
|
|
32
36
|
static init(opts?: InitOptions): Promise<AllezORM>;
|
|
37
|
+
|
|
38
|
+
/** Persist DB to IndexedDB immediately. */
|
|
33
39
|
saveNow(): Promise<void>;
|
|
40
|
+
|
|
41
|
+
/** Execute DDL/DML; returns true on success (compat helper). */
|
|
34
42
|
exec(sql: string, params?: any[]): Promise<boolean>;
|
|
43
|
+
/** Alias of exec for ergonomics. */
|
|
35
44
|
run(sql: string, params?: any[]): Promise<boolean>;
|
|
45
|
+
|
|
46
|
+
/** Execute DDL/DML; resolves when finished. */
|
|
36
47
|
execute(sql: string, params?: any[]): Promise<void>;
|
|
48
|
+
/** Run a SELECT and return rows as plain objects. */
|
|
37
49
|
query<T = Row>(sql: string, params?: any[]): Promise<T[]>;
|
|
50
|
+
/** Run a SELECT and return the first row or null. */
|
|
38
51
|
get<T = Row>(sql: string, params?: any[]): Promise<T | null>;
|
|
52
|
+
|
|
53
|
+
/** Convenience table helper. */
|
|
39
54
|
table<T extends Row = Row>(table: string): TableHelper<T>;
|
|
55
|
+
|
|
56
|
+
/** Register/upgrade schemas. */
|
|
40
57
|
registerSchemas(schemas: Schema[]): Promise<void>;
|
|
41
|
-
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* ------------------------------------------------------------------ */
|
|
61
|
+
/* Browser-friendly convenience exports (compat with Angular consumer) */
|
|
62
|
+
/* ------------------------------------------------------------------ */
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Open (or reuse) a browser DB by name and return an AllezORM instance.
|
|
66
|
+
* Equivalent to `AllezORM.init({ dbName: name, ...opts })` with caching.
|
|
67
|
+
*/
|
|
68
|
+
export function openBrowserDb(name: string, opts?: InitOptions): Promise<AllezORM>;
|
|
69
|
+
|
|
70
|
+
/** Alias for compatibility with consumers that call `openDb`. */
|
|
71
|
+
export const openDb: typeof openBrowserDb;
|
|
72
|
+
|
|
73
|
+
/** Apply an array of Schema objects on an opened AllezORM instance. */
|
|
74
|
+
export function applySchemas(db: AllezORM, schemas: Schema[]): Promise<void>;
|
|
75
|
+
|
|
76
|
+
/** Run a SELECT and return rows (compat free functions). */
|
|
77
|
+
export function query<TRow = Row>(db: AllezORM, sql: string, params?: any[]): Promise<TRow[]>;
|
|
78
|
+
|
|
79
|
+
/** Execute DDL/DML (compat free function). */
|
|
80
|
+
export function exec(db: AllezORM, sql: string, params?: any[]): Promise<void>;
|
|
81
|
+
|
|
82
|
+
/** Keep a default export for ESM consumers. */
|
|
83
|
+
export default AllezORM;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "allez-orm",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "AllezORM: lightweight browser SQLite ORM (sql.js) + schema generator CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./allez-orm.mjs",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"exports": {
|
|
18
18
|
".": {
|
|
19
|
+
"types": "./index.d.ts",
|
|
19
20
|
"import": "./allez-orm.mjs",
|
|
20
21
|
"default": "./allez-orm.mjs"
|
|
21
22
|
},
|