@fluojs/drizzle 1.1.1 → 2.0.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.
package/dist/database.js CHANGED
@@ -121,7 +121,11 @@ class DrizzleDatabase {
121
121
  * @returns The transaction-scoped database inside an active boundary, or the root database outside one.
122
122
  */
123
123
  current() {
124
- return this.transactions.getStore()?.database ?? this.database;
124
+ const current = this.transactions.getStore();
125
+ if (!current || current.transactionBoundaryOwner.closed) {
126
+ return this.database;
127
+ }
128
+ return current.database;
125
129
  }
126
130
 
127
131
  /** Aborts active request transactions, waits for settlement, then runs the optional dispose hook. */
@@ -185,41 +189,66 @@ class DrizzleDatabase {
185
189
  async executeTransaction(fn, options, requestScoped, signal) {
186
190
  const current = this.transactions.getStore();
187
191
  if (current) {
192
+ if (current.transactionBoundaryOwner.closed) {
193
+ if (requestScoped) {
194
+ return this.executeInheritedRequestTransaction(current, fn, options, signal);
195
+ }
196
+ return this.executeManualRootTransaction(fn, options);
197
+ }
198
+ if (!requestScoped) {
199
+ return this.executeNestedManualTransaction(current.transactionBoundaryOwner, fn, options);
200
+ }
188
201
  if (requestScoped) {
189
202
  this.assertRequestTransactionsAvailable();
190
203
  } else {
191
204
  this.assertTransactionsAvailable();
192
205
  }
193
- if (options !== undefined) {
194
- throw new Error(NESTED_TRANSACTION_OPTIONS_NOT_SUPPORTED_ERROR);
195
- }
196
206
  if (requestScoped) {
197
207
  return this.executeNestedRequestTransaction(current, fn, signal);
198
208
  }
199
- return fn();
200
209
  }
201
210
  if (!requestScoped) {
202
211
  return this.executeManualRootTransaction(fn, options);
203
212
  }
204
- const transactionRunner = this.resolveTransactionRunner();
205
- if (!transactionRunner) {
206
- return this.executeRequestFallback(fn, signal);
207
- }
208
- return this.executeRequestTransaction(transactionRunner, fn, options, signal);
213
+ return this.executeRequestRootTransaction(fn, options, signal);
209
214
  }
210
215
  async executeManualRootTransaction(fn, options) {
211
216
  const deferredRequestTransactionSettlements = new Set();
217
+ const fallbackTransactionOwner = {
218
+ closed: false,
219
+ callbackSettlements: new Set(),
220
+ requestTransactionSettlements: new Set()
221
+ };
212
222
  const activeTransactionScope = this.trackAvailableTransactionScope();
213
223
  try {
214
224
  const transactionRunner = this.resolveTransactionRunner();
215
225
  if (!transactionRunner) {
216
- return await fn();
226
+ try {
227
+ return await this.transactions.run({
228
+ database: this.database,
229
+ fallbackTransactionOwner,
230
+ transactionBoundaryOwner: fallbackTransactionOwner
231
+ }, fn);
232
+ } finally {
233
+ await this.closeTransactionBoundaryOwner(fallbackTransactionOwner);
234
+ }
217
235
  }
218
- return await transactionRunner(transactionDatabase => this.transactions.run({
219
- database: transactionDatabase,
220
- deferredRequestTransactionSettlements
221
- }, fn), options);
236
+ return await transactionRunner(async transactionDatabase => {
237
+ try {
238
+ return await this.transactions.run({
239
+ database: transactionDatabase,
240
+ deferredRequestTransactionSettlements,
241
+ transactionBoundaryOwner: fallbackTransactionOwner
242
+ }, fn);
243
+ } finally {
244
+ await this.closeTransactionBoundaryOwner(fallbackTransactionOwner);
245
+ }
246
+ }, options);
222
247
  } finally {
248
+ await this.closeTransactionBoundaryOwner(fallbackTransactionOwner);
249
+ for (const handle of fallbackTransactionOwner.requestTransactionSettlements) {
250
+ this.untrackActiveRequestTransaction(handle);
251
+ }
223
252
  for (const handle of deferredRequestTransactionSettlements) {
224
253
  this.untrackActiveRequestTransaction(handle);
225
254
  }
@@ -230,23 +259,92 @@ class DrizzleDatabase {
230
259
  this.assertRequestTransactionsAvailable();
231
260
  const abortContext = createRequestAbortContext(signal);
232
261
  const active = this.trackActiveRequestTransaction(abortContext.controller);
262
+ const transactionBoundaryOwner = {
263
+ closed: false,
264
+ callbackSettlements: new Set(),
265
+ requestTransactionSettlements: new Set()
266
+ };
233
267
  try {
234
- const result = await transactionRunner(transactionDatabase => this.transactions.run({
235
- database: transactionDatabase,
236
- requestAbortSignal: abortContext.signal
237
- }, () => raceWithAbort(fn, abortContext.signal)), options);
268
+ const result = await transactionRunner(async transactionDatabase => {
269
+ try {
270
+ return await this.transactions.run({
271
+ database: transactionDatabase,
272
+ inheritedRequestAbortSignal: signal ?? abortContext.signal,
273
+ requestAbortSignal: abortContext.signal,
274
+ transactionBoundaryOwner
275
+ }, () => raceWithAbort(fn, abortContext.signal));
276
+ } finally {
277
+ await this.closeTransactionBoundaryOwner(transactionBoundaryOwner);
278
+ }
279
+ }, options);
238
280
  this.throwIfRequestAborted(abortContext.signal);
239
281
  return result;
240
282
  } finally {
283
+ transactionBoundaryOwner.closed = true;
241
284
  abortContext.cleanup();
242
285
  this.untrackActiveRequestTransaction(active);
243
286
  }
244
287
  }
288
+ async executeRequestRootTransaction(fn, options, signal) {
289
+ const transactionRunner = this.resolveTransactionRunner();
290
+ if (!transactionRunner) {
291
+ return this.executeRequestFallback(fn, signal);
292
+ }
293
+ return this.executeRequestTransaction(transactionRunner, fn, options, signal);
294
+ }
295
+ async executeInheritedRequestTransaction(current, fn, options, signal) {
296
+ const inheritedRequestAbortSignal = current.inheritedRequestAbortSignal ?? current.requestAbortSignal;
297
+ if (!inheritedRequestAbortSignal) {
298
+ return this.executeRequestRootTransaction(fn, options, signal);
299
+ }
300
+ const abortSignalView = createRequestAbortSignalView(inheritedRequestAbortSignal, signal);
301
+ try {
302
+ this.throwIfRequestAborted(abortSignalView.signal);
303
+ return await this.executeRequestRootTransaction(fn, options, abortSignalView.signal);
304
+ } finally {
305
+ abortSignalView.cleanup();
306
+ }
307
+ }
308
+ async executeNestedManualTransaction(owner, fn, options) {
309
+ this.assertTransactionsAvailable();
310
+ if (options !== undefined) {
311
+ throw new Error(NESTED_TRANSACTION_OPTIONS_NOT_SUPPORTED_ERROR);
312
+ }
313
+ const callback = Promise.resolve().then(fn);
314
+ const removeSettlement = () => {
315
+ owner.callbackSettlements.delete(settlement);
316
+ };
317
+ const settlement = callback.then(removeSettlement, removeSettlement);
318
+ owner.callbackSettlements.add(settlement);
319
+ return callback;
320
+ }
245
321
  async executeNestedRequestTransaction(current, fn, signal) {
322
+ const fallbackTransactionOwner = current.fallbackTransactionOwner;
323
+ if (fallbackTransactionOwner?.closed) {
324
+ if (current.requestAbortSignal) {
325
+ const abortSignalView = createRequestAbortSignalView(current.requestAbortSignal, signal);
326
+ try {
327
+ return await this.executeRequestFallback(fn, abortSignalView.signal);
328
+ } finally {
329
+ abortSignalView.cleanup();
330
+ }
331
+ }
332
+ return this.executeRequestFallback(fn, signal);
333
+ }
334
+ const runCallback = () => {
335
+ const callback = fn();
336
+ const transactionBoundaryOwner = current.transactionBoundaryOwner;
337
+ const removeSettlement = () => {
338
+ transactionBoundaryOwner.callbackSettlements.delete(settlement);
339
+ };
340
+ const settlement = callback.then(removeSettlement, removeSettlement);
341
+ transactionBoundaryOwner.callbackSettlements.add(settlement);
342
+ return callback;
343
+ };
246
344
  if (current.requestAbortSignal) {
247
345
  const abortSignalView = createRequestAbortSignalView(current.requestAbortSignal, signal);
248
346
  try {
249
- const result = await raceWithAbort(fn, abortSignalView.signal);
347
+ const result = await raceWithAbort(runCallback, abortSignalView.signal);
250
348
  this.throwIfRequestAborted(abortSignalView.signal);
251
349
  return result;
252
350
  } finally {
@@ -256,17 +354,27 @@ class DrizzleDatabase {
256
354
  this.assertRequestTransactionsAvailable();
257
355
  const abortContext = createRequestAbortContext(signal);
258
356
  const active = this.trackActiveRequestTransaction(abortContext.controller);
259
- current.deferredRequestTransactionSettlements?.add(active);
357
+ let ownerDefersSettlement = false;
358
+ if (fallbackTransactionOwner) {
359
+ fallbackTransactionOwner.requestTransactionSettlements.add(active);
360
+ ownerDefersSettlement = true;
361
+ } else if (current.deferredRequestTransactionSettlements) {
362
+ current.deferredRequestTransactionSettlements.add(active);
363
+ ownerDefersSettlement = true;
364
+ }
260
365
  try {
261
366
  const result = await this.transactions.run({
262
367
  database: current.database,
263
- requestAbortSignal: abortContext.signal
264
- }, () => raceWithAbort(fn, abortContext.signal));
368
+ fallbackTransactionOwner,
369
+ inheritedRequestAbortSignal: current.inheritedRequestAbortSignal,
370
+ requestAbortSignal: abortContext.signal,
371
+ transactionBoundaryOwner: current.transactionBoundaryOwner
372
+ }, () => raceWithAbort(runCallback, abortContext.signal));
265
373
  this.throwIfRequestAborted(abortContext.signal);
266
374
  return result;
267
375
  } finally {
268
376
  abortContext.cleanup();
269
- if (current.deferredRequestTransactionSettlements) {
377
+ if (ownerDefersSettlement) {
270
378
  this.markRequestTransactionInactiveForStatus(active);
271
379
  } else {
272
380
  this.untrackActiveRequestTransaction(active);
@@ -276,16 +384,46 @@ class DrizzleDatabase {
276
384
  async executeRequestFallback(fn, signal) {
277
385
  this.assertRequestTransactionsAvailable();
278
386
  const abortContext = createRequestAbortContext(signal);
387
+ if (abortContext.signal.aborted) {
388
+ const abortError = createAbortError(abortContext.signal.reason);
389
+ abortContext.cleanup();
390
+ throw abortError;
391
+ }
279
392
  const active = this.trackActiveRequestTransaction(abortContext.controller);
393
+ const fallbackTransactionOwner = {
394
+ closed: false,
395
+ callbackSettlements: new Set(),
396
+ requestTransactionSettlements: new Set()
397
+ };
280
398
  try {
281
- const result = await raceWithAbort(fn, abortContext.signal);
399
+ const result = await raceWithAbort(() => {
400
+ const callback = new Promise(resolve => resolve(this.transactions.run({
401
+ database: this.database,
402
+ fallbackTransactionOwner,
403
+ inheritedRequestAbortSignal: signal ?? abortContext.signal,
404
+ requestAbortSignal: abortContext.signal,
405
+ transactionBoundaryOwner: fallbackTransactionOwner
406
+ }, fn)));
407
+ return callback.finally(async () => {
408
+ await this.closeTransactionBoundaryOwner(fallbackTransactionOwner);
409
+ for (const handle of fallbackTransactionOwner.requestTransactionSettlements) {
410
+ this.untrackActiveRequestTransaction(handle);
411
+ }
412
+ this.untrackActiveRequestTransaction(active);
413
+ });
414
+ }, abortContext.signal);
282
415
  this.throwIfRequestAborted(abortContext.signal);
283
416
  return result;
284
417
  } finally {
285
418
  abortContext.cleanup();
286
- this.untrackActiveRequestTransaction(active);
287
419
  }
288
420
  }
421
+ async closeTransactionBoundaryOwner(owner) {
422
+ while (owner.callbackSettlements.size > 0) {
423
+ await Promise.all(owner.callbackSettlements);
424
+ }
425
+ owner.closed = true;
426
+ }
289
427
  assertRequestTransactionsAvailable() {
290
428
  if (this.lifecycleState !== 'ready') {
291
429
  throw new Error(REQUEST_TRANSACTION_UNAVAILABLE_ERROR);
package/dist/module.d.ts CHANGED
@@ -1,15 +1,31 @@
1
1
  import type { AsyncModuleOptions } from '@fluojs/core';
2
- import { type ModuleType } from '@fluojs/runtime';
2
+ import type { ModuleType } from '@fluojs/runtime';
3
3
  import type { DrizzleDatabaseLike, DrizzleModuleOptions } from './types.js';
4
- type DrizzleAsyncModuleOptions<TDatabase extends DrizzleDatabaseLike<TTransactionDatabase, TTransactionOptions>, TTransactionDatabase, TTransactionOptions> = AsyncModuleOptions<Omit<DrizzleModuleOptions<TDatabase, TTransactionDatabase, TTransactionOptions>, 'global'>> & Pick<DrizzleModuleOptions<TDatabase, TTransactionDatabase, TTransactionOptions>, 'global'>;
4
+ /**
5
+ * Configures an async Drizzle module registration through an injected factory.
6
+ *
7
+ * @typeParam TDatabase Root Drizzle database handle registered in the module.
8
+ * @typeParam TTransactionDatabase Transaction-scoped database handle resolved inside transaction callbacks.
9
+ * @typeParam TTransactionOptions Options forwarded to the underlying Drizzle transaction runner.
10
+ */
11
+ export type DrizzleAsyncModuleOptions<TDatabase extends DrizzleDatabaseLike<TTransactionDatabase, TTransactionOptions>, TTransactionDatabase, TTransactionOptions> = AsyncModuleOptions<Omit<DrizzleModuleOptions<TDatabase, TTransactionDatabase, TTransactionOptions>, 'global' | 'name'>> & Pick<DrizzleModuleOptions<TDatabase, TTransactionDatabase, TTransactionOptions>, 'global' | 'name'>;
5
12
  /**
6
13
  * Module entrypoint for wiring a Drizzle database into the Fluo runtime lifecycle.
7
14
  */
8
15
  export declare class DrizzleModule {
9
- /** Creates a module definition from static Drizzle options. */
16
+ /**
17
+ * Creates a module definition from static Drizzle options.
18
+ *
19
+ * @param options Static Drizzle registration options.
20
+ * @returns A runtime module definition for the requested Drizzle registration.
21
+ */
10
22
  static forRoot<TDatabase extends DrizzleDatabaseLike<TTransactionDatabase, TTransactionOptions>, TTransactionDatabase = TDatabase, TTransactionOptions = unknown>(options: DrizzleModuleOptions<TDatabase, TTransactionDatabase, TTransactionOptions>): ModuleType;
11
- /** Creates a module definition from DI-aware async Drizzle options. */
23
+ /**
24
+ * Creates a module definition from DI-aware async Drizzle options.
25
+ *
26
+ * @param options Async Drizzle registration options.
27
+ * @returns A runtime module definition for the requested Drizzle registration.
28
+ */
12
29
  static forRootAsync<TDatabase extends DrizzleDatabaseLike<TTransactionDatabase, TTransactionOptions>, TTransactionDatabase = TDatabase, TTransactionOptions = unknown>(options: DrizzleAsyncModuleOptions<TDatabase, TTransactionDatabase, TTransactionOptions>): ModuleType;
13
30
  }
14
- export {};
15
31
  //# sourceMappingURL=module.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAIhE,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAc5E,KAAK,yBAAyB,CAC5B,SAAS,SAAS,mBAAmB,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,EAChF,oBAAoB,EACpB,mBAAmB,IACjB,kBAAkB,CAAC,IAAI,CAAC,oBAAoB,CAAC,SAAS,EAAE,oBAAoB,EAAE,mBAAmB,CAAC,EAAE,QAAQ,CAAC,CAAC,GAChH,IAAI,CAAC,oBAAoB,CAAC,SAAS,EAAE,oBAAoB,EAAE,mBAAmB,CAAC,EAAE,QAAQ,CAAC,CAAC;AAgI7F;;GAEG;AACH,qBAAa,aAAa;IACxB,+DAA+D;IAC/D,MAAM,CAAC,OAAO,CACZ,SAAS,SAAS,mBAAmB,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,EAChF,oBAAoB,GAAG,SAAS,EAChC,mBAAmB,GAAG,OAAO,EAC7B,OAAO,EAAE,oBAAoB,CAAC,SAAS,EAAE,oBAAoB,EAAE,mBAAmB,CAAC,GAAG,UAAU;IAIlG,uEAAuE;IACvE,MAAM,CAAC,YAAY,CACjB,SAAS,SAAS,mBAAmB,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,EAChF,oBAAoB,GAAG,SAAS,EAChC,mBAAmB,GAAG,OAAO,EAC7B,OAAO,EAAE,yBAAyB,CAAC,SAAS,EAAE,oBAAoB,EAAE,mBAAmB,CAAC,GAAG,UAAU;CAGxG"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGlD,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAE5E;;;;;;GAMG;AACH,MAAM,MAAM,yBAAyB,CACnC,SAAS,SAAS,mBAAmB,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,EAChF,oBAAoB,EACpB,mBAAmB,IACjB,kBAAkB,CAAC,IAAI,CAAC,oBAAoB,CAAC,SAAS,EAAE,oBAAoB,EAAE,mBAAmB,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC,CAAC,GACzH,IAAI,CAAC,oBAAoB,CAAC,SAAS,EAAE,oBAAoB,EAAE,mBAAmB,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC,CAAC;AAEtG;;GAEG;AACH,qBAAa,aAAa;IACxB;;;;;OAKG;IACH,MAAM,CAAC,OAAO,CACZ,SAAS,SAAS,mBAAmB,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,EAChF,oBAAoB,GAAG,SAAS,EAChC,mBAAmB,GAAG,OAAO,EAC7B,OAAO,EAAE,oBAAoB,CAAC,SAAS,EAAE,oBAAoB,EAAE,mBAAmB,CAAC,GAAG,UAAU;IAIlG;;;;;OAKG;IACH,MAAM,CAAC,YAAY,CACjB,SAAS,SAAS,mBAAmB,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,EAChF,oBAAoB,GAAG,SAAS,EAChC,mBAAmB,GAAG,OAAO,EAC7B,OAAO,EAAE,yBAAyB,CAAC,SAAS,EAAE,oBAAoB,EAAE,mBAAmB,CAAC,GAAG,UAAU;CAGxG"}
package/dist/module.js CHANGED
@@ -1,89 +1,33 @@
1
- import { defineModule } from '@fluojs/runtime';
2
- import { DrizzleDatabase } from './database.js';
3
- import { DRIZZLE_DATABASE, DRIZZLE_DISPOSE, DRIZZLE_HANDLE_PROVIDER, DRIZZLE_OPTIONS } from './tokens.js';
4
- const DRIZZLE_NORMALIZED_OPTIONS = Symbol('fluo.drizzle.normalized-options');
5
- const DRIZZLE_MODULE_EXPORTS = [DrizzleDatabase, DRIZZLE_HANDLE_PROVIDER];
6
- function isObjectLike(value) {
7
- return typeof value === 'object' && value !== null || typeof value === 'function';
8
- }
9
- function normalizeDrizzleModuleOptions(options) {
10
- if (!isObjectLike(options.database)) {
11
- throw new Error('DrizzleModule requires a database option.');
12
- }
13
- return {
14
- ...options,
15
- strictTransactions: options.strictTransactions ?? false
16
- };
17
- }
18
- function createRuntimeOptionsProviderValue(strictTransactions) {
19
- return {
20
- strictTransactions
21
- };
22
- }
23
- function createDrizzleRuntimeProviders(normalizedOptionsProvider) {
24
- return [normalizedOptionsProvider, {
25
- inject: [DRIZZLE_NORMALIZED_OPTIONS],
26
- provide: DRIZZLE_DATABASE,
27
- useFactory: options => options.database
28
- }, {
29
- inject: [DRIZZLE_NORMALIZED_OPTIONS],
30
- provide: DRIZZLE_DISPOSE,
31
- useFactory: options => options.dispose
32
- }, {
33
- inject: [DRIZZLE_NORMALIZED_OPTIONS],
34
- provide: DRIZZLE_OPTIONS,
35
- useFactory: options => createRuntimeOptionsProviderValue(options.strictTransactions)
36
- }, {
37
- inject: [DRIZZLE_DATABASE, DRIZZLE_DISPOSE, DRIZZLE_OPTIONS],
38
- provide: DrizzleDatabase,
39
- useFactory: (database, dispose, databaseOptions) => DrizzleDatabase.createFacade(database, dispose, databaseOptions)
40
- }, {
41
- provide: DRIZZLE_HANDLE_PROVIDER,
42
- useExisting: DrizzleDatabase
43
- }];
44
- }
45
- function createDrizzleProvidersAsync(options) {
46
- const normalizedOptionsProvider = {
47
- inject: options.inject,
48
- provide: DRIZZLE_NORMALIZED_OPTIONS,
49
- scope: 'singleton',
50
- useFactory: async (...deps) => normalizeDrizzleModuleOptions({
51
- ...(await options.useFactory(...deps)),
52
- global: options.global
53
- })
54
- };
55
- return createDrizzleRuntimeProviders(normalizedOptionsProvider);
56
- }
57
- function buildDrizzleModule(options) {
58
- class DrizzleRootModuleDefinition {}
59
- return defineModule(DrizzleRootModuleDefinition, {
60
- exports: DRIZZLE_MODULE_EXPORTS,
61
- global: options.global ?? false,
62
- providers: createDrizzleRuntimeProviders({
63
- provide: DRIZZLE_NORMALIZED_OPTIONS,
64
- useValue: normalizeDrizzleModuleOptions(options)
65
- })
66
- });
67
- }
68
- function buildDrizzleModuleAsync(options) {
69
- class DrizzleAsyncModuleDefinition {}
70
- return defineModule(DrizzleAsyncModuleDefinition, {
71
- exports: DRIZZLE_MODULE_EXPORTS,
72
- global: options.global ?? false,
73
- providers: createDrizzleProvidersAsync(options)
74
- });
75
- }
1
+ import { buildDrizzleModule, buildDrizzleModuleAsync } from './named-registration.js';
2
+
3
+ /**
4
+ * Configures an async Drizzle module registration through an injected factory.
5
+ *
6
+ * @typeParam TDatabase Root Drizzle database handle registered in the module.
7
+ * @typeParam TTransactionDatabase Transaction-scoped database handle resolved inside transaction callbacks.
8
+ * @typeParam TTransactionOptions Options forwarded to the underlying Drizzle transaction runner.
9
+ */
76
10
 
77
11
  /**
78
12
  * Module entrypoint for wiring a Drizzle database into the Fluo runtime lifecycle.
79
13
  */
80
14
  export class DrizzleModule {
81
- /** Creates a module definition from static Drizzle options. */
15
+ /**
16
+ * Creates a module definition from static Drizzle options.
17
+ *
18
+ * @param options Static Drizzle registration options.
19
+ * @returns A runtime module definition for the requested Drizzle registration.
20
+ */
82
21
  static forRoot(options) {
83
22
  return buildDrizzleModule(options);
84
23
  }
85
24
 
86
- /** Creates a module definition from DI-aware async Drizzle options. */
25
+ /**
26
+ * Creates a module definition from DI-aware async Drizzle options.
27
+ *
28
+ * @param options Async Drizzle registration options.
29
+ * @returns A runtime module definition for the requested Drizzle registration.
30
+ */
87
31
  static forRootAsync(options) {
88
32
  return buildDrizzleModuleAsync(options);
89
33
  }
@@ -0,0 +1,20 @@
1
+ import { type ModuleType } from '@fluojs/runtime';
2
+ import type { DrizzleAsyncModuleOptions } from './module.js';
3
+ import type { DrizzleDatabaseLike, DrizzleModuleOptions } from './types.js';
4
+ /**
5
+ * Builds a synchronous Drizzle runtime module definition.
6
+ *
7
+ * @internal
8
+ * @param options Static Drizzle registration options.
9
+ * @returns A module definition for the default or named registration.
10
+ */
11
+ export declare function buildDrizzleModule<TDatabase extends DrizzleDatabaseLike<TTransactionDatabase, TTransactionOptions>, TTransactionDatabase = TDatabase, TTransactionOptions = unknown>(options: DrizzleModuleOptions<TDatabase, TTransactionDatabase, TTransactionOptions>): ModuleType;
12
+ /**
13
+ * Builds an asynchronous Drizzle runtime module definition.
14
+ *
15
+ * @internal
16
+ * @param options Async Drizzle registration options.
17
+ * @returns A module definition for the default or named registration.
18
+ */
19
+ export declare function buildDrizzleModuleAsync<TDatabase extends DrizzleDatabaseLike<TTransactionDatabase, TTransactionOptions>, TTransactionDatabase = TDatabase, TTransactionOptions = unknown>(options: DrizzleAsyncModuleOptions<TDatabase, TTransactionDatabase, TTransactionOptions>): ModuleType;
20
+ //# sourceMappingURL=named-registration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"named-registration.d.ts","sourceRoot":"","sources":["../src/named-registration.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGhE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAmB7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAmF5E;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,SAAS,mBAAmB,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,EAChF,oBAAoB,GAAG,SAAS,EAChC,mBAAmB,GAAG,OAAO,EAC7B,OAAO,EAAE,oBAAoB,CAAC,SAAS,EAAE,oBAAoB,EAAE,mBAAmB,CAAC,GAAG,UAAU,CAajG;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,SAAS,mBAAmB,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,EAChF,oBAAoB,GAAG,SAAS,EAChC,mBAAmB,GAAG,OAAO,EAC7B,OAAO,EAAE,yBAAyB,CAAC,SAAS,EAAE,oBAAoB,EAAE,mBAAmB,CAAC,GAAG,UAAU,CAUtG"}
@@ -0,0 +1,84 @@
1
+ import { defineModule } from '@fluojs/runtime';
2
+ import { DrizzleDatabase } from './database.js';
3
+ import { createDrizzleRuntimeProviders, getNormalizedOptionsToken, getRegistrationGuardToken } from './registration-providers.js';
4
+ import { normalizeDrizzleRegistrationName } from './registration-name.js';
5
+ import { DRIZZLE_DATABASE, DRIZZLE_DISPOSE, DRIZZLE_HANDLE_PROVIDER, DRIZZLE_OPTIONS, getDrizzleDatabaseToken, getDrizzleDisposeToken, getDrizzleHandleProviderToken, getDrizzleOptionsToken } from './tokens.js';
6
+ import { DrizzleTransactionInterceptor } from './transaction.js';
7
+ const DRIZZLE_MODULE_EXPORTS = [DrizzleDatabase, DrizzleTransactionInterceptor, DRIZZLE_HANDLE_PROVIDER, DRIZZLE_DATABASE, DRIZZLE_DISPOSE, DRIZZLE_OPTIONS];
8
+ function isObjectLike(value) {
9
+ return typeof value === 'object' && value !== null || typeof value === 'function';
10
+ }
11
+ function assertNamedRegistrationIsScoped(name, global) {
12
+ if (name !== undefined && global) {
13
+ throw new Error('Named Drizzle registrations are scoped and cannot be registered globally.');
14
+ }
15
+ }
16
+ function normalizeDrizzleModuleOptions(options) {
17
+ if (!isObjectLike(options.database)) {
18
+ throw new Error('DrizzleModule requires a database option.');
19
+ }
20
+ const name = normalizeDrizzleRegistrationName(options.name);
21
+ assertNamedRegistrationIsScoped(name, options.global);
22
+ return {
23
+ ...options,
24
+ global: name === undefined ? options.global : false,
25
+ name,
26
+ strictTransactions: options.strictTransactions ?? false
27
+ };
28
+ }
29
+ function createDrizzleProvidersAsync(options, name) {
30
+ const registrationGuardToken = name === undefined ? undefined : getRegistrationGuardToken(name);
31
+ const normalizedOptionsProvider = {
32
+ inject: registrationGuardToken === undefined ? options.inject : [registrationGuardToken, ...(options.inject ?? [])],
33
+ provide: getNormalizedOptionsToken(name),
34
+ scope: 'singleton',
35
+ useFactory: async (...dependencies) => normalizeDrizzleModuleOptions({
36
+ ...(await options.useFactory(...(registrationGuardToken === undefined ? dependencies : dependencies.slice(1)))),
37
+ global: options.global,
38
+ name
39
+ })
40
+ };
41
+ return createDrizzleRuntimeProviders(normalizedOptionsProvider, name);
42
+ }
43
+ function getDrizzleModuleExports(name) {
44
+ return name === undefined ? DRIZZLE_MODULE_EXPORTS : [getDrizzleDatabaseToken(name), getDrizzleDisposeToken(name), getDrizzleHandleProviderToken(name), getDrizzleOptionsToken(name)];
45
+ }
46
+
47
+ /**
48
+ * Builds a synchronous Drizzle runtime module definition.
49
+ *
50
+ * @internal
51
+ * @param options Static Drizzle registration options.
52
+ * @returns A module definition for the default or named registration.
53
+ */
54
+ export function buildDrizzleModule(options) {
55
+ const normalizedOptions = normalizeDrizzleModuleOptions(options);
56
+ const name = normalizedOptions.name;
57
+ class DrizzleRootModuleDefinition {}
58
+ return defineModule(DrizzleRootModuleDefinition, {
59
+ exports: getDrizzleModuleExports(name),
60
+ global: normalizedOptions.global ?? false,
61
+ providers: createDrizzleRuntimeProviders({
62
+ provide: getNormalizedOptionsToken(name),
63
+ useValue: normalizedOptions
64
+ }, name)
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Builds an asynchronous Drizzle runtime module definition.
70
+ *
71
+ * @internal
72
+ * @param options Async Drizzle registration options.
73
+ * @returns A module definition for the default or named registration.
74
+ */
75
+ export function buildDrizzleModuleAsync(options) {
76
+ const name = normalizeDrizzleRegistrationName(options.name);
77
+ assertNamedRegistrationIsScoped(name, options.global);
78
+ class DrizzleAsyncModuleDefinition {}
79
+ return defineModule(DrizzleAsyncModuleDefinition, {
80
+ exports: getDrizzleModuleExports(name),
81
+ global: name === undefined ? options.global ?? false : false,
82
+ providers: createDrizzleProvidersAsync(options, name)
83
+ });
84
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Normalizes a public Drizzle registration name before it becomes part of a DI token.
3
+ *
4
+ * @internal
5
+ * @param name Optional registration name supplied by a module or token helper.
6
+ * @returns The trimmed registration name, or `undefined` for the default registration.
7
+ */
8
+ export declare function normalizeDrizzleRegistrationName(name?: string): string | undefined;
9
+ //# sourceMappingURL=registration-name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registration-name.d.ts","sourceRoot":"","sources":["../src/registration-name.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAYlF"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Normalizes a public Drizzle registration name before it becomes part of a DI token.
3
+ *
4
+ * @internal
5
+ * @param name Optional registration name supplied by a module or token helper.
6
+ * @returns The trimmed registration name, or `undefined` for the default registration.
7
+ */
8
+ export function normalizeDrizzleRegistrationName(name) {
9
+ if (name === undefined) {
10
+ return undefined;
11
+ }
12
+ const normalizedName = name.trim();
13
+ if (normalizedName.length === 0) {
14
+ throw new Error('DrizzleModule name must be a non-empty string when provided.');
15
+ }
16
+ return normalizedName;
17
+ }
@@ -0,0 +1,44 @@
1
+ import type { Provider } from '@fluojs/di';
2
+ import type { DrizzleDatabaseLike, DrizzleModuleOptions } from './types.js';
3
+ /**
4
+ * Normalized runtime options consumed by lifecycle-aware Drizzle providers.
5
+ *
6
+ * @internal
7
+ */
8
+ export type DrizzleRuntimeOptions = {
9
+ strictTransactions: boolean;
10
+ };
11
+ /**
12
+ * Fully normalized module options stored behind an internal registration token.
13
+ *
14
+ * @internal
15
+ */
16
+ export type ResolvedDrizzleModuleOptions<TDatabase extends DrizzleDatabaseLike<TTransactionDatabase, TTransactionOptions>, TTransactionDatabase, TTransactionOptions> = Omit<DrizzleModuleOptions<TDatabase, TTransactionDatabase, TTransactionOptions>, 'strictTransactions'> & {
17
+ strictTransactions: boolean;
18
+ };
19
+ /**
20
+ * Returns the internal options token for a default or named registration.
21
+ *
22
+ * @internal
23
+ * @param name Optional normalized registration name.
24
+ * @returns The internal token that stores normalized module options.
25
+ */
26
+ export declare function getNormalizedOptionsToken(name?: string): symbol;
27
+ /**
28
+ * Returns the globally stable duplicate-registration guard token for a name.
29
+ *
30
+ * @internal
31
+ * @param name Normalized named-registration identity.
32
+ * @returns The guard token shared by registrations using the same name.
33
+ */
34
+ export declare function getRegistrationGuardToken(name: string): symbol;
35
+ /**
36
+ * Builds the provider graph for a default or named Drizzle registration.
37
+ *
38
+ * @internal
39
+ * @param normalizedOptionsProvider Provider that supplies normalized module options.
40
+ * @param name Optional normalized registration name.
41
+ * @returns Providers for the registration's raw handle, lifecycle wrapper, options, and disposal hook.
42
+ */
43
+ export declare function createDrizzleRuntimeProviders<TDatabase extends DrizzleDatabaseLike<TTransactionDatabase, TTransactionOptions>, TTransactionDatabase, TTransactionOptions>(normalizedOptionsProvider: Provider, name?: string): Provider[];
44
+ //# sourceMappingURL=registration-providers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registration-providers.d.ts","sourceRoot":"","sources":["../src/registration-providers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAU3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAE5E;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,kBAAkB,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,4BAA4B,CACtC,SAAS,SAAS,mBAAmB,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,EAChF,oBAAoB,EACpB,mBAAmB,IACjB,IAAI,CAAC,oBAAoB,CAAC,SAAS,EAAE,oBAAoB,EAAE,mBAAmB,CAAC,EAAE,oBAAoB,CAAC,GAAG;IAC3G,kBAAkB,EAAE,OAAO,CAAC;CAC7B,CAAC;AAKF;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAI/D;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9D;AAoBD;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC3C,SAAS,SAAS,mBAAmB,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,EAChF,oBAAoB,EACpB,mBAAmB,EAEnB,yBAAyB,EAAE,QAAQ,EACnC,IAAI,CAAC,EAAE,MAAM,GACZ,QAAQ,EAAE,CAyGZ"}