@fluojs/drizzle 1.1.0 → 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
@@ -5,19 +5,23 @@ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e =
5
5
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
6
6
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
7
7
  import { AsyncLocalStorage } from 'node:async_hooks';
8
- import { createAbortError, createRequestAbortContext, raceWithAbort, trackActiveRequestTransaction, untrackActiveRequestTransaction } from '@fluojs/runtime';
9
8
  import { Inject } from '@fluojs/core';
10
- import { DRIZZLE_DATABASE, DRIZZLE_DISPOSE, DRIZZLE_OPTIONS } from './tokens.js';
9
+ import { createAbortError, createRequestAbortContext, raceWithAbort, trackActiveRequestTransaction, untrackActiveRequestTransaction } from '@fluojs/runtime';
11
10
  import { createDrizzlePlatformStatusSnapshot } from './status.js';
11
+ import { DRIZZLE_DATABASE, DRIZZLE_DISPOSE, DRIZZLE_OPTIONS } from './tokens.js';
12
12
  const TRANSACTION_NOT_SUPPORTED_ERROR = 'Transaction not supported: Drizzle database does not implement transaction.';
13
13
  const NESTED_TRANSACTION_OPTIONS_NOT_SUPPORTED_ERROR = 'Nested Drizzle transaction options are not supported because the active transaction context is reused.';
14
14
  const TRANSACTION_UNAVAILABLE_ERROR = 'Drizzle transactions are not available during application shutdown.';
15
15
  const REQUEST_TRANSACTION_UNAVAILABLE_ERROR = 'Drizzle request transactions are not available during shutdown.';
