@danceroutine/tango-orm 1.3.0 → 1.4.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 (60) hide show
  1. package/dist/PostgresAdapter-BFdo_nIt.js +4 -0
  2. package/dist/{PostgresAdapter-C9a1XJRx.js → PostgresAdapter-CMiEpHya.js} +4 -52
  3. package/dist/PostgresAdapter-CMiEpHya.js.map +1 -0
  4. package/dist/PostgresClient-BQJZfEOT.js +68 -0
  5. package/dist/PostgresClient-BQJZfEOT.js.map +1 -0
  6. package/dist/SqliteAdapter-A-P9zUhP.js +4 -0
  7. package/dist/SqliteAdapter-CeqhyrPC.js +44 -0
  8. package/dist/SqliteAdapter-CeqhyrPC.js.map +1 -0
  9. package/dist/{SqliteAdapter-Dp6VRXmz.js → SqliteClient-CjOK9-ki.js} +20 -41
  10. package/dist/SqliteClient-CjOK9-ki.js.map +1 -0
  11. package/dist/connection/clients/DBClient.d.ts +6 -0
  12. package/dist/connection/clients/dialects/PostgresClient.d.ts +21 -5
  13. package/dist/connection/clients/dialects/SqliteClient.d.ts +13 -1
  14. package/dist/connection/index.js +5 -3
  15. package/dist/{connection-CVvycXus.js → connection-B_K2ZAf7.js} +7 -5
  16. package/dist/{connection-CVvycXus.js.map → connection-B_K2ZAf7.js.map} +1 -1
  17. package/dist/defaultRuntime-BPK9kWEW.js +447 -0
  18. package/dist/defaultRuntime-BPK9kWEW.js.map +1 -0
  19. package/dist/index.d.ts +2 -1
  20. package/dist/index.js +12 -9
  21. package/dist/manager/ModelManager.d.ts +2 -0
  22. package/dist/manager/index.js +6 -6
  23. package/dist/manager/internal/RuntimeBoundClient.d.ts +5 -1
  24. package/dist/{manager-D6tU8xTO.js → manager-C6oJ2tAF.js} +2 -2
  25. package/dist/{manager-D6tU8xTO.js.map → manager-C6oJ2tAF.js.map} +1 -1
  26. package/dist/query/index.js +1 -1
  27. package/dist/{query-6GeNOf-w.js → query-CWZ1cfjo.js} +1 -1
  28. package/dist/{query-6GeNOf-w.js.map → query-CWZ1cfjo.js.map} +1 -1
  29. package/dist/{registerModelObjects-CXSI5ndy.js → registerModelObjects-Bva_f-Qh.js} +26 -118
  30. package/dist/registerModelObjects-Bva_f-Qh.js.map +1 -0
  31. package/dist/runtime/TangoRuntime.d.ts +17 -4
  32. package/dist/runtime/index.js +6 -6
  33. package/dist/runtime/internal/DBClientProvider.d.ts +12 -0
  34. package/dist/runtime/internal/PostgresDBClientProvider.d.ts +12 -0
  35. package/dist/runtime/internal/SqliteDBClientProvider.d.ts +16 -0
  36. package/dist/runtime/internal/createDBClientProvider.d.ts +5 -0
  37. package/dist/{runtime-7U5_XDad.js → runtime-ByXbpVBS.js} +3 -2
  38. package/dist/{runtime-7U5_XDad.js.map → runtime-ByXbpVBS.js.map} +1 -1
  39. package/dist/transaction/AtomicTransaction.d.ts +32 -0
  40. package/dist/transaction/UnitOfWork.d.ts +3 -0
  41. package/dist/transaction/atomic.d.ts +2 -0
  42. package/dist/transaction/index.d.ts +2 -0
  43. package/dist/transaction/index.js +5 -2
  44. package/dist/transaction/internal/context/AsyncLocalTransactionEngine.d.ts +21 -0
  45. package/dist/transaction/internal/context/CallbackRecord.d.ts +5 -0
  46. package/dist/transaction/internal/context/FrameBoundTransaction.d.ts +20 -0
  47. package/dist/transaction/internal/context/FrameTransactionHandle.d.ts +4 -0
  48. package/dist/transaction/internal/context/TransactionEngine.d.ts +16 -0
  49. package/dist/transaction/internal/context/TransactionFrame.d.ts +7 -0
  50. package/dist/transaction/internal/context/TransactionState.d.ts +10 -0
  51. package/dist/transaction/internal/context/index.d.ts +1 -0
  52. package/dist/{transaction-DooTMuAl.js → transaction-Cs0Z9tbW.js} +15 -3
  53. package/dist/transaction-Cs0Z9tbW.js.map +1 -0
  54. package/package.json +6 -6
  55. package/dist/PostgresAdapter-C9a1XJRx.js.map +0 -1
  56. package/dist/PostgresAdapter-CBc1u8eT.js +0 -3
  57. package/dist/SqliteAdapter-BJKNxCvS.js +0 -3
  58. package/dist/SqliteAdapter-Dp6VRXmz.js.map +0 -1
  59. package/dist/registerModelObjects-CXSI5ndy.js.map +0 -1
  60. package/dist/transaction-DooTMuAl.js.map +0 -1
@@ -1,8 +1,7 @@
1
- import { connectDB } from "./connection-CVvycXus.js";
2
- import { InternalRelationKind, OrmSqlSafetyAdapter, QuerySet } from "./query-6GeNOf-w.js";
1
+ import { InternalRelationKind, OrmSqlSafetyAdapter, QuerySet } from "./query-CWZ1cfjo.js";
2
+ import { RuntimeBoundClient, TransactionEngine, getTangoRuntime } from "./defaultRuntime-BPK9kWEW.js";
3
3
  import { NotFoundError } from "@danceroutine/tango-core";
4
4
  import { ModelRegistry, registerModelAugmentor } from "@danceroutine/tango-schema";
5
- import { loadConfig, loadConfigFromProjectRoot } from "@danceroutine/tango-config";
6
5
 
7
6
  //#region src/manager/internal/MutationCompiler.ts
8
7
  var MutationCompiler = class {
@@ -48,34 +47,6 @@ var MutationCompiler = class {
48
47
  }
49
48
  };
50
49
 
51
- //#endregion
52
- //#region src/manager/internal/RuntimeBoundClient.ts
53
- var RuntimeBoundClient = class {
54
- constructor(runtime) {
55
- this.runtime = runtime;
56
- }
57
- async query(sql, params) {
58
- const client = await this.runtime.getClient();
59
- return client.query(sql, params);
60
- }
61
- async begin() {
62
- const client = await this.runtime.getClient();
63
- await client.begin();
64
- }
65
- async commit() {
66
- const client = await this.runtime.getClient();
67
- await client.commit();
68
- }
69
- async rollback() {
70
- const client = await this.runtime.getClient();
71
- await client.rollback();
72
- }
73
- async close() {
74
- const client = await this.runtime.getClient();
75
- await client.close();
76
- }
77
- };
78
-
79
50
  //#endregion
