@fragno-dev/db 0.2.0 → 0.2.2
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/.turbo/turbo-build.log +34 -30
- package/CHANGELOG.md +49 -0
- package/dist/adapters/generic-sql/query/where-builder.js +1 -1
- package/dist/db-fragment-definition-builder.d.ts +31 -39
- package/dist/db-fragment-definition-builder.d.ts.map +1 -1
- package/dist/db-fragment-definition-builder.js +20 -16
- package/dist/db-fragment-definition-builder.js.map +1 -1
- package/dist/fragments/internal-fragment.d.ts +94 -8
- package/dist/fragments/internal-fragment.d.ts.map +1 -1
- package/dist/fragments/internal-fragment.js +56 -55
- package/dist/fragments/internal-fragment.js.map +1 -1
- package/dist/hooks/hooks.d.ts +5 -3
- package/dist/hooks/hooks.d.ts.map +1 -1
- package/dist/hooks/hooks.js +38 -37
- package/dist/hooks/hooks.js.map +1 -1
- package/dist/mod.d.ts +3 -3
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +4 -4
- package/dist/mod.js.map +1 -1
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts +367 -80
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/execute-unit-of-work.js +448 -148
- package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.d.ts +35 -11
- package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.js +49 -19
- package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
- package/dist/query/value-decoding.js +1 -1
- package/dist/schema/create.d.ts +2 -3
- package/dist/schema/create.d.ts.map +1 -1
- package/dist/schema/create.js +2 -5
- package/dist/schema/create.js.map +1 -1
- package/dist/schema/generate-id.d.ts +20 -0
- package/dist/schema/generate-id.d.ts.map +1 -0
- package/dist/schema/generate-id.js +28 -0
- package/dist/schema/generate-id.js.map +1 -0
- package/dist/sql-driver/dialects/durable-object-dialect.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +1 -0
- package/src/adapters/drizzle/drizzle-adapter-sqlite3.test.ts +41 -25
- package/src/adapters/generic-sql/test/generic-drizzle-adapter-sqlite3.test.ts +39 -25
- package/src/db-fragment-definition-builder.test.ts +58 -42
- package/src/db-fragment-definition-builder.ts +78 -88
- package/src/db-fragment-instantiator.test.ts +64 -88
- package/src/db-fragment-integration.test.ts +292 -142
- package/src/fragments/internal-fragment.test.ts +272 -266
- package/src/fragments/internal-fragment.ts +155 -122
- package/src/hooks/hooks.test.ts +268 -264
- package/src/hooks/hooks.ts +74 -63
- package/src/mod.ts +14 -4
- package/src/query/unit-of-work/execute-unit-of-work.test.ts +1582 -998
- package/src/query/unit-of-work/execute-unit-of-work.ts +1746 -343
- package/src/query/unit-of-work/tx-builder.test.ts +1041 -0
- package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +269 -21
- package/src/query/unit-of-work/unit-of-work.test.ts +64 -0
- package/src/query/unit-of-work/unit-of-work.ts +65 -30
- package/src/schema/create.ts +2 -5
- package/src/schema/generate-id.test.ts +57 -0
- package/src/schema/generate-id.ts +38 -0
- package/src/shared/config.ts +0 -10
- package/src/shared/connection-pool.ts +0 -24
- package/src/shared/prisma.ts +0 -45
package/dist/hooks/hooks.js
CHANGED
|
@@ -25,7 +25,7 @@ function prepareHookMutations(uow, config) {
|
|
|
25
25
|
lastAttemptAt: null,
|
|
26
26
|
nextRetryAt: null,
|
|
27
27
|
error: null,
|
|
28
|
-
nonce: uow.
|
|
28
|
+
nonce: uow.idempotencyKey
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
31
|
}
|
|
@@ -36,50 +36,51 @@ function prepareHookMutations(uow, config) {
|
|
|
36
36
|
async function processHooks(config) {
|
|
37
37
|
const { hooks, namespace, internalFragment, defaultRetryPolicy } = config;
|
|
38
38
|
const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });
|
|
39
|
+
const pendingEvents = await internalFragment.inContext(async function() {
|
|
40
|
+
return await this.handlerTx().withServiceCalls(() => [internalFragment.services.hookService.getPendingHookEvents(namespace)]).transform(({ serviceResult: [events] }) => events).execute();
|
|
41
|
+
});
|
|
42
|
+
if (pendingEvents.length === 0) return;
|
|
43
|
+
const processedEvents = await Promise.allSettled(pendingEvents.map(async (event) => {
|
|
44
|
+
const hookFn = hooks[event.hookName];
|
|
45
|
+
if (!hookFn) return {
|
|
46
|
+
eventId: event.id,
|
|
47
|
+
status: "failed",
|
|
48
|
+
error: `Hook '${event.hookName}' not found in hooks map`,
|
|
49
|
+
attempts: event.attempts,
|
|
50
|
+
maxAttempts: event.maxAttempts
|
|
51
|
+
};
|
|
52
|
+
try {
|
|
53
|
+
const hookContext = { idempotencyKey: event.idempotencyKey };
|
|
54
|
+
await hookFn.call(hookContext, event.payload);
|
|
55
|
+
return {
|
|
56
|
+
eventId: event.id,
|
|
57
|
+
status: "completed"
|
|
58
|
+
};
|
|
59
|
+
} catch (error) {
|
|
60
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
61
|
+
return {
|
|
62
|
+
eventId: event.id,
|
|
63
|
+
status: "failed",
|
|
64
|
+
error: errorMessage,
|
|
65
|
+
attempts: event.attempts,
|
|
66
|
+
maxAttempts: event.maxAttempts
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}));
|
|
39
70
|
await internalFragment.inContext(async function() {
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
await executeRetrieve();
|
|
43
|
-
const pendingEvents = await pendingEventsPromise;
|
|
44
|
-
if (pendingEvents.length === 0) return;
|
|
45
|
-
const processedEvents = await Promise.allSettled(pendingEvents.map(async (event) => {
|
|
46
|
-
const hookFn = hooks[event.hookName];
|
|
47
|
-
if (!hookFn) return {
|
|
48
|
-
eventId: event.id,
|
|
49
|
-
status: "failed",
|
|
50
|
-
error: `Hook '${event.hookName}' not found in hooks map`,
|
|
51
|
-
attempts: event.attempts,
|
|
52
|
-
maxAttempts: event.maxAttempts
|
|
53
|
-
};
|
|
54
|
-
try {
|
|
55
|
-
const hookContext = { nonce: event.nonce };
|
|
56
|
-
await hookFn.call(hookContext, event.payload);
|
|
57
|
-
return {
|
|
58
|
-
eventId: event.id,
|
|
59
|
-
status: "completed"
|
|
60
|
-
};
|
|
61
|
-
} catch (error) {
|
|
62
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
63
|
-
return {
|
|
64
|
-
eventId: event.id,
|
|
65
|
-
status: "failed",
|
|
66
|
-
error: errorMessage,
|
|
67
|
-
attempts: event.attempts,
|
|
68
|
-
maxAttempts: event.maxAttempts
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
}));
|
|
71
|
+
await this.handlerTx().withServiceCalls(() => {
|
|
72
|
+
const txResults = [];
|
|
72
73
|
for (const processedEvent of processedEvents) {
|
|
73
74
|
if (processedEvent.status === "rejected") continue;
|
|
74
75
|
const { eventId, status } = processedEvent.value;
|
|
75
|
-
if (status === "completed") internalFragment.services.hookService.markHookCompleted(eventId);
|
|
76
|
+
if (status === "completed") txResults.push(internalFragment.services.hookService.markHookCompleted(eventId));
|
|
76
77
|
else if (status === "failed") {
|
|
77
78
|
const { error, attempts } = processedEvent.value;
|
|
78
|
-
internalFragment.services.hookService.markHookFailed(eventId, error, attempts, retryPolicy);
|
|
79
|
+
txResults.push(internalFragment.services.hookService.markHookFailed(eventId, error, attempts, retryPolicy));
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
|
-
|
|
82
|
-
});
|
|
82
|
+
return txResults;
|
|
83
|
+
}).execute();
|
|
83
84
|
});
|
|
84
85
|
}
|
|
85
86
|
|
package/dist/hooks/hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","names":["hookContext: HookContext"],"sources":["../../src/hooks/hooks.ts"],"sourcesContent":["import type { RetryPolicy } from \"../query/unit-of-work/retry-policy\";\nimport { ExponentialBackoffRetryPolicy } from \"../query/unit-of-work/retry-policy\";\nimport type { IUnitOfWork } from \"../query/unit-of-work/unit-of-work\";\nimport type { InternalFragmentInstance } from \"../fragments/internal-fragment\";\n\n/**\n * Context available in hook functions via `this`.\n * Contains the
|
|
1
|
+
{"version":3,"file":"hooks.js","names":["hookContext: HookContext","txResults: TxResult<void>[]"],"sources":["../../src/hooks/hooks.ts"],"sourcesContent":["import type { RetryPolicy } from \"../query/unit-of-work/retry-policy\";\nimport { ExponentialBackoffRetryPolicy } from \"../query/unit-of-work/retry-policy\";\nimport type { IUnitOfWork } from \"../query/unit-of-work/unit-of-work\";\nimport type { InternalFragmentInstance } from \"../fragments/internal-fragment\";\nimport type { TxResult } from \"../query/unit-of-work/execute-unit-of-work\";\n\n/**\n * Context available in hook functions via `this`.\n * Contains the idempotency key for idempotency and database access.\n */\nexport interface HookContext {\n /**\n * Unique idempotency key for this transaction.\n * Use this for idempotency checks in your hook implementation.\n */\n idempotencyKey: string;\n}\n\n/**\n * A hook function signature.\n * Hooks receive a typed payload and access context via `this`.\n */\nexport type HookFn<TPayload = unknown> = (payload: TPayload) => void | Promise<void>;\n\n/**\n * Map of hook names to hook functions.\n * Used for type-safe hook definitions and triggering.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type HooksMap = Record<string, HookFn<any>>;\n\n/**\n * Extract the payload type from a hook function.\n */\nexport type HookPayload<T> = T extends HookFn<infer P> ? P : never;\n\n/**\n * Options for triggering a hook.\n */\nexport interface TriggerHookOptions {\n /**\n * Optional retry policy override for this specific hook trigger.\n * If not provided, uses the default retry policy.\n */\n retryPolicy?: RetryPolicy;\n}\n\n/**\n * Internal representation of a triggered hook.\n * Stored in the Unit of Work before execution.\n */\nexport interface TriggeredHook {\n hookName: string;\n payload: unknown;\n options?: TriggerHookOptions;\n}\n\n/**\n * Configuration for hook processing.\n */\nexport interface HookProcessorConfig {\n hooks: HooksMap;\n namespace: string;\n internalFragment: InternalFragmentInstance;\n defaultRetryPolicy?: RetryPolicy;\n}\n\n/**\n * Add hook events as mutation operations to the UOW.\n * This should be called before executeMutations() so hook records are created\n * in the same transaction as the user's mutations.\n */\nexport function prepareHookMutations(uow: IUnitOfWork, config: HookProcessorConfig): void {\n const { namespace, internalFragment, defaultRetryPolicy } = config;\n const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });\n\n const triggeredHooks = uow.getTriggeredHooks();\n\n if (triggeredHooks.length === 0) {\n return;\n }\n\n const internalSchema = internalFragment.$internal.deps.schema;\n const internalUow = uow.forSchema(internalSchema);\n\n for (const hook of triggeredHooks) {\n const hookRetryPolicy = hook.options?.retryPolicy ?? retryPolicy;\n const maxAttempts = hookRetryPolicy.shouldRetry(4) ? 5 : 1;\n internalUow.create(\"fragno_hooks\", {\n namespace,\n hookName: hook.hookName,\n payload: hook.payload,\n status: \"pending\",\n attempts: 0,\n maxAttempts,\n lastAttemptAt: null,\n nextRetryAt: null,\n error: null,\n nonce: uow.idempotencyKey,\n });\n }\n}\n\n/**\n * Process pending hook events after the transaction has committed.\n * This should be called in the onSuccess callback after executeMutations().\n */\nexport async function processHooks(config: HookProcessorConfig): Promise<void> {\n const { hooks, namespace, internalFragment, defaultRetryPolicy } = config;\n const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });\n\n // Get pending events\n const pendingEvents = await internalFragment.inContext(async function () {\n return await this.handlerTx()\n .withServiceCalls(\n () => [internalFragment.services.hookService.getPendingHookEvents(namespace)] as const,\n )\n .transform(({ serviceResult: [events] }) => events)\n .execute();\n });\n\n if (pendingEvents.length === 0) {\n return;\n }\n\n // Process events (async work outside transaction)\n const processedEvents = await Promise.allSettled(\n pendingEvents.map(async (event) => {\n const hookFn = hooks[event.hookName];\n if (!hookFn) {\n return {\n eventId: event.id,\n status: \"failed\" as const,\n error: `Hook '${event.hookName}' not found in hooks map`,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n };\n }\n\n try {\n const hookContext: HookContext = { idempotencyKey: event.idempotencyKey };\n await hookFn.call(hookContext, event.payload);\n return {\n eventId: event.id,\n status: \"completed\" as const,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n eventId: event.id,\n status: \"failed\" as const,\n error: errorMessage,\n attempts: event.attempts,\n maxAttempts: event.maxAttempts,\n };\n }\n }),\n );\n\n // Mark events as completed/failed\n await internalFragment.inContext(async function () {\n await this.handlerTx()\n .withServiceCalls(() => {\n const txResults: TxResult<void>[] = [];\n for (const processedEvent of processedEvents) {\n if (processedEvent.status === \"rejected\") {\n continue;\n }\n\n const { eventId, status } = processedEvent.value;\n\n if (status === \"completed\") {\n txResults.push(internalFragment.services.hookService.markHookCompleted(eventId));\n } else if (status === \"failed\") {\n const { error, attempts } = processedEvent.value;\n txResults.push(\n internalFragment.services.hookService.markHookFailed(\n eventId,\n error,\n attempts,\n retryPolicy,\n ),\n );\n }\n }\n return txResults;\n })\n .execute();\n });\n}\n"],"mappings":";;;;;;;;AAwEA,SAAgB,qBAAqB,KAAkB,QAAmC;CACxF,MAAM,EAAE,WAAW,kBAAkB,uBAAuB;CAC5D,MAAM,cAAc,sBAAsB,IAAI,8BAA8B,EAAE,YAAY,GAAG,CAAC;CAE9F,MAAM,iBAAiB,IAAI,mBAAmB;AAE9C,KAAI,eAAe,WAAW,EAC5B;CAGF,MAAM,iBAAiB,iBAAiB,UAAU,KAAK;CACvD,MAAM,cAAc,IAAI,UAAU,eAAe;AAEjD,MAAK,MAAM,QAAQ,gBAAgB;EAEjC,MAAM,eADkB,KAAK,SAAS,eAAe,aACjB,YAAY,EAAE,GAAG,IAAI;AACzD,cAAY,OAAO,gBAAgB;GACjC;GACA,UAAU,KAAK;GACf,SAAS,KAAK;GACd,QAAQ;GACR,UAAU;GACV;GACA,eAAe;GACf,aAAa;GACb,OAAO;GACP,OAAO,IAAI;GACZ,CAAC;;;;;;;AAQN,eAAsB,aAAa,QAA4C;CAC7E,MAAM,EAAE,OAAO,WAAW,kBAAkB,uBAAuB;CACnE,MAAM,cAAc,sBAAsB,IAAI,8BAA8B,EAAE,YAAY,GAAG,CAAC;CAG9F,MAAM,gBAAgB,MAAM,iBAAiB,UAAU,iBAAkB;AACvE,SAAO,MAAM,KAAK,WAAW,CAC1B,uBACO,CAAC,iBAAiB,SAAS,YAAY,qBAAqB,UAAU,CAAC,CAC9E,CACA,WAAW,EAAE,eAAe,CAAC,cAAc,OAAO,CAClD,SAAS;GACZ;AAEF,KAAI,cAAc,WAAW,EAC3B;CAIF,MAAM,kBAAkB,MAAM,QAAQ,WACpC,cAAc,IAAI,OAAO,UAAU;EACjC,MAAM,SAAS,MAAM,MAAM;AAC3B,MAAI,CAAC,OACH,QAAO;GACL,SAAS,MAAM;GACf,QAAQ;GACR,OAAO,SAAS,MAAM,SAAS;GAC/B,UAAU,MAAM;GAChB,aAAa,MAAM;GACpB;AAGH,MAAI;GACF,MAAMA,cAA2B,EAAE,gBAAgB,MAAM,gBAAgB;AACzE,SAAM,OAAO,KAAK,aAAa,MAAM,QAAQ;AAC7C,UAAO;IACL,SAAS,MAAM;IACf,QAAQ;IACT;WACM,OAAO;GACd,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AAC3E,UAAO;IACL,SAAS,MAAM;IACf,QAAQ;IACR,OAAO;IACP,UAAU,MAAM;IAChB,aAAa,MAAM;IACpB;;GAEH,CACH;AAGD,OAAM,iBAAiB,UAAU,iBAAkB;AACjD,QAAM,KAAK,WAAW,CACnB,uBAAuB;GACtB,MAAMC,YAA8B,EAAE;AACtC,QAAK,MAAM,kBAAkB,iBAAiB;AAC5C,QAAI,eAAe,WAAW,WAC5B;IAGF,MAAM,EAAE,SAAS,WAAW,eAAe;AAE3C,QAAI,WAAW,YACb,WAAU,KAAK,iBAAiB,SAAS,YAAY,kBAAkB,QAAQ,CAAC;aACvE,WAAW,UAAU;KAC9B,MAAM,EAAE,OAAO,aAAa,eAAe;AAC3C,eAAU,KACR,iBAAiB,SAAS,YAAY,eACpC,SACA,OACA,UACA,YACD,CACF;;;AAGL,UAAO;IACP,CACD,SAAS;GACZ"}
|
package/dist/mod.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { AnySchema } from "./schema/create.js";
|
|
2
2
|
import { Cursor, CursorData, CursorResult, decodeCursor } from "./query/cursor.js";
|
|
3
3
|
import { ExponentialBackoffRetryPolicy, LinearBackoffRetryPolicy, NoRetryPolicy, RetryPolicy } from "./query/unit-of-work/retry-policy.js";
|
|
4
|
-
import {
|
|
4
|
+
import { BuilderTransformContextWithMutate, BuilderTransformContextWithoutMutate, ConcurrencyConflictError, ExtractServiceFinalResults, ExtractServiceRetrieveResults, HandlerBuilderMutateContext, HandlerTxBuilder, ServiceBuilderMutateContext, ServiceTxBuilder, TxResult, createHandlerTxBuilder, createServiceTxBuilder } from "./query/unit-of-work/execute-unit-of-work.js";
|
|
5
5
|
import { DatabaseFragmentContext, DatabaseFragmentDefinitionBuilder, DatabaseHandlerContext, FragnoPublicConfigWithDatabase, ImplicitDatabaseDependencies } from "./db-fragment-definition-builder.js";
|
|
6
|
+
import { withDatabase } from "./with-database.js";
|
|
6
7
|
import { InternalFragmentInstance, internalFragmentDef } from "./fragments/internal-fragment.js";
|
|
7
8
|
import { HookContext, HookFn, HookPayload, HooksMap, TriggerHookOptions } from "./hooks/hooks.js";
|
|
8
9
|
import { IUnitOfWork, IUnitOfWorkRestricted, TypedUnitOfWork, UOWCompiler, UOWDecoder, UOWExecutor, UnitOfWork, createUnitOfWork } from "./query/unit-of-work/unit-of-work.js";
|
|
9
10
|
import { DatabaseAdapter } from "./adapters/adapters.js";
|
|
10
|
-
import { withDatabase } from "./with-database.js";
|
|
11
11
|
import { AnyFragnoInstantiatedFragment, BoundServices, FragnoInstantiatedFragment } from "@fragno-dev/core";
|
|
12
12
|
|
|
13
13
|
//#region src/mod.d.ts
|
|
@@ -61,5 +61,5 @@ type AnyFragnoInstantiatedDatabaseFragment = FragnoInstantiatedFragment<any, Imp
|
|
|
61
61
|
*/
|
|
62
62
|
declare function migrate(fragment: AnyFragnoInstantiatedDatabaseFragment): Promise<void>;
|
|
63
63
|
//#endregion
|
|
64
|
-
export { AnyFragnoInstantiatedDatabaseFragment, type BoundServices, CreateFragnoDatabaseDefinitionOptions, Cursor, type CursorData, type CursorResult, type DatabaseAdapter, type DatabaseFragmentContext, DatabaseFragmentDefinitionBuilder, type DatabaseHandlerContext as DatabaseRequestContext,
|
|
64
|
+
export { AnyFragnoInstantiatedDatabaseFragment, type BoundServices, type BuilderTransformContextWithMutate, type BuilderTransformContextWithoutMutate, ConcurrencyConflictError, CreateFragnoDatabaseDefinitionOptions, Cursor, type CursorData, type CursorResult, type DatabaseAdapter, type DatabaseFragmentContext, DatabaseFragmentDefinitionBuilder, type DatabaseHandlerContext as DatabaseRequestContext, ExponentialBackoffRetryPolicy, type ExtractServiceFinalResults, type ExtractServiceRetrieveResults, FragnoDatabase, type FragnoPublicConfigWithDatabase, type HandlerBuilderMutateContext, HandlerTxBuilder, type HookContext, type HookFn, type HookPayload, type HooksMap, type IUnitOfWork, type IUnitOfWorkRestricted, type ImplicitDatabaseDependencies, type InternalFragmentInstance, LinearBackoffRetryPolicy, NoRetryPolicy, type RetryPolicy, type ServiceBuilderMutateContext, ServiceTxBuilder, type TriggerHookOptions, type TxResult, TypedUnitOfWork, type UOWCompiler, type UOWDecoder, type UOWExecutor, UnitOfWork, createHandlerTxBuilder, createServiceTxBuilder, createUnitOfWork, decodeCursor, fragnoDatabaseFakeSymbol, fragnoDatabaseLibraryVersion, internalFragmentDef, isFragnoDatabase, migrate, withDatabase };
|
|
65
65
|
//# sourceMappingURL=mod.d.ts.map
|
package/dist/mod.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.ts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;cAiBa;cACA;UAEI,gDAAgD;;EAHpD,MAAA,EAKH,CALG;AACb;AAEiB,iBAKD,gBAAA,CALC,KAAqC,EAAA,OAAA,CAAW,EAAA,KAAA,IAKN,cAHhD,CAG+D,SAH/D,CAAA;AAGX;AAmBA;;;AAKgF,cALnE,cAKmE,CAAA,gBALpC,SAKoC,EAAA,aAAA,IAAA,CAAA,CAAA;EAAhB,CAAA,OAAA;EAMrB,WAAA,CAAA,OAAA,EAAA;IAApC,SAAA,EAAA,MAAA;IAQK,MAAA,EAdwC,CAcxC;IAIqB,OAAA,EAlB+B,eAkB/B,CAlB+C,UAkB/C,CAAA;EAAhB,CAAA;EAAe,KAZzB,wBAAA,GAYyB,EAAA,OAZW,wBAYX;
|
|
1
|
+
{"version":3,"file":"mod.d.ts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;cAiBa;cACA;UAEI,gDAAgD;;EAHpD,MAAA,EAKH,CALG;AACb;AAEiB,iBAKD,gBAAA,CALC,KAAqC,EAAA,OAAA,CAAW,EAAA,KAAA,IAKN,cAHhD,CAG+D,SAH/D,CAAA;AAGX;AAmBA;;;AAKgF,cALnE,cAKmE,CAAA,gBALpC,SAKoC,EAAA,aAAA,IAAA,CAAA,CAAA;EAAhB,CAAA,OAAA;EAMrB,WAAA,CAAA,OAAA,EAAA;IAApC,SAAA,EAAA,MAAA;IAQK,MAAA,EAdwC,CAcxC;IAIqB,OAAA,EAlB+B,eAkB/B,CAlB+C,UAkB/C,CAAA;EAAhB,CAAA;EAAe,KAZzB,wBAAA,GAYyB,EAAA,OAZW,wBAYX;EA2DpB,IAAA,SAAA,CAAA,CAAA,EAAA,MAAA;EAGmB,IAAA,MAAA,CAAA,CAAA,EAlEnB,CAkEmB;EAA7B,IAAA,OAAA,CAAA,CAAA,EA9De,eA8Df,CA9D+B,UA8D/B,CAAA;;KAHU,qCAAA,GAAwC,gCAGlD,6BAA6B,gCAS7B;oBAEoB;IAA6B,eAAe;;;;;;;;;;;;;;;;;;;;;;;iBAyB5C,OAAA,WAAkB,wCAAwC"}
|
package/dist/mod.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Cursor, decodeCursor } from "./query/cursor.js";
|
|
2
|
-
import { TypedUnitOfWork, UnitOfWork, createUnitOfWork } from "./query/unit-of-work/unit-of-work.js";
|
|
3
1
|
import { ExponentialBackoffRetryPolicy, LinearBackoffRetryPolicy, NoRetryPolicy } from "./query/unit-of-work/retry-policy.js";
|
|
4
|
-
import {
|
|
2
|
+
import { ConcurrencyConflictError, HandlerTxBuilder, ServiceTxBuilder, createHandlerTxBuilder, createServiceTxBuilder } from "./query/unit-of-work/execute-unit-of-work.js";
|
|
5
3
|
import { DatabaseFragmentDefinitionBuilder } from "./db-fragment-definition-builder.js";
|
|
4
|
+
import { Cursor, decodeCursor } from "./query/cursor.js";
|
|
6
5
|
import { getSchemaVersionFromDatabase, internalFragmentDef } from "./fragments/internal-fragment.js";
|
|
7
6
|
import { withDatabase } from "./with-database.js";
|
|
7
|
+
import { TypedUnitOfWork, UnitOfWork, createUnitOfWork } from "./query/unit-of-work/unit-of-work.js";
|
|
8
8
|
|
|
9
9
|
//#region src/mod.ts
|
|
10
10
|
const fragnoDatabaseFakeSymbol = "$fragno-database";
|
|
@@ -84,5 +84,5 @@ async function migrate(fragment) {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
//#endregion
|
|
87
|
-
export { Cursor, DatabaseFragmentDefinitionBuilder, ExponentialBackoffRetryPolicy, FragnoDatabase, LinearBackoffRetryPolicy, NoRetryPolicy, TypedUnitOfWork, UnitOfWork, createUnitOfWork, decodeCursor,
|
|
87
|
+
export { ConcurrencyConflictError, Cursor, DatabaseFragmentDefinitionBuilder, ExponentialBackoffRetryPolicy, FragnoDatabase, HandlerTxBuilder, LinearBackoffRetryPolicy, NoRetryPolicy, ServiceTxBuilder, TypedUnitOfWork, UnitOfWork, createHandlerTxBuilder, createServiceTxBuilder, createUnitOfWork, decodeCursor, fragnoDatabaseFakeSymbol, fragnoDatabaseLibraryVersion, internalFragmentDef, isFragnoDatabase, migrate, withDatabase };
|
|
88
88
|
//# sourceMappingURL=mod.js.map
|
package/dist/mod.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.js","names":["#namespace","#schema","#adapter"],"sources":["../src/mod.ts"],"sourcesContent":["import type { DatabaseAdapter } from \"./adapters/adapters\";\nimport type { AnySchema } from \"./schema/create\";\nimport type { CursorResult } from \"./query/cursor\";\nimport { Cursor } from \"./query/cursor\";\nimport type { FragnoInstantiatedFragment, AnyFragnoInstantiatedFragment } from \"@fragno-dev/core\";\nimport type {\n FragnoPublicConfigWithDatabase,\n ImplicitDatabaseDependencies,\n} from \"./db-fragment-definition-builder\";\nimport {\n getSchemaVersionFromDatabase,\n type InternalFragmentInstance,\n} from \"./fragments/internal-fragment\";\n\nexport type { DatabaseAdapter, CursorResult };\nexport { Cursor };\n\nexport const fragnoDatabaseFakeSymbol = \"$fragno-database\" as const;\nexport const fragnoDatabaseLibraryVersion = \"0.1\" as const;\n\nexport interface CreateFragnoDatabaseDefinitionOptions<T extends AnySchema> {\n namespace: string;\n schema: T;\n}\n\nexport function isFragnoDatabase(value: unknown): value is FragnoDatabase<AnySchema> {\n if (value instanceof FragnoDatabase) {\n return true;\n }\n\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n\n return (\n fragnoDatabaseFakeSymbol in value &&\n value[fragnoDatabaseFakeSymbol] === fragnoDatabaseFakeSymbol\n );\n}\n\n/**\n * A Fragno database instance with a bound adapter.\n * Created from a FragnoDatabaseDefinition by calling .create(adapter).\n */\nexport class FragnoDatabase<const T extends AnySchema, TUOWConfig = void> {\n #namespace: string;\n #schema: T;\n #adapter: DatabaseAdapter<TUOWConfig>;\n\n constructor(options: { namespace: string; schema: T; adapter: DatabaseAdapter<TUOWConfig> }) {\n this.#namespace = options.namespace;\n this.#schema = options.schema;\n this.#adapter = options.adapter;\n }\n\n get [fragnoDatabaseFakeSymbol](): typeof fragnoDatabaseFakeSymbol {\n return fragnoDatabaseFakeSymbol;\n }\n\n get namespace() {\n return this.#namespace;\n }\n\n get schema() {\n return this.#schema;\n }\n\n get adapter(): DatabaseAdapter<TUOWConfig> {\n return this.#adapter;\n }\n}\n\nexport {\n DatabaseFragmentDefinitionBuilder,\n type FragnoPublicConfigWithDatabase,\n type DatabaseFragmentContext,\n type DatabaseHandlerContext as DatabaseRequestContext,\n type ImplicitDatabaseDependencies,\n} from \"./db-fragment-definition-builder\";\n\nexport { withDatabase } from \"./with-database\";\n\nexport { decodeCursor, type CursorData } from \"./query/cursor\";\n\nexport {\n createUnitOfWork,\n UnitOfWork,\n TypedUnitOfWork,\n type IUnitOfWork,\n type IUnitOfWorkRestricted,\n type UOWCompiler,\n type UOWExecutor,\n type UOWDecoder,\n} from \"./query/unit-of-work/unit-of-work\";\n\nexport {\n type RetryPolicy,\n NoRetryPolicy,\n ExponentialBackoffRetryPolicy,\n LinearBackoffRetryPolicy,\n} from \"./query/unit-of-work/retry-policy\";\n\nexport {\n
|
|
1
|
+
{"version":3,"file":"mod.js","names":["#namespace","#schema","#adapter"],"sources":["../src/mod.ts"],"sourcesContent":["import type { DatabaseAdapter } from \"./adapters/adapters\";\nimport type { AnySchema } from \"./schema/create\";\nimport type { CursorResult } from \"./query/cursor\";\nimport { Cursor } from \"./query/cursor\";\nimport type { FragnoInstantiatedFragment, AnyFragnoInstantiatedFragment } from \"@fragno-dev/core\";\nimport type {\n FragnoPublicConfigWithDatabase,\n ImplicitDatabaseDependencies,\n} from \"./db-fragment-definition-builder\";\nimport {\n getSchemaVersionFromDatabase,\n type InternalFragmentInstance,\n} from \"./fragments/internal-fragment\";\n\nexport type { DatabaseAdapter, CursorResult };\nexport { Cursor };\n\nexport const fragnoDatabaseFakeSymbol = \"$fragno-database\" as const;\nexport const fragnoDatabaseLibraryVersion = \"0.1\" as const;\n\nexport interface CreateFragnoDatabaseDefinitionOptions<T extends AnySchema> {\n namespace: string;\n schema: T;\n}\n\nexport function isFragnoDatabase(value: unknown): value is FragnoDatabase<AnySchema> {\n if (value instanceof FragnoDatabase) {\n return true;\n }\n\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n\n return (\n fragnoDatabaseFakeSymbol in value &&\n value[fragnoDatabaseFakeSymbol] === fragnoDatabaseFakeSymbol\n );\n}\n\n/**\n * A Fragno database instance with a bound adapter.\n * Created from a FragnoDatabaseDefinition by calling .create(adapter).\n */\nexport class FragnoDatabase<const T extends AnySchema, TUOWConfig = void> {\n #namespace: string;\n #schema: T;\n #adapter: DatabaseAdapter<TUOWConfig>;\n\n constructor(options: { namespace: string; schema: T; adapter: DatabaseAdapter<TUOWConfig> }) {\n this.#namespace = options.namespace;\n this.#schema = options.schema;\n this.#adapter = options.adapter;\n }\n\n get [fragnoDatabaseFakeSymbol](): typeof fragnoDatabaseFakeSymbol {\n return fragnoDatabaseFakeSymbol;\n }\n\n get namespace() {\n return this.#namespace;\n }\n\n get schema() {\n return this.#schema;\n }\n\n get adapter(): DatabaseAdapter<TUOWConfig> {\n return this.#adapter;\n }\n}\n\nexport {\n DatabaseFragmentDefinitionBuilder,\n type FragnoPublicConfigWithDatabase,\n type DatabaseFragmentContext,\n type DatabaseHandlerContext as DatabaseRequestContext,\n type ImplicitDatabaseDependencies,\n} from \"./db-fragment-definition-builder\";\n\nexport { withDatabase } from \"./with-database\";\n\nexport { decodeCursor, type CursorData } from \"./query/cursor\";\n\nexport {\n createUnitOfWork,\n UnitOfWork,\n TypedUnitOfWork,\n type IUnitOfWork,\n type IUnitOfWorkRestricted,\n type UOWCompiler,\n type UOWExecutor,\n type UOWDecoder,\n} from \"./query/unit-of-work/unit-of-work\";\n\nexport {\n type RetryPolicy,\n NoRetryPolicy,\n ExponentialBackoffRetryPolicy,\n LinearBackoffRetryPolicy,\n} from \"./query/unit-of-work/retry-policy\";\n\nexport {\n ConcurrencyConflictError,\n // Builder pattern exports\n ServiceTxBuilder,\n HandlerTxBuilder,\n createServiceTxBuilder,\n createHandlerTxBuilder,\n type TxResult,\n // Builder context types\n type ServiceBuilderMutateContext,\n type HandlerBuilderMutateContext,\n type BuilderTransformContextWithMutate,\n type BuilderTransformContextWithoutMutate,\n type ExtractServiceRetrieveResults,\n type ExtractServiceFinalResults,\n} from \"./query/unit-of-work/execute-unit-of-work\";\n\nexport type { BoundServices } from \"@fragno-dev/core\";\n\nexport { internalFragmentDef } from \"./fragments/internal-fragment\";\nexport type { InternalFragmentInstance } from \"./fragments/internal-fragment\";\n\nexport type { HookContext, HooksMap, HookFn, HookPayload, TriggerHookOptions } from \"./hooks/hooks\";\n\nexport type AnyFragnoInstantiatedDatabaseFragment = FragnoInstantiatedFragment<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n ImplicitDatabaseDependencies<AnySchema>,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n FragnoPublicConfigWithDatabase,\n // Ensure the fragment has the internal fragment linked\n { _fragno_internal: InternalFragmentInstance } & Record<string, AnyFragnoInstantiatedFragment>\n>;\n\n/**\n * Helper function to run migrations for a database fragment.\n * Extracts the database adapter, schema, and namespace from the fragment and runs migrations.\n * This function:\n * 1. Ensures the internal settings fragment is migrated first\n * 2. Retrieves the current database version from the internal fragment\n * 3. Runs migration from current version to target version\n *\n * @param fragment - The instantiated fragment to run migrations for\n * @throws Error if the fragment doesn't have database support or the adapter doesn't support migrations\n *\n * @example\n * ```typescript\n * const fragment = instantiate(myFragmentDef)\n * .withConfig({})\n * .withRoutes([])\n * .withOptions({ databaseAdapter: myAdapter })\n * .build();\n *\n * await migrate(fragment);\n * ```\n */\nexport async function migrate(fragment: AnyFragnoInstantiatedDatabaseFragment): Promise<void> {\n const { options, deps, linkedFragments } = fragment.$internal;\n const adapter = options.databaseAdapter;\n\n // Check if adapter supports prepareMigrations\n if (!adapter.prepareMigrations) {\n throw new Error(\n \"Database adapter does not support prepareMigrations. Please use an adapter that implements this method.\",\n );\n }\n\n const schema = deps.schema;\n const namespace = deps.namespace;\n\n // Step 1: Ensure the internal fragment (settings table) is migrated first\n const internalFragment = linkedFragments._fragno_internal;\n\n if (!internalFragment) {\n throw new Error(\"Internal fragment not found. Please ensure the internal fragment is linked.\");\n }\n\n if (!(await adapter.isConnectionHealthy())) {\n throw new Error(\n \"Database connection is not healthy. Please check your database connection and try again.\",\n );\n }\n\n const internalDeps = internalFragment.$internal.deps;\n const internalSchema = internalDeps.schema;\n const internalNamespace = internalDeps.namespace;\n\n const internalCurrentVersion = await getSchemaVersionFromDatabase(\n internalFragment,\n internalNamespace,\n );\n\n // Migrate internal fragment if needed\n if (internalCurrentVersion < internalSchema.version) {\n const internalMigrations = adapter.prepareMigrations(internalSchema, internalNamespace);\n await internalMigrations.execute(internalCurrentVersion, internalSchema.version);\n }\n\n // Step 2: Get current database version for this fragment's namespace\n const currentVersion = await getSchemaVersionFromDatabase(internalFragment, namespace);\n\n // Step 3: Run the migration from current version to target version\n const targetVersion = schema.version;\n\n if (currentVersion === targetVersion) {\n return;\n }\n\n if (currentVersion > targetVersion) {\n throw new Error(\n `Cannot migrate backwards: current version (${currentVersion}) > target version (${targetVersion})`,\n );\n }\n\n const migrations = adapter.prepareMigrations(schema, namespace);\n await migrations.execute(currentVersion, targetVersion);\n}\n"],"mappings":";;;;;;;;;AAiBA,MAAa,2BAA2B;AACxC,MAAa,+BAA+B;AAO5C,SAAgB,iBAAiB,OAAoD;AACnF,KAAI,iBAAiB,eACnB,QAAO;AAGT,KAAI,OAAO,UAAU,YAAY,UAAU,KACzC,QAAO;AAGT,QACE,4BAA4B,SAC5B,MAAM,8BAA8B;;;;;;AAQxC,IAAa,iBAAb,MAA0E;CACxE;CACA;CACA;CAEA,YAAY,SAAiF;AAC3F,QAAKA,YAAa,QAAQ;AAC1B,QAAKC,SAAU,QAAQ;AACvB,QAAKC,UAAW,QAAQ;;CAG1B,KAAK,4BAA6D;AAChE,SAAO;;CAGT,IAAI,YAAY;AACd,SAAO,MAAKF;;CAGd,IAAI,SAAS;AACX,SAAO,MAAKC;;CAGd,IAAI,UAAuC;AACzC,SAAO,MAAKC;;;;;;;;;;;;;;;;;;;;;;;;;AAiGhB,eAAsB,QAAQ,UAAgE;CAC5F,MAAM,EAAE,SAAS,MAAM,oBAAoB,SAAS;CACpD,MAAM,UAAU,QAAQ;AAGxB,KAAI,CAAC,QAAQ,kBACX,OAAM,IAAI,MACR,0GACD;CAGH,MAAM,SAAS,KAAK;CACpB,MAAM,YAAY,KAAK;CAGvB,MAAM,mBAAmB,gBAAgB;AAEzC,KAAI,CAAC,iBACH,OAAM,IAAI,MAAM,8EAA8E;AAGhG,KAAI,CAAE,MAAM,QAAQ,qBAAqB,CACvC,OAAM,IAAI,MACR,2FACD;CAGH,MAAM,eAAe,iBAAiB,UAAU;CAChD,MAAM,iBAAiB,aAAa;CACpC,MAAM,oBAAoB,aAAa;CAEvC,MAAM,yBAAyB,MAAM,6BACnC,kBACA,kBACD;AAGD,KAAI,yBAAyB,eAAe,QAE1C,OAD2B,QAAQ,kBAAkB,gBAAgB,kBAAkB,CAC9D,QAAQ,wBAAwB,eAAe,QAAQ;CAIlF,MAAM,iBAAiB,MAAM,6BAA6B,kBAAkB,UAAU;CAGtF,MAAM,gBAAgB,OAAO;AAE7B,KAAI,mBAAmB,cACrB;AAGF,KAAI,iBAAiB,cACnB,OAAM,IAAI,MACR,8CAA8C,eAAe,sBAAsB,cAAc,GAClG;AAIH,OADmB,QAAQ,kBAAkB,QAAQ,UAAU,CAC9C,QAAQ,gBAAgB,cAAc"}
|