allez-orm 1.0.8 → 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.
Files changed (3) hide show
  1. package/allez-orm.mjs +76 -12
  2. package/index.d.ts +43 -1
  3. 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
- /** Resolve/init sql.js WASM in a way that never triggers node core deps. */
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 (typeof window !== "undefined" && window.initSqlJs) {
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 a CDN ESM import that bundlers won't touch (no fs/path/crypto resolution).
39
- // Dynamic URL import requires CORS; the official CDN allows it.
39
+ // 2) Try CDN ESM (CORS-enabled). Bundlers wont 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) Last resort: local dist entry. This ONLY works if the consumer configured
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
- await orm.registerSchemas(schemas);
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
- await self.execute(`UPDATE ${table} SET deleted_at=? WHERE id=?`, [ts, id]);
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.8",
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",