80
51
  //#region src/manager/ModelManager.ts
81
52
  const sqlSafetyAdapter = new OrmSqlSafetyAdapter();
@@ -87,8 +58,10 @@ var ModelManager = class ModelManager {
87
58
  model;
88
59
  client;
89
60
  dialect;
61
+ runtime;
90
62
  constructor(model, runtime) {
91
63
  this.model = model;
64
+ this.runtime = runtime;
92
65
  this.client = new RuntimeBoundClient(runtime);
93
66
  this.dialect = runtime.getDialect();
94
67
  this.mutationCompiler = new MutationCompiler(this.dialect);
@@ -174,7 +147,8 @@ var ModelManager = class ModelManager {
174
147
  await this.model.hooks?.afterCreate?.({
175
148
  record: created,
176
149
  model: this.model,
177
- manager: this
150
+ manager: this,
151
+ transaction: this.getHookTransaction()
178
152
  });
179
153
  return created;
180
154
  }
@@ -197,7 +171,8 @@ var ModelManager = class ModelManager {
197
171
  previous: current,
198
172
  record: updated,
199
173
  model: this.model,
200
- manager: this
174
+ manager: this,
175
+ transaction: this.getHookTransaction()
201
176
  });
202
177
  return updated;
203
178
  }
@@ -207,7 +182,8 @@ var ModelManager = class ModelManager {
207
182
  id,
208
183
  current,
209
184
  model: this.model,
210
- manager: this
185
+ manager: this,
186
+ transaction: this.getHookTransaction()
211
187
  });
212
188
  const validatedPlan = sqlSafetyAdapter.validate({
213
189
  kind: "delete",
@@ -219,7 +195,8 @@ var ModelManager = class ModelManager {
219
195
  id,
220
196
  previous: current,
221
197
  model: this.model,
222
- manager: this
198
+ manager: this,
199
+ transaction: this.getHookTransaction()
223
200
  });
224
201
  }
