@kubun/db 0.8.1 → 0.9.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.
Files changed (3) hide show
  1. package/lib/db.d.ts +14 -0
  2. package/lib/db.js +1 -1
  3. package/package.json +5 -5
package/lib/db.d.ts CHANGED
@@ -14,6 +14,20 @@ export type StoreDefinition<Tables, API> = {
14
14
  export type StoreProvider<Stores extends Record<string, unknown> = Record<string, unknown>> = {
15
15
  getStore<S extends keyof Stores & string>(name: S): Promise<Stores[S]>;
16
16
  onCommit(fn: () => void): void;
17
+ /**
18
+ * Run `fn` against a `StoreProvider` that scopes its writes to a transaction.
19
+ *
20
+ * On `KubunDB`, opens a fresh DB transaction and provides a transactional
21
+ * `tx` provider; the outer provider's `onCommit` hooks fire after commit.
22
+ *
23
+ * On a transactional `tx` provider obtained from a parent
24
+ * `withTransaction` call, this method runs `fn` inline with `this` as the
25
+ * nested `tx`. The underlying driver does not nest transactions
26
+ * (no savepoints), so atomicity is inherited from the outer transaction
27
+ * — a throw in `fn` rolls the whole outer transaction back, exactly the
28
+ * semantics callers expect for "register + bind atomically".
29
+ */
30
+ withTransaction<NestedStores extends Record<string, unknown>, R>(fn: (tx: StoreProvider<NestedStores>) => Promise<R>): Promise<R>;
17
31
  };
18
32
  export type KubunDBParams = {
19
33
  adapter: Adapter;
package/lib/db.js CHANGED
@@ -1 +1 @@
1
- import{getKubunLogger as t}from"@kubun/logger";import{Kysely as e,Migrator as r,ParseJSONResultsPlugin as i}from"kysely";export class KubunDB{#t;#e;#r;#i=new Map;#s=new Map;#a=new Map;constructor(r){this.#t=r.adapter,this.#e=new e({dialect:this.#t.dialect,plugins:[new i]}),this.#r=r.logger??t("db")}get adapter(){return this.#t}register(t){let e=this.#a.get(t.name);if(null!=e){e!==t&&this.#r.warn('store "{name}" registered with different definition, ignoring',{name:t.name});return}this.#a.set(t.name,t)}async getStore(t){if(await this.#o(t),!this.#s.has(t)){let e=this.#a.get(t);if(null==e)throw Error(`Store "${t}" is not registered`);this.#s.set(t,e.createAPI(this.#e,this.#t))}return this.#s.get(t)}onCommit(t){t()}async withTransaction(t){await Promise.all(Array.from(this.#a.keys()).map(t=>this.#o(t)));let e=[],r=await this.#e.transaction().execute(async r=>{let i=new Map;return await t({getStore:async t=>{if(!i.has(t)){let e=this.#a.get(t);if(null==e)throw Error(`Store "${t}" is not registered`);i.set(t,e.createAPI(r,this.#t))}return i.get(t)},onCommit:t=>{e.push(t)}})});for(let t of e)t();return r}async close(){await this.#e.destroy(),this.#r.info("closed")}async #o(t){let e=this.#i.get(t);return null==e&&(e=this.#n(t).catch(e=>{throw this.#i.delete(t),e}),this.#i.set(t,e)),e}async #n(t){let e=this.#a.get(t);if(null==e)throw Error(`Store "${t}" is not registered`);for(let t of e.dependsOn??[])await this.#o(t);let i="function"==typeof e.migrations?e.migrations({types:this.#t.types,functions:this.#t.functions}):e.migrations;if(0===Object.keys(i).length)return;let s=new r({db:this.#e,provider:{getMigrations:()=>Promise.resolve(i)},migrationTableName:`kubun_${t}_migration`,migrationLockTableName:`kubun_${t}_migration_lock`}),a=await s.migrateToLatest();if(null!=a.error)throw a.error;this.#r.info("store {name} migrations complete",{name:t})}}
1
+ import{getKubunLogger as t}from"@kubun/logger";import{Kysely as e,Migrator as r,ParseJSONResultsPlugin as i}from"kysely";export class KubunDB{#t;#e;#r;#i=new Map;#s=new Map;#a=new Map;constructor(r){this.#t=r.adapter,this.#e=new e({dialect:this.#t.dialect,plugins:[new i]}),this.#r=r.logger??t("db")}get adapter(){return this.#t}register(t){let e=this.#a.get(t.name);if(null!=e){e!==t&&this.#r.warn('store "{name}" registered with different definition, ignoring',{name:t.name});return}this.#a.set(t.name,t)}async getStore(t){if(await this.#o(t),!this.#s.has(t)){let e=this.#a.get(t);if(null==e)throw Error(`Store "${t}" is not registered`);this.#s.set(t,e.createAPI(this.#e,this.#t))}return this.#s.get(t)}onCommit(t){t()}async withTransaction(t){await Promise.all(Array.from(this.#a.keys()).map(t=>this.#o(t)));let e=[],r=await this.#e.transaction().execute(async r=>{let i=new Map,s={getStore:async t=>{if(!i.has(t)){let e=this.#a.get(t);if(null==e)throw Error(`Store "${t}" is not registered`);i.set(t,e.createAPI(r,this.#t))}return i.get(t)},onCommit:t=>{e.push(t)},withTransaction:async t=>await t(s)};return await t(s)});for(let t of e)t();return r}async close(){await this.#e.destroy(),this.#r.info("closed")}async #o(t){let e=this.#i.get(t);return null==e&&(e=this.#n(t).catch(e=>{throw this.#i.delete(t),e}),this.#i.set(t,e)),e}async #n(t){let e=this.#a.get(t);if(null==e)throw Error(`Store "${t}" is not registered`);for(let t of e.dependsOn??[])await this.#o(t);let i="function"==typeof e.migrations?e.migrations({types:this.#t.types,functions:this.#t.functions}):e.migrations;if(0===Object.keys(i).length)return;let s=new r({db:this.#e,provider:{getMigrations:()=>Promise.resolve(i)},migrationTableName:`kubun_${t}_migration`,migrationLockTableName:`kubun_${t}_migration_lock`}),a=await s.migrateToLatest();if(null!=a.error)throw a.error;this.#r.info("store {name} migrations complete",{name:t})}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/db",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "license": "see LICENSE.md",
5
5
  "keywords": [],
6
6
  "type": "module",
@@ -15,12 +15,12 @@
15
15
  ],
16
16
  "sideEffects": false,
17
17
  "dependencies": {
18
- "kysely": "^0.28.16",
19
- "@kubun/db-adapter": "^0.8.1",
20
- "@kubun/logger": "^0.8.0"
18
+ "kysely": "^0.28.17",
19
+ "@kubun/db-adapter": "^0.9.0",
20
+ "@kubun/logger": "^0.9.0"
21
21
  },
22
22
  "devDependencies": {
23
- "@kubun/db-better-sqlite": "^0.8.1"
23
+ "@kubun/db-better-sqlite": "^0.9.0"
24
24
  },
25
25
  "scripts": {
26
26
  "build:clean": "del lib",