16
16
  function createCurrentlessDrizzleFacade(target) {
17
17
  return new Proxy(target, {
18
- get(database, prop, receiver) {
18
+ get(database, prop) {
19
19
  if (prop in database) {
20
- return Reflect.get(database, prop, receiver);
20
+ const value = Reflect.get(database, prop, database);
21
+ if (typeof value === 'function') {
22
+ return value.bind(database);
23
+ }
24
+ return value;
21
25
  }
22
26
  const currentDatabase = database.current();
23
27
  const value = Reflect.get(currentDatabase, prop, currentDatabase);
@@ -92,7 +96,8 @@ class DrizzleDatabase {
92
96
  * @remarks
93
97
  * This compatibility helper is used by `DrizzleModule` provider wiring. Application code should prefer
94
98
  * `DrizzleModule.forRoot(...)` or `DrizzleModule.forRootAsync(...)`, then type injected repository handles as
95
- * `DrizzleDatabaseFacade<TDatabase>` when direct Drizzle methods are needed.
99
+ * `DrizzleDatabaseFacade<TDatabase>` when direct Drizzle methods are needed. Wrapper and lifecycle methods remain
100
+ * bound to the lifecycle owner while unknown Drizzle query properties forward to the ambient `current()` handle.
96
101
  *
97
102
  * @param database Root Drizzle database handle registered in the module.
98
103
  * @param dispose Optional shutdown hook used to close pools or driver resources.
@@ -116,7 +121,11 @@ class DrizzleDatabase {
116
121
  * @returns The transaction-scoped database inside an active boundary, or the root database outside one.
117
122
  */
118
123
  current() {
119
- 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;
120
129
  }
121
130
 
122
131
  /** Aborts active request transactions, waits for settlement, then runs the optional dispose hook. */
@@ -180,67 +189,162 @@ class DrizzleDatabase {
180
189
  async executeTransaction(fn, options, requestScoped, signal) {
181
190
  const current = this.transactions.getStore();
182
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
+ }
183
201
  if (requestScoped) {
184
202
  this.assertRequestTransactionsAvailable();
185
203
  } else {
186
204
  this.assertTransactionsAvailable();
187
205
  }
188
- if (options !== undefined) {
189
- throw new Error(NESTED_TRANSACTION_OPTIONS_NOT_SUPPORTED_ERROR);
190
- }
191
206
  if (requestScoped) {
192
207
  return this.executeNestedRequestTransaction(current, fn, signal);
193
208
  }
194
- return fn();
195
209
  }
196
210
  if (!requestScoped) {
197
- this.assertTransactionsAvailable();
211
+ return this.executeManualRootTransaction(fn, options);
198
212
  }
199
- const transactionRunner = this.resolveTransactionRunner();
200
- if (!transactionRunner) {
201
- if (requestScoped) {
202
- return this.executeRequestFallback(fn, signal);
213
+ return this.executeRequestRootTransaction(fn, options, signal);
214
+ }
215
+ async executeManualRootTransaction(fn, options) {
216
+ const deferredRequestTransactionSettlements = new Set();
217
+ const fallbackTransactionOwner = {
218
+ closed: false,
219
+ callbackSettlements: new Set(),
220
+ requestTransactionSettlements: new Set()
221
+ };
222
+ const activeTransactionScope = this.trackAvailableTransactionScope();
223
+ try {
224
+ const transactionRunner = this.resolveTransactionRunner();
225
+ if (!transactionRunner) {
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
+ }
203
235
  }
204
- return fn();
205
- }
206
- if (!requestScoped) {
207
- const deferredRequestTransactionSettlements = new Set();
208
- const activeTransactionScope = this.trackActiveTransactionScope();
209
- try {
210
- return await transactionRunner(transactionDatabase => this.transactions.run({
211
- database: transactionDatabase,
212
- deferredRequestTransactionSettlements
213
- }, fn), options);
214
- } finally {
215
- for (const handle of deferredRequestTransactionSettlements) {
216
- this.untrackActiveRequestTransaction(handle);
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);
217
245
  }
218
- activeTransactionScope.settle();
246
+ }, options);
247
+ } finally {
248
+ await this.closeTransactionBoundaryOwner(fallbackTransactionOwner);
249
+ for (const handle of fallbackTransactionOwner.requestTransactionSettlements) {
250
+ this.untrackActiveRequestTransaction(handle);
219
251
  }
252
+ for (const handle of deferredRequestTransactionSettlements) {
253
+ this.untrackActiveRequestTransaction(handle);
254
+ }
255
+ activeTransactionScope.settle();
220
256
  }
221
- return this.executeRequestTransaction(transactionRunner, fn, options, signal);
222
257
  }
223
258
  async executeRequestTransaction(transactionRunner, fn, options, signal) {
224
259
  this.assertRequestTransactionsAvailable();
225
260
  const abortContext = createRequestAbortContext(signal);
226
261
  const active = this.trackActiveRequestTransaction(abortContext.controller);
262
+ const transactionBoundaryOwner = {
263
+ closed: false,
264
+ callbackSettlements: new Set(),
265
+ requestTransactionSettlements: new Set()
266
+ };
227
267
  try {
228
- const result = await transactionRunner(transactionDatabase => this.transactions.run({
229
- database: transactionDatabase,
230
- requestAbortSignal: abortContext.signal
231
- }, () => 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);
232
280
  this.throwIfRequestAborted(abortContext.signal);
233
281
  return result;
234
282
  } finally {
283
+ transactionBoundaryOwner.closed = true;
235
284
  abortContext.cleanup();
236
285
  this.untrackActiveRequestTransaction(active);
237
286
  }
238
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
+ }
239
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
+ };
240
344
  if (current.requestAbortSignal) {
241
345
  const abortSignalView = createRequestAbortSignalView(current.requestAbortSignal, signal);
242
346
  try {
243
- const result = await raceWithAbort(fn, abortSignalView.signal);
347
+ const result = await raceWithAbort(runCallback, abortSignalView.signal);
244
348
  this.throwIfRequestAborted(abortSignalView.signal);
245
349
  return result;
246
350
  } finally {
@@ -250,17 +354,27 @@ class DrizzleDatabase {
250
354
  this.assertRequestTransactionsAvailable();
251
355
  const abortContext = createRequestAbortContext(signal);
252
356
  const active = this.trackActiveRequestTransaction(abortContext.controller);
253
- 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
+ }
254
365
  try {
255
366
  const result = await this.transactions.run({
256
367
  database: current.database,
257
- requestAbortSignal: abortContext.signal
258
- }, () => raceWithAbort(fn, abortContext.signal));
368
+ fallbackTransactionOwner,
369
+ inheritedRequestAbortSignal: current.inheritedRequestAbortSignal,
370
+ requestAbortSignal: abortContext.signal,
371
+ transactionBoundaryOwner: current.transactionBoundaryOwner
372
+ }, () => raceWithAbort(runCallback, abortContext.signal));
259
373
  this.throwIfRequestAborted(abortContext.signal);
260
374
  return result;
261
375
  } finally {
262
376
  abortContext.cleanup();
263
- if (current.deferredRequestTransactionSettlements) {
377
+ if (ownerDefersSettlement) {
264
378
  this.markRequestTransactionInactiveForStatus(active);
265
379
  } else {
266
380
  this.untrackActiveRequestTransaction(active);
@@ -270,16 +384,46 @@ class DrizzleDatabase {
270
384
  async executeRequestFallback(fn, signal) {
271
385
  this.assertRequestTransactionsAvailable();
272
386
  const abortContext = createRequestAbortContext(signal);
387
+ if (abortContext.signal.aborted) {
388
+ const abortError = createAbortError(abortContext.signal.reason);
389
+ abortContext.cleanup();
390
+ throw abortError;
391
+ }
273
392
  const active = this.trackActiveRequestTransaction(abortContext.controller);
393
+ const fallbackTransactionOwner = {
394
+ closed: false,
395
+ callbackSettlements: new Set(),
396
+ requestTransactionSettlements: new Set()
397
+ };
274
398
  try {
275
- 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);
276
415
  this.throwIfRequestAborted(abortContext.signal);
277
416
  return result;
278
417
  } finally {
279
418
  abortContext.cleanup();
280
- this.untrackActiveRequestTransaction(active);
281
419
  }
282
420
  }
421
+ async closeTransactionBoundaryOwner(owner) {
422
+ while (owner.callbackSettlements.size > 0) {
423
+ await Promise.all(owner.callbackSettlements);
424
+ }
425
+ owner.closed = true;
426
+ }
283
427
  assertRequestTransactionsAvailable() {
284
428
  if (this.lifecycleState !== 'ready') {
285
429
  throw new Error(REQUEST_TRANSACTION_UNAVAILABLE_ERROR);
@@ -328,6 +472,10 @@ class DrizzleDatabase {
328
472
  }
329
473
  };
330
474
  }
475
+ trackAvailableTransactionScope() {
476
+ this.assertTransactionsAvailable();
477
+ return this.trackActiveTransactionScope();
478
+ }
331
479
  resolveTransactionRunner() {
332
480
  if (typeof this.database.transaction !== 'function') {
333
481
  if (this.databaseOptions.strictTransactions) {
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
+ }