225
202
  async bulkCreate(inputs) {
@@ -228,7 +205,8 @@ var ModelManager = class ModelManager {
228
205
  const batchPrepared = await this.model.hooks?.beforeBulkCreate?.({
229
206
  rows: perRowPrepared,
230
207
  model: this.model,
231
- manager: this
208
+ manager: this,
209
+ transaction: this.getHookTransaction()
232
210
  }) ?? perRowPrepared;
233
211
  const preparedKeys = Object.keys(batchPrepared[0] ?? {});
234
212
  if (preparedKeys.length === 0) throw new Error(`Cannot create ${this.model.metadata.name} without any values.`);
@@ -243,12 +221,14 @@ var ModelManager = class ModelManager {
243
221
  await Promise.all(result.rows.map((record) => this.model.hooks?.afterCreate?.({
244
222
  record,
245
223
  model: this.model,
246
- manager: this
224
+ manager: this,
225
+ transaction: this.getHookTransaction()
247
226
  })));
248
227
  await this.model.hooks?.afterBulkCreate?.({
249
228
  records: result.rows,
250
229
  model: this.model,
251
- manager: this
230
+ manager: this,
231
+ transaction: this.getHookTransaction()
252
232
  });
253
233
  return result.rows;
254
234
  }
@@ -256,7 +236,8 @@ var ModelManager = class ModelManager {
256
236
  return await this.model.hooks?.beforeCreate?.({
257
237
  data,
258
238
  model: this.model,
259
- manager: this
239
+ manager: this,
240
+ transaction: this.getHookTransaction()
260
241
  }) ?? data;
261
242
  }
262
243
  async runBeforeUpdate(id, patch, current) {
@@ -265,88 +246,15 @@ var ModelManager = class ModelManager {
265
246
  patch,
266
247
  current,
267
248
  model: this.model,
268
- manager: this
249
+ manager: this,
250
+ transaction: this.getHookTransaction()
269
251
  }) ?? patch;
270
252
  }
271
- };
272
-
273
- //#endregion
274
- //#region src/runtime/TangoRuntime.ts
275
- var TangoRuntime = class TangoRuntime {
276
- static BRAND = "tango.orm.runtime";
277
- __tangoBrand = TangoRuntime.BRAND;
278
- loadedConfig;
279
- clientPromise = null;
280
- constructor(loadLoadedConfig) {
281
- this.loadedConfig = loadLoadedConfig();
282
- }
283
- /**
284
- * Narrow an unknown value to `TangoRuntime`.
285
- */
286
- static isTangoRuntime(value) {
287
- return typeof value === "object" && value !== null && value.__tangoBrand === TangoRuntime.BRAND;
288
- }
289
- /**
290
- * Return the loaded Tango config snapshot for the active environment.
291
- */
292
- getConfig() {
293
- return this.loadedConfig;
294
- }
295
- /**
296
- * Return the configured SQL dialect for the current runtime.
297
- */
298
- getDialect() {
299
- return this.loadedConfig.current.db.adapter;
300
- }
301
- /**
302
- * Return the shared DB client, creating it once on first access.
303
- */
304
- async getClient() {
305
- if (!this.clientPromise) {
306
- const db = this.loadedConfig.current.db;
307
- this.clientPromise = connectDB({
308
- adapter: db.adapter,
309
- url: db.url,
310
- host: db.host,
311
- port: db.port,
312
- database: db.database,
313
- user: db.user,
314
- password: db.password,
315
- filename: db.filename,
316
- maxConnections: db.maxConnections
317
- });
318
- }
319
- return this.clientPromise;
320
- }
321
- /**
322
- * Close and clear the cached DB client so tests can start fresh.
323
- */
324
- async reset() {
325
- if (!this.clientPromise) return;
326
- const client = await this.clientPromise;
327
- this.clientPromise = null;
328
- await client.close();
253
+ getHookTransaction() {
254
+ return TransactionEngine.forRuntime(this.runtime).getActiveTransaction();
329
255
  }
330
256
  };
331
257
 
332
- //#endregion
333
- //#region src/runtime/defaultRuntime.ts
334
- let defaultRuntime = null;
335
- function initializeTangoRuntime(fromFile) {
336
- defaultRuntime = new TangoRuntime(() => loadConfig(fromFile));
337
- return defaultRuntime;
338
- }
339
- function getTangoRuntime() {
340
- if (!defaultRuntime) defaultRuntime = new TangoRuntime(() => loadConfigFromProjectRoot());
341
- return defaultRuntime;
342
- }
343
- async function resetTangoRuntime() {
344
- if (!defaultRuntime) return;
345
- const runtime = defaultRuntime;
346
- defaultRuntime = null;
347
- await runtime.reset();
348
- }
349
-
350
258
  //#endregion
351
259
  //#region src/manager/registerModelObjects.ts
352
260
  const managerCache = new WeakMap();
@@ -375,5 +283,5 @@ function registerModelObjects() {
375
283
  }
376
284
 
377
285
  //#endregion
378
- export { ModelManager, TangoRuntime, getTangoRuntime, initializeTangoRuntime, registerModelObjects, resetTangoRuntime };
379
- //# sourceMappingURL=registerModelObjects-CXSI5ndy.js.map
286
+ export { ModelManager, registerModelObjects };
287
+ //# sourceMappingURL=registerModelObjects-Bva_f-Qh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registerModelObjects-Bva_f-Qh.js","names":["dialect: Dialect","plan: ValidatedInsertSqlPlan","values: readonly unknown[]","plan: ValidatedUpdateSqlPlan","id: unknown","plan: ValidatedDeleteSqlPlan","valueRows: ReadonlyArray<ReadonlyArray<unknown>>","count: number","index: number","model: ModelLike<TModelRow>","runtime: TangoRuntime","value: unknown","rawMeta: TableMeta","id: TModelRow[keyof TModelRow]","input: Partial<TModelRow>","patch: Partial<TModelRow>","inputs: Partial<TModelRow>[]","batchPrepared: Partial<TModelRow>[]","data: Partial<TModelRow>","current: TModelRow","model: SchemaModel<TSchema, TKey>"],"sources":["../src/manager/internal/MutationCompiler.ts","../src/manager/ModelManager.ts","../src/manager/registerModelObjects.ts"],"sourcesContent":["import type { CompiledQuery, Dialect } from '../../query/domain/index';\nimport type {\n ValidatedDeleteSqlPlan,\n ValidatedInsertSqlPlan,\n ValidatedUpdateSqlPlan,\n} from '../../validation/SQLValidationEngine';\n\n/**\n * Internal compiler for manager-owned INSERT/UPDATE/DELETE statements.\n */\nexport class MutationCompiler {\n constructor(private readonly dialect: Dialect) {}\n\n compileInsert(plan: ValidatedInsertSqlPlan, values: readonly unknown[]): CompiledQuery {\n return {\n sql: `INSERT INTO ${plan.meta.table} (${plan.writeKeys.join(', ')}) VALUES (${this.buildValuePlaceholders(plan.writeKeys.length)}) RETURNING *`,\n params: values,\n };\n }\n\n compileUpdate(plan: ValidatedUpdateSqlPlan, values: readonly unknown[], id: unknown): CompiledQuery {\n const sets = plan.writeKeys.map((key, index) => `${key} = ${this.placeholder(index + 1)}`).join(', ');\n const whereParam = this.placeholder(plan.writeKeys.length + 1);\n\n return {\n sql: `UPDATE ${plan.meta.table} SET ${sets} WHERE ${plan.meta.pk} = ${whereParam} RETURNING *`,\n params: [...values, id],\n };\n }\n\n compileDelete(plan: ValidatedDeleteSqlPlan, id: unknown): CompiledQuery {\n return {\n sql: `DELETE FROM ${plan.meta.table} WHERE ${plan.meta.pk} = ${this.placeholder(1)}`,\n params: [id],\n };\n }\n\n compileBulkInsert(plan: ValidatedInsertSqlPlan, valueRows: ReadonlyArray<ReadonlyArray<unknown>>): CompiledQuery {\n const columnCount = plan.writeKeys.length;\n const placeholders = valueRows\n .map((_row, rowIndex) => {\n const offset = rowIndex * columnCount;\n return `(${plan.writeKeys.map((_, colIndex) => this.placeholder(offset + colIndex + 1)).join(', ')})`;\n })\n .join(', ');\n\n return {\n sql: `INSERT INTO ${plan.meta.table} (${plan.writeKeys.join(', ')}) VALUES ${placeholders} RETURNING *`,\n params: valueRows.flat(),\n };\n }\n\n private buildValuePlaceholders(count: number): string {\n return Array.from({ length: count }, (_value, index) => this.placeholder(index + 1)).join(', ');\n }\n\n private placeholder(index: number): string {\n return this.dialect === 'postgres' ? `$${index}` : '?';\n }\n}\n","import { NotFoundError } from '@danceroutine/tango-core';\nimport { ModelRegistry, type ModelWriteHooks } from '@danceroutine/tango-schema';\nimport type { FilterInput, RelationMeta, TableMeta } from '../query/domain/index';\nimport { InternalRelationKind } from '../query/domain/internal/InternalRelationKind';\nimport type { QuerySet } from '../query/index';\nimport { QuerySet as QuerySetClass } from '../query/index';\nimport type { Dialect, QueryExecutor } from '../query/index';\nimport type { TangoRuntime } from '../runtime/TangoRuntime';\nimport { OrmSqlSafetyAdapter } from '../validation';\nimport { TransactionEngine } from '../transaction/internal/context';\nimport type { ManagerLike } from './ManagerLike';\nimport { MutationCompiler } from './internal/MutationCompiler';\nimport { RuntimeBoundClient } from './internal/RuntimeBoundClient';\n\nconst sqlSafetyAdapter = new OrmSqlSafetyAdapter();\n\ntype FieldLike = {\n name: string;\n type: string;\n primaryKey?: boolean;\n};\n\ntype ModelLike<TModelRow extends Record<string, unknown>> = {\n metadata: {\n key?: string;\n name: string;\n table: string;\n fields: FieldLike[];\n };\n schema: {\n parse(input: unknown): TModelRow;\n };\n hooks?: ModelWriteHooks<TModelRow>;\n};\n\n/**\n * Model-backed data access API exposed as `Model.objects`.\n */\nexport class ModelManager<TModelRow extends Record<string, unknown>, TSourceModel = unknown>\n implements ManagerLike<TModelRow, TSourceModel>\n{\n static readonly BRAND = 'tango.orm.model_manager' as const;\n readonly __tangoBrand: typeof ModelManager.BRAND = ModelManager.BRAND;\n private readonly queryExecutor: QueryExecutor<TModelRow>;\n private readonly mutationCompiler: MutationCompiler;\n private readonly model: ModelLike<TModelRow>;\n private readonly client: RuntimeBoundClient;\n private readonly dialect: Dialect;\n private readonly runtime: TangoRuntime;\n\n constructor(model: ModelLike<TModelRow>, runtime: TangoRuntime) {\n this.model = model;\n this.runtime = runtime;\n this.client = new RuntimeBoundClient(runtime);\n this.dialect = runtime.getDialect() as Dialect;\n this.mutationCompiler = new MutationCompiler(this.dialect);\n this.queryExecutor = {\n get meta() {\n return ModelManager.createTableMeta(model);\n },\n client: this.client,\n dialect: this.dialect,\n run: async (compiled) => {\n const result = await this.client.query<TModelRow>(compiled.sql, compiled.params);\n return result.rows;\n },\n };\n }\n\n get meta(): TableMeta {\n return ModelManager.createTableMeta(this.model);\n }\n\n /**\n * Narrow an unknown value to `ModelManager`.\n */\n static isModelManager<TModelRow extends Record<string, unknown>>(value: unknown): value is ModelManager<TModelRow> {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === ModelManager.BRAND\n );\n }\n\n private static createTableMeta<TModelRow extends Record<string, unknown>>(model: ModelLike<TModelRow>): TableMeta {\n const pkField = model.metadata.fields.find((field) => field.primaryKey);\n if (!pkField) {\n throw new Error(`Model '${model.metadata.name}' cannot attach a manager without a primary key field.`);\n }\n\n const rawMeta: TableMeta = {\n table: model.metadata.table,\n pk: pkField.name,\n columns: Object.fromEntries(model.metadata.fields.map((field) => [field.name, field.type])),\n };\n\n if (model.metadata.key) {\n const owner = ModelRegistry.getOwner(model as never);\n const relations = owner.getResolvedRelationGraph().byModel.get(model.metadata.key);\n if (relations && relations.size > 0) {\n // This metadata is recomputed on access today. Most application tables have a small relation set, so\n // keeping this path direct is acceptable until profiling points to a dedicated manager-meta cache.\n rawMeta.relations = Object.fromEntries(\n Array.from(relations.entries())\n .filter(([, relation]) => relation.kind !== InternalRelationKind.MANY_TO_MANY)\n .map(([name, relation]) => {\n const targetModel = owner.getByKey(relation.targetModelKey)!;\n // Query planning needs the target model's SQL-facing shape so joined and prefetched rows can be\n // selected, normalized, and attached without leaking internal alias columns.\n const targetColumns = Object.fromEntries(\n targetModel.metadata.fields.map((field) => [field.name, field.type])\n );\n // Forward relations join from this table's FK to the target key. Reverse relations flip that:\n // this table's primary key matches the target model's FK back to this model.\n const sourceKey =\n relation.kind === InternalRelationKind.BELONGS_TO\n ? relation.localFieldName\n : relation.targetFieldName;\n const targetKey =\n relation.kind === InternalRelationKind.BELONGS_TO\n ? relation.targetFieldName\n : relation.localFieldName;\n\n return [\n name,\n {\n kind: relation.kind as RelationMeta['kind'],\n table: targetModel.metadata.table,\n sourceKey: sourceKey as string,\n targetKey: targetKey as string,\n targetColumns,\n alias: relation.alias,\n },\n ];\n })\n );\n }\n }\n\n const validatedMeta = sqlSafetyAdapter.validate({\n kind: 'insert',\n meta: rawMeta,\n writeKeys: Object.keys(rawMeta.columns),\n }).meta;\n\n if (rawMeta.relations) {\n validatedMeta.relations = rawMeta.relations;\n }\n\n return validatedMeta;\n }\n\n query(): QuerySet<TModelRow, TModelRow, TSourceModel> {\n return new QuerySetClass<TModelRow, TModelRow, TSourceModel>(this.queryExecutor, {});\n }\n\n async findById(id: TModelRow[keyof TModelRow]): Promise<TModelRow | null> {\n const filter = { [this.meta.pk]: id } as unknown as FilterInput<TModelRow>;\n return this.query().filter(filter).fetchOne();\n }\n\n async getOrThrow(id: TModelRow[keyof TModelRow]): Promise<TModelRow> {\n const result = await this.findById(id);\n if (!result) {\n throw new NotFoundError(`${this.model.metadata.name} with ${this.meta.pk}=${String(id)} not found`);\n }\n return result;\n }\n\n async create(input: Partial<TModelRow>): Promise<TModelRow> {\n const prepared = await this.runBeforeCreate(input);\n const preparedKeys = Object.keys(prepared);\n if (preparedKeys.length === 0) {\n throw new Error(`Cannot create ${this.model.metadata.name} without any values.`);\n }\n\n const validatedPlan = sqlSafetyAdapter.validate({\n kind: 'insert',\n meta: this.meta,\n writeKeys: preparedKeys,\n });\n const compiled = this.mutationCompiler.compileInsert(\n validatedPlan,\n preparedKeys.map((key) => prepared[key as keyof TModelRow])\n );\n const result = await this.queryExecutor.client.query<TModelRow>(compiled.sql, compiled.params);\n const created = result.rows[0]!;\n await this.model.hooks?.afterCreate?.({\n record: created,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n });\n return created;\n }\n\n async update(id: TModelRow[keyof TModelRow], patch: Partial<TModelRow>): Promise<TModelRow> {\n const current = await this.getOrThrow(id);\n const prepared = await this.runBeforeUpdate(id, patch, current);\n const preparedKeys = Object.keys(prepared);\n if (preparedKeys.length === 0) {\n throw new Error(`Cannot update ${this.model.metadata.name} without any values.`);\n }\n\n const validatedPlan = sqlSafetyAdapter.validate({\n kind: 'update',\n meta: this.meta,\n writeKeys: preparedKeys,\n });\n const compiled = this.mutationCompiler.compileUpdate(\n validatedPlan,\n preparedKeys.map((key) => prepared[key as keyof TModelRow]),\n id\n );\n const result = await this.queryExecutor.client.query<TModelRow>(compiled.sql, compiled.params);\n const updated = result.rows[0]!;\n await this.model.hooks?.afterUpdate?.({\n id,\n patch: prepared,\n previous: current,\n record: updated,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n });\n return updated;\n }\n\n async delete(id: TModelRow[keyof TModelRow]): Promise<void> {\n const current = await this.getOrThrow(id);\n await this.model.hooks?.beforeDelete?.({\n id,\n current,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n });\n const validatedPlan = sqlSafetyAdapter.validate({\n kind: 'delete',\n meta: this.meta,\n });\n const compiled = this.mutationCompiler.compileDelete(validatedPlan, id);\n await this.queryExecutor.client.query(compiled.sql, compiled.params);\n await this.model.hooks?.afterDelete?.({\n id,\n previous: current,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n });\n }\n\n async bulkCreate(inputs: Partial<TModelRow>[]): Promise<TModelRow[]> {\n if (inputs.length === 0) {\n return [];\n }\n\n const perRowPrepared = await Promise.all(inputs.map((input) => this.runBeforeCreate(input)));\n const batchPrepared: Partial<TModelRow>[] =\n (await this.model.hooks?.beforeBulkCreate?.({\n rows: perRowPrepared,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n })) ?? perRowPrepared;\n const preparedKeys = Object.keys(batchPrepared[0] ?? {});\n if (preparedKeys.length === 0) {\n throw new Error(`Cannot create ${this.model.metadata.name} without any values.`);\n }\n\n const validatedPlan = sqlSafetyAdapter.validate({\n kind: 'insert',\n meta: this.meta,\n writeKeys: preparedKeys,\n });\n const valueRows = batchPrepared.map((input) => preparedKeys.map((key) => input[key as keyof TModelRow]));\n const compiled = this.mutationCompiler.compileBulkInsert(validatedPlan, valueRows);\n const result = await this.queryExecutor.client.query<TModelRow>(compiled.sql, compiled.params);\n await Promise.all(\n result.rows.map((record) =>\n this.model.hooks?.afterCreate?.({\n record,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n })\n )\n );\n await this.model.hooks?.afterBulkCreate?.({\n records: result.rows,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n });\n return result.rows;\n }\n\n private async runBeforeCreate(data: Partial<TModelRow>): Promise<Partial<TModelRow>> {\n return (\n (await this.model.hooks?.beforeCreate?.({\n data,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n })) ?? data\n );\n }\n\n private async runBeforeUpdate(\n id: TModelRow[keyof TModelRow],\n patch: Partial<TModelRow>,\n current: TModelRow\n ): Promise<Partial<TModelRow>> {\n return (\n (await this.model.hooks?.beforeUpdate?.({\n id,\n patch,\n current,\n model: this.model,\n manager: this,\n transaction: this.getHookTransaction(),\n })) ?? patch\n );\n }\n\n private getHookTransaction() {\n return TransactionEngine.forRuntime(this.runtime).getActiveTransaction();\n }\n}\n","import type { z } from 'zod';\nimport type { Model as SchemaModel, PersistedModelOutput } from '@danceroutine/tango-schema/domain';\nimport { registerModelAugmentor } from '@danceroutine/tango-schema';\nimport { ModelManager } from './ModelManager';\nimport type { TangoRuntime } from '../runtime/TangoRuntime';\nimport { getTangoRuntime } from '../runtime/defaultRuntime';\n\nconst managerCache = new WeakMap<object, { runtime: TangoRuntime; manager: ModelManager<Record<string, unknown>> }>();\nlet hasRegisteredModelObjects = false;\n\ntype AugmentableSchemaModel<TSchema extends z.ZodObject<z.ZodRawShape>> = {\n metadata: {\n key?: string;\n name: string;\n table: string;\n fields: Array<{ name: string; type: string; primaryKey?: boolean }>;\n };\n schema: {\n parse(input: unknown): PersistedModelOutput<TSchema>;\n };\n hooks?: SchemaModel<TSchema>['hooks'];\n};\n\nfunction defineObjectsProperty<TSchema extends z.ZodObject<z.ZodRawShape>, TKey extends string>(\n model: SchemaModel<TSchema, TKey>\n): void {\n Object.defineProperty(model, 'objects', {\n configurable: true,\n enumerable: true,\n get() {\n const runtime = getTangoRuntime();\n const cached = managerCache.get(model);\n if (cached && cached.runtime === runtime) {\n return cached.manager;\n }\n\n const manager = new ModelManager<PersistedModelOutput<TSchema>, SchemaModel<TSchema, TKey>>(\n model as unknown as AugmentableSchemaModel<TSchema>,\n runtime\n );\n managerCache.set(model, {\n runtime,\n manager: manager as ModelManager<Record<string, unknown>>,\n });\n return manager;\n },\n });\n}\n\n/**\n * Install the schema model augmentor that exposes `Model.objects`.\n * This registration is idempotent so multiple Tango entrypoints can safely call it.\n */\nexport function registerModelObjects(): void {\n if (hasRegisteredModelObjects) {\n return;\n }\n\n registerModelAugmentor(defineObjectsProperty);\n hasRegisteredModelObjects = true;\n}\n"],"mappings":"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"}
@@ -1,15 +1,17 @@
1
1
  import type { LoadedConfig } from '@danceroutine/tango-config';
2
2
  import type { DBClient } from '../connection/index';
3
3
  import type { Dialect } from '../query/domain/index';
4
+ import type { TransactionClientLease } from './internal/DBClientProvider';
4
5
  /**
5
6
  * Framework-owned database runtime that resolves Tango config and lazily
6
- * creates the shared DB client used by manager-backed models.
7
+ * creates the shared connection provider used by manager-backed models.
7
8
  */
8
9
  export declare class TangoRuntime {
9
10
  static readonly BRAND: "tango.orm.runtime";
10
11
  readonly __tangoBrand: typeof TangoRuntime.BRAND;
11
12
  private readonly loadedConfig;
12
- private clientPromise;
13
+ private providerPromise;
14
+ private runtimeClientPromise;
13
15
  constructor(loadLoadedConfig: () => LoadedConfig);
14
16
  /**
15
17
  * Narrow an unknown value to `TangoRuntime`.
@@ -24,11 +26,22 @@ export declare class TangoRuntime {
24
26
  */
25
27
  getDialect(): Dialect;
26
28
  /**
27
- * Return the shared DB client, creating it once on first access.
29
+ * Return the runtime-bound DB client facade used by manager-backed code.
28
30
  */
29
31
  getClient(): Promise<DBClient>;
30
32
  /**
31
- * Close and clear the cached DB client so tests can start fresh.
33
+ * Execute SQL through the autocommit path owned by this runtime.
34
+ */
35
+ query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{
36
+ rows: T[];
37
+ }>;
38
+ /**
39
+ * Lease a transaction-scoped client for `transaction.atomic(...)`.
40
+ */
41
+ leaseTransactionClient(): Promise<TransactionClientLease>;
42
+ /**
43
+ * Close and clear the cached runtime resources so tests can start fresh.
32
44
  */
33
45
  reset(): Promise<void>;
46
+ private getProvider;
34
47
  }
@@ -1,8 +1,8 @@
1
- import "../PostgresAdapter-C9a1XJRx.js";
2
- import "../SqliteAdapter-Dp6VRXmz.js";
3
- import "../connection-CVvycXus.js";
4
- import "../query-6GeNOf-w.js";
5
- import { TangoRuntime, getTangoRuntime, initializeTangoRuntime, registerModelObjects, resetTangoRuntime } from "../registerModelObjects-CXSI5ndy.js";
6
- import "../runtime-7U5_XDad.js";
1
+ import "../PostgresClient-BQJZfEOT.js";
2
+ import "../SqliteClient-CjOK9-ki.js";
3
+ import "../query-CWZ1cfjo.js";
4
+ import { TangoRuntime, getTangoRuntime, initializeTangoRuntime, resetTangoRuntime } from "../defaultRuntime-BPK9kWEW.js";
5
+ import { registerModelObjects } from "../registerModelObjects-Bva_f-Qh.js";
6
+ import "../runtime-ByXbpVBS.js";
7
7
 
8
8
  export { TangoRuntime, getTangoRuntime, initializeTangoRuntime, registerModelObjects, resetTangoRuntime };
@@ -0,0 +1,12 @@
1
+ import type { DBClient } from '../../connection/clients/DBClient';
2
+ export interface TransactionClientLease {
3
+ readonly client: DBClient;
4
+ release(): Promise<void>;
5
+ }
6
+ export interface DBClientProvider {
7
+ query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{
8
+ rows: T[];
9
+ }>;
10
+ leaseTransactionClient(): Promise<TransactionClientLease>;
11
+ reset(): Promise<void>;
12
+ }
@@ -0,0 +1,12 @@
1
+ import type { AdapterConfig } from '../../connection/adapters/Adapter';
2
+ import type { DBClientProvider, TransactionClientLease } from './DBClientProvider';
3
+ export declare class PostgresDBClientProvider implements DBClientProvider {
4
+ private readonly pool;
5
+ private activeLeaseCount;
6
+ constructor(config: AdapterConfig);
7
+ query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{
8
+ rows: T[];
9
+ }>;
10
+ leaseTransactionClient(): Promise<TransactionClientLease>;
11
+ reset(): Promise<void>;
12
+ }
@@ -0,0 +1,16 @@
1
+ import type { AdapterConfig } from '../../connection/adapters/Adapter';
2
+ import type { DBClientProvider, TransactionClientLease } from './DBClientProvider';
3
+ export declare class SqliteDBClientProvider implements DBClientProvider {
4
+ private readonly filename;
5
+ private readonly Database;
6
+ private readonly autocommitClient;
7
+ private activeLeaseCount;
8
+ constructor(config?: AdapterConfig);
9
+ query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{
10
+ rows: T[];
11
+ }>;
12
+ leaseTransactionClient(): Promise<TransactionClientLease>;
13
+ reset(): Promise<void>;
14
+ private openClient;
15
+ private getDatabaseCtor;
16
+ }
@@ -0,0 +1,5 @@
1
+ import type { AdapterConfig } from '../../connection/adapters/Adapter';
2
+ import type { DBClientProvider } from './DBClientProvider';
3
+ export declare function createDBClientProvider(config: AdapterConfig & {
4
+ adapter: string;
5
+ }): DBClientProvider;
@@ -1,5 +1,6 @@
1
1
  import { __export } from "./chunk-DLY2FNSh.js";
2
- import { TangoRuntime, getTangoRuntime, initializeTangoRuntime, registerModelObjects, resetTangoRuntime } from "./registerModelObjects-CXSI5ndy.js";
2
+ import { TangoRuntime, getTangoRuntime, initializeTangoRuntime, resetTangoRuntime } from "./defaultRuntime-BPK9kWEW.js";
3
+ import { registerModelObjects } from "./registerModelObjects-Bva_f-Qh.js";
3
4
 
4
5
  //#region src/runtime/index.ts
5
6
  var runtime_exports = {};
@@ -14,4 +15,4 @@ registerModelObjects();
14
15
 
15
16
  //#endregion
16
17
  export { runtime_exports };
17
- //# sourceMappingURL=runtime-7U5_XDad.js.map
18
+ //# sourceMappingURL=runtime-ByXbpVBS.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-7U5_XDad.js","names":[],"sources":["../src/runtime/index.ts"],"sourcesContent":["import type { z } from 'zod';\nimport type { ModelManager } from '../manager/ModelManager';\nimport type { Model, PersistedModelOutput } from '@danceroutine/tango-schema/domain';\nimport { registerModelObjects } from '../manager/registerModelObjects';\n\n/**\n * Domain boundary barrel: centralizes Tango runtime ownership APIs.\n */\n\ndeclare global {\n interface TangoSchemaModelAugmentations<\n TSchema extends z.ZodObject<z.ZodRawShape> = z.ZodObject<z.ZodRawShape>,\n TKey extends string = string,\n > {\n readonly objects: ModelManager<PersistedModelOutput<TSchema>, Model<TSchema, TKey>>;\n }\n}\n\nregisterModelObjects();\n\nexport { registerModelObjects } from '../manager/registerModelObjects';\nexport { TangoRuntime } from './TangoRuntime';\nexport { getTangoRuntime, initializeTangoRuntime, resetTangoRuntime } from './defaultRuntime';\n"],"mappings":";;;;;;;;;;;;AAkBA,sBAAsB"}
1
+ {"version":3,"file":"runtime-ByXbpVBS.js","names":[],"sources":["../src/runtime/index.ts"],"sourcesContent":["import type { z } from 'zod';\nimport type { ModelManager } from '../manager/ModelManager';\nimport type { Model, PersistedModelOutput } from '@danceroutine/tango-schema/domain';\nimport { registerModelObjects } from '../manager/registerModelObjects';\n\n/**\n * Domain boundary barrel: centralizes Tango runtime ownership APIs.\n */\n\ndeclare global {\n interface TangoSchemaModelAugmentations<\n TSchema extends z.ZodObject<z.ZodRawShape> = z.ZodObject<z.ZodRawShape>,\n TKey extends string = string,\n > {\n readonly objects: ModelManager<PersistedModelOutput<TSchema>, Model<TSchema, TKey>>;\n }\n}\n\nregisterModelObjects();\n\nexport { registerModelObjects } from '../manager/registerModelObjects';\nexport { TangoRuntime } from './TangoRuntime';\nexport { getTangoRuntime, initializeTangoRuntime, resetTangoRuntime } from './defaultRuntime';\n"],"mappings":";;;;;;;;;;;;;AAkBA,sBAAsB"}
@@ -0,0 +1,32 @@
1
+ export interface OnCommitOptions {
2
+ robust?: boolean;
3
+ }
4
+ export interface SavepointOptions {
5
+ throwOnError?: boolean;
6
+ }
7
+ /**
8
+ * Result returned by `tx.savepoint(...)`.
9
+ */
10
+ export type SavepointResult<T> = {
11
+ ok: true;
12
+ value: T;
13
+ } | {
14
+ ok: false;
15
+ error: unknown;
16
+ };
17
+ export interface AtomicTransaction {
18
+ /**
19
+ * Register a callback that should run only after the outermost transaction commit succeeds.
20
+ */
21
+ onCommit(callback: () => void, options?: OnCommitOptions): void;
22
+ /**
23
+ * Run work inside a nested savepoint and, by default, capture rollback as a result object instead of throwing.
24
+ */
25
+ savepoint<T>(work: (tx: AtomicTransaction) => Promise<T> | T): Promise<SavepointResult<T>>;
26
+ savepoint<T>(work: (tx: AtomicTransaction) => Promise<T> | T, options: {
27
+ throwOnError: false;
28
+ }): Promise<SavepointResult<T>>;
29
+ savepoint<T>(work: (tx: AtomicTransaction) => Promise<T> | T, options: {
30
+ throwOnError: true;
31
+ }): Promise<T>;
32
+ }
@@ -15,6 +15,9 @@ import type { DBClient } from '../connection/clients/DBClient';
15
15
  * throw error;
16
16
  * }
17
17
  * ```
18
+ *
19
+ * @deprecated Use `transaction.atomic(async (tx) => { ... })` for application
20
+ * transaction workflows. `UnitOfWork` remains exported only for compatibility.
18
21
  */
19
22
  export declare class UnitOfWork {
20
23
  static readonly BRAND: "tango.orm.unit_of_work";
@@ -0,0 +1,2 @@
1
+ import type { AtomicTransaction } from './AtomicTransaction';
2
+ export declare function atomic<T>(work: (tx: AtomicTransaction) => Promise<T> | T): Promise<T>;
@@ -1,4 +1,6 @@
1
1
  /**
2
2
  * Domain boundary barrel: centralizes this subdomain's public contract.
3
3
  */
4
+ export { atomic } from './atomic';
5
+ export type { AtomicTransaction, OnCommitOptions, SavepointOptions, SavepointResult } from './AtomicTransaction';
4
6
  export { UnitOfWork } from './UnitOfWork';
@@ -1,3 +1,6 @@
1
- import { UnitOfWork } from "../transaction-DooTMuAl.js";
1
+ import "../PostgresClient-BQJZfEOT.js";
2
+ import "../SqliteClient-CjOK9-ki.js";
3
+ import "../defaultRuntime-BPK9kWEW.js";
4
+ import { UnitOfWork, atomic } from "../transaction-Cs0Z9tbW.js";
2
5
 
3
- export { UnitOfWork };
6
+ export { UnitOfWork, atomic };
@@ -0,0 +1,21 @@
1
+ import type { TangoRuntime } from '../../../runtime/TangoRuntime';
2
+ import type { TransactionClientLease } from '../../../runtime/internal/DBClientProvider';
3
+ import type { AtomicTransaction, SavepointOptions, SavepointResult } from '../../AtomicTransaction';
4
+ import type { TransactionState } from './TransactionState';
5
+ export declare class AsyncLocalTransactionEngine {
6
+ private readonly logger;
7
+ private readonly storage;
8
+ assertNoActiveAtomicTransaction(): void;
9
+ getActiveTransaction(runtime: TangoRuntime): AtomicTransaction | undefined;
10
+ getActiveLease(runtime: TangoRuntime): TransactionClientLease | undefined;
11
+ atomic<T>(runtime: TangoRuntime, work: (tx: AtomicTransaction) => Promise<T> | T): Promise<T>;
12
+ runSavepoint<T>(state: TransactionState, work: (tx: AtomicTransaction) => Promise<T> | T, options: SavepointOptions): Promise<T | SavepointResult<T>>;
13
+ private runNested;
14
+ private pushFrame;
15
+ private popFrame;
16
+ private rollbackOuter;
17
+ private runCommittedCallbacks;
18
+ private deactivateAllFrames;
19
+ private attachCause;
20
+ private isErrorValue;
21
+ }
@@ -0,0 +1,5 @@
1
+ export type CallbackRecord = {
2
+ order: number;
3
+ callback: () => void;
4
+ robust: boolean;
5
+ };
@@ -0,0 +1,20 @@
1
+ import type { AtomicTransaction, OnCommitOptions, SavepointResult } from '../../AtomicTransaction';
2
+ import type { AsyncLocalTransactionEngine } from './AsyncLocalTransactionEngine';
3
+ import type { TransactionFrame } from './TransactionFrame';
4
+ import type { TransactionState } from './TransactionState';
5
+ export declare class FrameBoundTransaction implements AtomicTransaction {
6
+ private readonly engine;
7
+ private readonly state;
8
+ private readonly frame;
9
+ private active;
10
+ constructor(engine: AsyncLocalTransactionEngine, state: TransactionState, frame: TransactionFrame);
11
+ onCommit(callback: () => void, options?: OnCommitOptions): void;
12
+ savepoint<T>(work: (tx: AtomicTransaction) => Promise<T> | T): Promise<SavepointResult<T>>;
13
+ savepoint<T>(work: (tx: AtomicTransaction) => Promise<T> | T, options: {
14
+ throwOnError: false;
15
+ }): Promise<SavepointResult<T>>;
16
+ savepoint<T>(work: (tx: AtomicTransaction) => Promise<T> | T, options: {
17
+ throwOnError: true;
18
+ }): Promise<T>;
19
+ deactivate(): void;
20
+ }
@@ -0,0 +1,4 @@
1
+ import type { AtomicTransaction } from '../../AtomicTransaction';
2
+ export interface FrameTransactionHandle extends AtomicTransaction {
3
+ deactivate(): void;
4
+ }
@@ -0,0 +1,16 @@
1
+ import type { TangoRuntime } from '../../../runtime/TangoRuntime';
2
+ import type { TransactionClientLease } from '../../../runtime/internal/DBClientProvider';
3
+ import type { AtomicTransaction } from '../../AtomicTransaction';
4
+ /**
5
+ * Runtime-bound transaction facade used by internal ORM/runtime components.
6
+ */
7
+ export declare class TransactionEngine {
8
+ private readonly runtime;
9
+ private static readonly engine;
10
+ private constructor();
11
+ static forRuntime(runtime: TangoRuntime): TransactionEngine;
12
+ static assertNoActiveAtomicTransaction(): void;
13
+ getActiveTransaction(): AtomicTransaction | undefined;
14
+ getActiveLease(): TransactionClientLease | undefined;
15
+ atomic<T>(work: (tx: AtomicTransaction) => Promise<T> | T): Promise<T>;
16
+ }
@@ -0,0 +1,7 @@
1
+ import type { CallbackRecord } from './CallbackRecord';
2
+ import type { FrameTransactionHandle } from './FrameTransactionHandle';
3
+ export type TransactionFrame = {
4
+ callbacks: CallbackRecord[];
5
+ facade: FrameTransactionHandle;
6
+ savepointName?: string;
7
+ };
@@ -0,0 +1,10 @@
1
+ import type { TangoRuntime } from '../../../runtime/TangoRuntime';
2
+ import type { TransactionClientLease } from '../../../runtime/internal/DBClientProvider';
3
+ import type { TransactionFrame } from './TransactionFrame';
4
+ export type TransactionState = {
5
+ runtime: TangoRuntime;
6
+ lease: TransactionClientLease;
7
+ frames: TransactionFrame[];
8
+ nextCallbackOrder: number;
9
+ nextSavepointId: number;
10
+ };
@@ -0,0 +1 @@
1
+ export { TransactionEngine } from './TransactionEngine';
@@ -1,5 +1,12 @@
1
1
  import { __export } from "./chunk-DLY2FNSh.js";
2
+ import { TransactionEngine, getTangoRuntime } from "./defaultRuntime-BPK9kWEW.js";
2
3
 
4
+ //#region src/transaction/atomic.ts
5
+ async function atomic(work) {
6
+ return TransactionEngine.forRuntime(getTangoRuntime()).atomic(work);
7
+ }
8
+
9
+ //#endregion
3
10
  //#region src/transaction/UnitOfWork.ts
4
11
  var UnitOfWork = class UnitOfWork {
5
12
  static BRAND = "tango.orm.unit_of_work";
@@ -19,6 +26,7 @@ var UnitOfWork = class UnitOfWork {
19
26
  * Convenience factory that constructs and begins a unit of work.
20
27
  */
21
28
  static async start(client) {
29
+ TransactionEngine.assertNoActiveAtomicTransaction();
22
30
  const uow = new UnitOfWork(client);
23
31
  await uow.begin();
24
32
  return uow;
@@ -27,6 +35,7 @@ var UnitOfWork = class UnitOfWork {
27
35
  * Begin a transaction if one is not already active.
28
36
  */
29
37
  async begin() {
38
+ TransactionEngine.assertNoActiveAtomicTransaction();
30
39
  if (!this.isActive) {
31
40
  await this.client.begin();
32
41
  this.isActive = true;
@@ -61,8 +70,11 @@ var UnitOfWork = class UnitOfWork {
61
70
  //#endregion
62
71
  //#region src/transaction/index.ts
63
72
  var transaction_exports = {};
64
- __export(transaction_exports, { UnitOfWork: () => UnitOfWork });
73
+ __export(transaction_exports, {
74
+ UnitOfWork: () => UnitOfWork,
75
+ atomic: () => atomic
76
+ });
65
77
 
66
78
  //#endregion
67
- export { UnitOfWork, transaction_exports };
68
- //# sourceMappingURL=transaction-DooTMuAl.js.map
79
+ export { UnitOfWork, atomic, transaction_exports };
80
+ //# sourceMappingURL=transaction-Cs0Z9tbW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction-Cs0Z9tbW.js","names":["work: (tx: AtomicTransaction) => Promise<T> | T","client: DBClient","value: unknown"],"sources":["../src/transaction/atomic.ts","../src/transaction/UnitOfWork.ts","../src/transaction/index.ts"],"sourcesContent":["import { getTangoRuntime } from '../runtime/defaultRuntime';\nimport type { AtomicTransaction } from './AtomicTransaction';\nimport { TransactionEngine } from './internal/context';\n\nexport async function atomic<T>(work: (tx: AtomicTransaction) => Promise<T> | T): Promise<T> {\n return TransactionEngine.forRuntime(getTangoRuntime()).atomic(work);\n}\n","import type { DBClient } from '../connection/clients/DBClient';\nimport { TransactionEngine } from './internal/context';\n\n/**\n * Unit of Work pattern implementation for managing database transactions.\n * Ensures that a set of operations either all succeed or all fail together.\n *\n * @example\n * ```typescript\n * const uow = await UnitOfWork.start(dbClient);\n * try {\n * await userRepo.create({ email: 'test@example.com' });\n * await postRepo.create({ title: 'Hello' });\n * await uow.commit();\n * } catch (error) {\n * await uow.rollback();\n * throw error;\n * }\n * ```\n *\n * @deprecated Use `transaction.atomic(async (tx) => { ... })` for application\n * transaction workflows. `UnitOfWork` remains exported only for compatibility.\n */\nexport class UnitOfWork {\n static readonly BRAND = 'tango.orm.unit_of_work' as const;\n readonly __tangoBrand: typeof UnitOfWork.BRAND = UnitOfWork.BRAND;\n protected client: DBClient;\n protected isActive = false;\n\n constructor(client: DBClient) {\n this.client = client;\n }\n\n /**\n * Narrow an unknown value to `UnitOfWork`.\n */\n static isUnitOfWork(value: unknown): value is UnitOfWork {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === UnitOfWork.BRAND\n );\n }\n\n /**\n * Convenience factory that constructs and begins a unit of work.\n */\n static async start(client: DBClient): Promise<UnitOfWork> {\n TransactionEngine.assertNoActiveAtomicTransaction();\n const uow = new UnitOfWork(client);\n await uow.begin();\n return uow;\n }\n\n /**\n * Begin a transaction if one is not already active.\n */\n async begin(): Promise<void> {\n TransactionEngine.assertNoActiveAtomicTransaction();\n if (!this.isActive) {\n await this.client.begin();\n this.isActive = true;\n }\n }\n\n /**\n * Commit the active transaction.\n */\n async commit(): Promise<void> {\n if (this.isActive) {\n await this.client.commit();\n this.isActive = false;\n }\n }\n\n /**\n * Return the underlying database client.\n */\n getClient(): DBClient {\n return this.client;\n }\n\n /**\n * Roll back the active transaction.\n */\n async rollback(): Promise<void> {\n if (this.isActive) {\n await this.client.rollback();\n this.isActive = false;\n }\n }\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { atomic } from './atomic';\nexport type { AtomicTransaction, OnCommitOptions, SavepointOptions, SavepointResult } from './AtomicTransaction';\nexport { UnitOfWork } from './UnitOfWork';\n"],"mappings":";;;;AAIO,eAAe,OAAUA,MAA6D;AACzF,QAAO,kBAAkB,WAAW,iBAAiB,CAAC,CAAC,OAAO,KAAK;AACtE;;;;ICiBY,aAAN,MAAM,WAAW;CACpB,OAAgB,QAAQ;CACxB,eAAiD,WAAW;CAC5D;CACA,WAAqB;CAErB,YAAYC,QAAkB;AAC1B,OAAK,SAAS;CACjB;;;;CAKD,OAAO,aAAaC,OAAqC;AACrD,gBACW,UAAU,YACjB,UAAU,QACT,MAAqC,iBAAiB,WAAW;CAEzE;;;;CAKD,aAAa,MAAMD,QAAuC;AACtD,oBAAkB,iCAAiC;EACnD,MAAM,MAAM,IAAI,WAAW;AAC3B,QAAM,IAAI,OAAO;AACjB,SAAO;CACV;;;;CAKD,MAAM,QAAuB;AACzB,oBAAkB,iCAAiC;AACnD,OAAK,KAAK,UAAU;AAChB,SAAM,KAAK,OAAO,OAAO;AACzB,QAAK,WAAW;EACnB;CACJ;;;;CAKD,MAAM,SAAwB;AAC1B,MAAI,KAAK,UAAU;AACf,SAAM,KAAK,OAAO,QAAQ;AAC1B,QAAK,WAAW;EACnB;CACJ;;;;CAKD,YAAsB;AAClB,SAAO,KAAK;CACf;;;;CAKD,MAAM,WAA0B;AAC5B,MAAI,KAAK,UAAU;AACf,SAAM,KAAK,OAAO,UAAU;AAC5B,QAAK,WAAW;EACnB;CACJ;AACJ"}