@fragno-dev/db 0.1.13 → 0.1.15
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 +179 -132
- package/CHANGELOG.md +30 -0
- package/dist/adapters/adapters.d.ts +27 -1
- package/dist/adapters/adapters.d.ts.map +1 -1
- package/dist/adapters/adapters.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-adapter.d.ts +5 -1
- package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-adapter.js +15 -3
- package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-query.js +7 -5
- package/dist/adapters/drizzle/drizzle-query.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts +0 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.js +76 -44
- package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-decoder.js +23 -16
- package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-executor.js +18 -7
- package/dist/adapters/drizzle/drizzle-uow-executor.js.map +1 -1
- package/dist/adapters/drizzle/generate.d.ts +4 -1
- package/dist/adapters/drizzle/generate.d.ts.map +1 -1
- package/dist/adapters/drizzle/generate.js +11 -18
- package/dist/adapters/drizzle/generate.js.map +1 -1
- package/dist/adapters/drizzle/shared.d.ts +14 -1
- package/dist/adapters/drizzle/shared.d.ts.map +1 -0
- package/dist/adapters/kysely/kysely-adapter.d.ts +5 -1
- package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
- package/dist/adapters/kysely/kysely-adapter.js +14 -3
- package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
- package/dist/adapters/kysely/kysely-query-builder.js +1 -1
- package/dist/adapters/kysely/kysely-query-compiler.js +3 -2
- package/dist/adapters/kysely/kysely-query-compiler.js.map +1 -1
- package/dist/adapters/kysely/kysely-query.d.ts +1 -0
- package/dist/adapters/kysely/kysely-query.d.ts.map +1 -1
- package/dist/adapters/kysely/kysely-query.js +28 -19
- package/dist/adapters/kysely/kysely-query.js.map +1 -1
- package/dist/adapters/kysely/kysely-shared.d.ts +14 -0
- package/dist/adapters/kysely/kysely-shared.d.ts.map +1 -0
- package/dist/adapters/kysely/kysely-shared.js +16 -1
- package/dist/adapters/kysely/kysely-shared.js.map +1 -1
- package/dist/adapters/kysely/kysely-uow-compiler.js +68 -16
- package/dist/adapters/kysely/kysely-uow-compiler.js.map +1 -1
- package/dist/adapters/kysely/kysely-uow-executor.js +8 -4
- package/dist/adapters/kysely/kysely-uow-executor.js.map +1 -1
- package/dist/adapters/kysely/migration/execute-base.js +1 -1
- package/dist/adapters/kysely/migration/execute-base.js.map +1 -1
- package/dist/db-fragment-definition-builder.d.ts +152 -0
- package/dist/db-fragment-definition-builder.d.ts.map +1 -0
- package/dist/db-fragment-definition-builder.js +137 -0
- package/dist/db-fragment-definition-builder.js.map +1 -0
- package/dist/fragments/internal-fragment.d.ts +19 -0
- package/dist/fragments/internal-fragment.d.ts.map +1 -0
- package/dist/fragments/internal-fragment.js +39 -0
- package/dist/fragments/internal-fragment.js.map +1 -0
- package/dist/migration-engine/generation-engine.d.ts.map +1 -1
- package/dist/migration-engine/generation-engine.js +35 -15
- package/dist/migration-engine/generation-engine.js.map +1 -1
- package/dist/mod.d.ts +8 -18
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +7 -34
- package/dist/mod.js.map +1 -1
- package/dist/node_modules/.pnpm/rou3@0.7.8/node_modules/rou3/dist/index.js +165 -0
- package/dist/node_modules/.pnpm/rou3@0.7.8/node_modules/rou3/dist/index.js.map +1 -0
- package/dist/packages/fragno/dist/api/bind-services.js +20 -0
- package/dist/packages/fragno/dist/api/bind-services.js.map +1 -0
- package/dist/packages/fragno/dist/api/error.js +48 -0
- package/dist/packages/fragno/dist/api/error.js.map +1 -0
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js +320 -0
- package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +1 -0
- package/dist/packages/fragno/dist/api/fragment-instantiator.js +487 -0
- package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +1 -0
- package/dist/packages/fragno/dist/api/fragno-response.js +73 -0
- package/dist/packages/fragno/dist/api/fragno-response.js.map +1 -0
- package/dist/packages/fragno/dist/api/internal/response-stream.js +81 -0
- package/dist/packages/fragno/dist/api/internal/response-stream.js.map +1 -0
- package/dist/packages/fragno/dist/api/internal/route.js +10 -0
- package/dist/packages/fragno/dist/api/internal/route.js.map +1 -0
- package/dist/packages/fragno/dist/api/mutable-request-state.js +97 -0
- package/dist/packages/fragno/dist/api/mutable-request-state.js.map +1 -0
- package/dist/packages/fragno/dist/api/request-context-storage.js +43 -0
- package/dist/packages/fragno/dist/api/request-context-storage.js.map +1 -0
- package/dist/packages/fragno/dist/api/request-input-context.js +118 -0
- package/dist/packages/fragno/dist/api/request-input-context.js.map +1 -0
- package/dist/packages/fragno/dist/api/request-middleware.js +83 -0
- package/dist/packages/fragno/dist/api/request-middleware.js.map +1 -0
- package/dist/packages/fragno/dist/api/request-output-context.js +119 -0
- package/dist/packages/fragno/dist/api/request-output-context.js.map +1 -0
- package/dist/packages/fragno/dist/api/route.js +17 -0
- package/dist/packages/fragno/dist/api/route.js.map +1 -0
- package/dist/packages/fragno/dist/internal/symbols.js +10 -0
- package/dist/packages/fragno/dist/internal/symbols.js.map +1 -0
- package/dist/query/cursor.d.ts +10 -2
- package/dist/query/cursor.d.ts.map +1 -1
- package/dist/query/cursor.js +11 -4
- package/dist/query/cursor.js.map +1 -1
- package/dist/query/execute-unit-of-work.d.ts +123 -0
- package/dist/query/execute-unit-of-work.d.ts.map +1 -0
- package/dist/query/execute-unit-of-work.js +184 -0
- package/dist/query/execute-unit-of-work.js.map +1 -0
- package/dist/query/query.d.ts +3 -3
- package/dist/query/query.d.ts.map +1 -1
- package/dist/query/result-transform.js +4 -2
- package/dist/query/result-transform.js.map +1 -1
- package/dist/query/retry-policy.d.ts +88 -0
- package/dist/query/retry-policy.d.ts.map +1 -0
- package/dist/query/retry-policy.js +61 -0
- package/dist/query/retry-policy.js.map +1 -0
- package/dist/query/unit-of-work.d.ts +171 -32
- package/dist/query/unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work.js +530 -133
- package/dist/query/unit-of-work.js.map +1 -1
- package/dist/schema/serialize.js +12 -7
- package/dist/schema/serialize.js.map +1 -1
- package/dist/with-database.d.ts +28 -0
- package/dist/with-database.d.ts.map +1 -0
- package/dist/with-database.js +34 -0
- package/dist/with-database.js.map +1 -0
- package/package.json +10 -3
- package/src/adapters/adapters.ts +30 -0
- package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +86 -17
- package/src/adapters/drizzle/drizzle-adapter-sqlite.test.ts +291 -7
- package/src/adapters/drizzle/drizzle-adapter.test.ts +3 -51
- package/src/adapters/drizzle/drizzle-adapter.ts +35 -7
- package/src/adapters/drizzle/drizzle-query.ts +25 -15
- package/src/adapters/drizzle/drizzle-uow-compiler-mysql.test.ts +1442 -0
- package/src/adapters/drizzle/drizzle-uow-compiler-sqlite.test.ts +1414 -0
- package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +78 -61
- package/src/adapters/drizzle/drizzle-uow-compiler.ts +123 -42
- package/src/adapters/drizzle/drizzle-uow-decoder.ts +34 -27
- package/src/adapters/drizzle/drizzle-uow-executor.ts +41 -8
- package/src/adapters/drizzle/generate.test.ts +102 -269
- package/src/adapters/drizzle/generate.ts +12 -30
- package/src/adapters/drizzle/test-utils.ts +36 -5
- package/src/adapters/kysely/kysely-adapter-pglite.test.ts +66 -22
- package/src/adapters/kysely/kysely-adapter-sqlite.test.ts +156 -0
- package/src/adapters/kysely/kysely-adapter.ts +25 -2
- package/src/adapters/kysely/kysely-query-compiler.ts +3 -8
- package/src/adapters/kysely/kysely-query.ts +57 -37
- package/src/adapters/kysely/kysely-shared.ts +34 -0
- package/src/adapters/kysely/kysely-uow-compiler.test.ts +62 -74
- package/src/adapters/kysely/kysely-uow-compiler.ts +92 -24
- package/src/adapters/kysely/kysely-uow-executor.ts +26 -7
- package/src/adapters/kysely/kysely-uow-joins.test.ts +33 -50
- package/src/adapters/kysely/migration/execute-base.ts +1 -1
- package/src/db-fragment-definition-builder.test.ts +887 -0
- package/src/db-fragment-definition-builder.ts +506 -0
- package/src/db-fragment-instantiator.test.ts +467 -0
- package/src/db-fragment-integration.test.ts +408 -0
- package/src/fragments/internal-fragment.test.ts +160 -0
- package/src/fragments/internal-fragment.ts +85 -0
- package/src/migration-engine/generation-engine.test.ts +58 -15
- package/src/migration-engine/generation-engine.ts +78 -25
- package/src/mod.ts +35 -43
- package/src/query/cursor.test.ts +119 -0
- package/src/query/cursor.ts +17 -4
- package/src/query/execute-unit-of-work.test.ts +1310 -0
- package/src/query/execute-unit-of-work.ts +463 -0
- package/src/query/query.ts +4 -4
- package/src/query/result-transform.test.ts +129 -0
- package/src/query/result-transform.ts +4 -1
- package/src/query/retry-policy.test.ts +217 -0
- package/src/query/retry-policy.ts +141 -0
- package/src/query/unit-of-work-coordinator.test.ts +833 -0
- package/src/query/unit-of-work-types.test.ts +15 -2
- package/src/query/unit-of-work.test.ts +878 -200
- package/src/query/unit-of-work.ts +963 -321
- package/src/schema/serialize.ts +22 -11
- package/src/with-database.ts +140 -0
- package/tsdown.config.ts +1 -0
- package/dist/fragment.d.ts +0 -54
- package/dist/fragment.d.ts.map +0 -1
- package/dist/fragment.js +0 -92
- package/dist/fragment.js.map +0 -1
- package/dist/shared/settings-schema.js +0 -36
- package/dist/shared/settings-schema.js.map +0 -1
- package/src/fragment.test.ts +0 -341
- package/src/fragment.ts +0 -198
- package/src/shared/settings-schema.ts +0 -61
package/dist/query/cursor.d.ts
CHANGED
|
@@ -47,6 +47,10 @@ interface CursorResult<T> {
|
|
|
47
47
|
* Cursor to fetch the next page (undefined if no more results)
|
|
48
48
|
*/
|
|
49
49
|
cursor?: Cursor;
|
|
50
|
+
/**
|
|
51
|
+
* Whether there are more results available after this page
|
|
52
|
+
*/
|
|
53
|
+
hasNextPage: boolean;
|
|
50
54
|
}
|
|
51
55
|
/**
|
|
52
56
|
* Cursor data structure for serialization
|
|
@@ -100,8 +104,12 @@ declare function createCursorFromRecord(record: Record<string, unknown>, indexCo
|
|
|
100
104
|
/**
|
|
101
105
|
* Serialize cursor values for database queries
|
|
102
106
|
*
|
|
103
|
-
* Converts cursor values (which are in
|
|
104
|
-
* using the column serialization rules.
|
|
107
|
+
* Converts cursor values (which are in JSON-compatible format after decode)
|
|
108
|
+
* to database format using the column serialization rules.
|
|
109
|
+
*
|
|
110
|
+
* This function performs a two-step process:
|
|
111
|
+
* 1. Deserialize from JSON format to application format (e.g., ISO string → Date)
|
|
112
|
+
* 2. Serialize from application format to database format (e.g., Date → driver format)
|
|
105
113
|
*
|
|
106
114
|
* @param cursor - The cursor object
|
|
107
115
|
* @param indexColumns - The columns that make up the index
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor.d.ts","names":[],"sources":["../../src/query/cursor.ts"],"sourcesContent":[],"mappings":";;;;;;;AAOA;AAgEiB,cAhEJ,MAAA,CAgEgB;
|
|
1
|
+
{"version":3,"file":"cursor.d.ts","names":[],"sources":["../../src/query/cursor.ts"],"sourcesContent":[],"mappings":";;;;;;;AAOA;AAgEiB,cAhEJ,MAAA,CAgEgB;EAkBZ,CAAA,OAAA;EAgCD,WAAA,CAAA,IAAY,EAAA;IA8DZ,SAAA,EAAA,MAAA;IACN,cAAA,EAAA,KAAA,GAAA,MAAA;IACM,QAAA,EAAA,MAAA;IAMb,WAAA,EA9Kc,MA8Kd,CAAA,MAAA,EAAA,OAAA,CAAA;EAAM,CAAA;EAuCO;;;EAGJ,IAAA,SAAA,CAAA,CAAA,EAAA,MAAA;EACT;;;;;;;;;;;qBAzLkB;;;;;;;;;UAsBJ;;;;SAIR;;;;WAIE;;;;;;;;;UAUM,UAAA;;;;;eAKF;;;;;;;;;;;;;;iBA2BC,YAAA,kBAA8B;;;;;;;;;;;;;;;;;;;;;;iBA8D9B,sBAAA,SACN,uCACM;;;;IAMb;;;;;;;;;;;;;;;;;;;;;;;;;iBAuCa,qBAAA,SACN,sBACM,uBACJ,cACT"}
|
package/dist/query/cursor.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { serialize } from "../schema/serialize.js";
|
|
1
|
+
import { deserialize, serialize } from "../schema/serialize.js";
|
|
2
2
|
|
|
3
3
|
//#region src/query/cursor.ts
|
|
4
4
|
/**
|
|
@@ -125,8 +125,12 @@ function createCursorFromRecord(record, indexColumns, metadata) {
|
|
|
125
125
|
/**
|
|
126
126
|
* Serialize cursor values for database queries
|
|
127
127
|
*
|
|
128
|
-
* Converts cursor values (which are in
|
|
129
|
-
* using the column serialization rules.
|
|
128
|
+
* Converts cursor values (which are in JSON-compatible format after decode)
|
|
129
|
+
* to database format using the column serialization rules.
|
|
130
|
+
*
|
|
131
|
+
* This function performs a two-step process:
|
|
132
|
+
* 1. Deserialize from JSON format to application format (e.g., ISO string → Date)
|
|
133
|
+
* 2. Serialize from application format to database format (e.g., Date → driver format)
|
|
130
134
|
*
|
|
131
135
|
* @param cursor - The cursor object
|
|
132
136
|
* @param indexColumns - The columns that make up the index
|
|
@@ -146,7 +150,10 @@ function serializeCursorValues(cursor, indexColumns, provider) {
|
|
|
146
150
|
const serialized = {};
|
|
147
151
|
for (const col of indexColumns) {
|
|
148
152
|
const value = cursor.indexValues[col.ormName];
|
|
149
|
-
if (value !== void 0)
|
|
153
|
+
if (value !== void 0) {
|
|
154
|
+
const deserialized = deserialize(value, col, provider);
|
|
155
|
+
serialized[col.ormName] = serialize(deserialized, col, provider);
|
|
156
|
+
}
|
|
150
157
|
}
|
|
151
158
|
return serialized;
|
|
152
159
|
}
|
package/dist/query/cursor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor.js","names":["#indexName","#orderDirection","#pageSize","#indexValues","json: string","indexValues: Record<string, unknown>","serialized: Record<string, unknown>"],"sources":["../../src/query/cursor.ts"],"sourcesContent":["import type { AnyColumn } from \"../schema/create\";\nimport { serialize } from \"../schema/serialize\";\nimport type { SQLProvider } from \"../shared/providers\";\n\n/**\n * Cursor object containing all information needed for pagination\n */\nexport class Cursor {\n readonly #indexName: string;\n readonly #orderDirection: \"asc\" | \"desc\";\n readonly #pageSize: number;\n readonly #indexValues: Record<string, unknown>;\n\n constructor(data: {\n indexName: string;\n orderDirection: \"asc\" | \"desc\";\n pageSize: number;\n indexValues: Record<string, unknown>;\n }) {\n this.#indexName = data.indexName;\n this.#orderDirection = data.orderDirection;\n this.#pageSize = data.pageSize;\n this.#indexValues = data.indexValues;\n }\n\n /**\n * Get the index name being used for pagination\n */\n get indexName(): string {\n return this.#indexName;\n }\n\n /**\n * Get the ordering direction\n */\n get orderDirection(): \"asc\" | \"desc\" {\n return this.#orderDirection;\n }\n\n /**\n * Get the page size\n */\n get pageSize(): number {\n return this.#pageSize;\n }\n\n /**\n * Get the cursor position values\n */\n get indexValues(): Record<string, unknown> {\n return this.#indexValues;\n }\n\n /**\n * Encode cursor to an opaque base64 string (safe to send to client)\n */\n encode(): string {\n const data: CursorData = {\n v: 1,\n indexName: this.#indexName,\n orderDirection: this.#orderDirection,\n pageSize: this.#pageSize,\n indexValues: this.#indexValues,\n };\n return encodeCursorData(data);\n }\n}\n\n/**\n * Result of a cursor-based query containing items and pagination cursor\n */\nexport interface CursorResult<T> {\n /**\n * The query results\n */\n items: T[];\n /**\n * Cursor to fetch the next page (undefined if no more results)\n */\n cursor?: Cursor;\n}\n\n/**\n * Cursor data structure for serialization\n */\nexport interface CursorData {\n v: number; // version\n indexName: string;\n orderDirection: \"asc\" | \"desc\";\n pageSize: number;\n indexValues: Record<string, unknown>;\n}\n\n/**\n * Encode cursor data to a base64 string (internal)\n */\nfunction encodeCursorData(data: CursorData): string {\n const json = JSON.stringify(data);\n // Use Buffer in Node.js or btoa in browsers\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(json, \"utf-8\").toString(\"base64\");\n }\n return btoa(json);\n}\n\n/**\n * Decode a base64 cursor string back to a Cursor object\n *\n * @param cursor - The base64-encoded cursor string\n * @returns Decoded Cursor object\n * @throws Error if cursor is invalid or malformed\n *\n * @example\n * ```ts\n * const cursor = decodeCursor(\"eyJpbmRleFZhbHVlcyI6e30sImRpcmVjdGlvbiI6ImZvcndhcmQifQ==\");\n * ```\n */\nexport function decodeCursor(cursor: string): Cursor {\n try {\n let json: string;\n if (typeof Buffer !== \"undefined\") {\n json = Buffer.from(cursor, \"base64\").toString(\"utf-8\");\n } else {\n json = atob(cursor);\n }\n const data = JSON.parse(json);\n\n // Validate structure\n if (\n !data ||\n typeof data !== \"object\" ||\n !data.indexValues ||\n typeof data.indexValues !== \"object\" ||\n typeof data.pageSize !== \"number\" ||\n !data.indexName ||\n !data.orderDirection ||\n (data.orderDirection !== \"asc\" && data.orderDirection !== \"desc\")\n ) {\n throw new Error(\"Invalid cursor structure\");\n }\n\n // Only support v1\n const version = typeof data.v === \"number\" ? data.v : 0;\n if (version !== 1) {\n throw new Error(`Unsupported cursor version: ${version}. Only v1 is supported.`);\n }\n\n return new Cursor({\n indexName: data.indexName,\n orderDirection: data.orderDirection,\n pageSize: data.pageSize,\n indexValues: data.indexValues,\n });\n } catch (error) {\n throw new Error(`Invalid cursor: ${error instanceof Error ? error.message : \"malformed data\"}`);\n }\n}\n\n/**\n * Create a cursor from a record and pagination metadata\n *\n * @param record - The database record\n * @param indexColumns - The columns that make up the index\n * @param metadata - Pagination metadata (index name, order direction, page size)\n * @returns Cursor object\n *\n * @example\n * ```ts\n * const cursor = createCursorFromRecord(\n * { id: \"abc\", name: \"Alice\", createdAt: 123 },\n * [table.columns.createdAt, table.columns.id],\n * {\n * indexName: \"idx_created\",\n * orderDirection: \"asc\",\n * pageSize: 10\n * }\n * );\n * ```\n */\nexport function createCursorFromRecord(\n record: Record<string, unknown>,\n indexColumns: AnyColumn[],\n metadata: {\n indexName: string;\n orderDirection: \"asc\" | \"desc\";\n pageSize: number;\n },\n): Cursor {\n const indexValues: Record<string, unknown> = {};\n\n for (const col of indexColumns) {\n indexValues[col.ormName] = record[col.ormName];\n }\n\n return new Cursor({\n indexName: metadata.indexName,\n orderDirection: metadata.orderDirection,\n pageSize: metadata.pageSize,\n indexValues,\n });\n}\n\n/**\n * Serialize cursor values for database queries\n *\n * Converts cursor values (which are in
|
|
1
|
+
{"version":3,"file":"cursor.js","names":["#indexName","#orderDirection","#pageSize","#indexValues","json: string","indexValues: Record<string, unknown>","serialized: Record<string, unknown>"],"sources":["../../src/query/cursor.ts"],"sourcesContent":["import type { AnyColumn } from \"../schema/create\";\nimport { deserialize, serialize } from \"../schema/serialize\";\nimport type { SQLProvider } from \"../shared/providers\";\n\n/**\n * Cursor object containing all information needed for pagination\n */\nexport class Cursor {\n readonly #indexName: string;\n readonly #orderDirection: \"asc\" | \"desc\";\n readonly #pageSize: number;\n readonly #indexValues: Record<string, unknown>;\n\n constructor(data: {\n indexName: string;\n orderDirection: \"asc\" | \"desc\";\n pageSize: number;\n indexValues: Record<string, unknown>;\n }) {\n this.#indexName = data.indexName;\n this.#orderDirection = data.orderDirection;\n this.#pageSize = data.pageSize;\n this.#indexValues = data.indexValues;\n }\n\n /**\n * Get the index name being used for pagination\n */\n get indexName(): string {\n return this.#indexName;\n }\n\n /**\n * Get the ordering direction\n */\n get orderDirection(): \"asc\" | \"desc\" {\n return this.#orderDirection;\n }\n\n /**\n * Get the page size\n */\n get pageSize(): number {\n return this.#pageSize;\n }\n\n /**\n * Get the cursor position values\n */\n get indexValues(): Record<string, unknown> {\n return this.#indexValues;\n }\n\n /**\n * Encode cursor to an opaque base64 string (safe to send to client)\n */\n encode(): string {\n const data: CursorData = {\n v: 1,\n indexName: this.#indexName,\n orderDirection: this.#orderDirection,\n pageSize: this.#pageSize,\n indexValues: this.#indexValues,\n };\n return encodeCursorData(data);\n }\n}\n\n/**\n * Result of a cursor-based query containing items and pagination cursor\n */\nexport interface CursorResult<T> {\n /**\n * The query results\n */\n items: T[];\n /**\n * Cursor to fetch the next page (undefined if no more results)\n */\n cursor?: Cursor;\n /**\n * Whether there are more results available after this page\n */\n hasNextPage: boolean;\n}\n\n/**\n * Cursor data structure for serialization\n */\nexport interface CursorData {\n v: number; // version\n indexName: string;\n orderDirection: \"asc\" | \"desc\";\n pageSize: number;\n indexValues: Record<string, unknown>;\n}\n\n/**\n * Encode cursor data to a base64 string (internal)\n */\nfunction encodeCursorData(data: CursorData): string {\n const json = JSON.stringify(data);\n // Use Buffer in Node.js or btoa in browsers\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(json, \"utf-8\").toString(\"base64\");\n }\n return btoa(json);\n}\n\n/**\n * Decode a base64 cursor string back to a Cursor object\n *\n * @param cursor - The base64-encoded cursor string\n * @returns Decoded Cursor object\n * @throws Error if cursor is invalid or malformed\n *\n * @example\n * ```ts\n * const cursor = decodeCursor(\"eyJpbmRleFZhbHVlcyI6e30sImRpcmVjdGlvbiI6ImZvcndhcmQifQ==\");\n * ```\n */\nexport function decodeCursor(cursor: string): Cursor {\n try {\n let json: string;\n if (typeof Buffer !== \"undefined\") {\n json = Buffer.from(cursor, \"base64\").toString(\"utf-8\");\n } else {\n json = atob(cursor);\n }\n const data = JSON.parse(json);\n\n // Validate structure\n if (\n !data ||\n typeof data !== \"object\" ||\n !data.indexValues ||\n typeof data.indexValues !== \"object\" ||\n typeof data.pageSize !== \"number\" ||\n !data.indexName ||\n !data.orderDirection ||\n (data.orderDirection !== \"asc\" && data.orderDirection !== \"desc\")\n ) {\n throw new Error(\"Invalid cursor structure\");\n }\n\n // Only support v1\n const version = typeof data.v === \"number\" ? data.v : 0;\n if (version !== 1) {\n throw new Error(`Unsupported cursor version: ${version}. Only v1 is supported.`);\n }\n\n return new Cursor({\n indexName: data.indexName,\n orderDirection: data.orderDirection,\n pageSize: data.pageSize,\n indexValues: data.indexValues,\n });\n } catch (error) {\n throw new Error(`Invalid cursor: ${error instanceof Error ? error.message : \"malformed data\"}`);\n }\n}\n\n/**\n * Create a cursor from a record and pagination metadata\n *\n * @param record - The database record\n * @param indexColumns - The columns that make up the index\n * @param metadata - Pagination metadata (index name, order direction, page size)\n * @returns Cursor object\n *\n * @example\n * ```ts\n * const cursor = createCursorFromRecord(\n * { id: \"abc\", name: \"Alice\", createdAt: 123 },\n * [table.columns.createdAt, table.columns.id],\n * {\n * indexName: \"idx_created\",\n * orderDirection: \"asc\",\n * pageSize: 10\n * }\n * );\n * ```\n */\nexport function createCursorFromRecord(\n record: Record<string, unknown>,\n indexColumns: AnyColumn[],\n metadata: {\n indexName: string;\n orderDirection: \"asc\" | \"desc\";\n pageSize: number;\n },\n): Cursor {\n const indexValues: Record<string, unknown> = {};\n\n for (const col of indexColumns) {\n indexValues[col.ormName] = record[col.ormName];\n }\n\n return new Cursor({\n indexName: metadata.indexName,\n orderDirection: metadata.orderDirection,\n pageSize: metadata.pageSize,\n indexValues,\n });\n}\n\n/**\n * Serialize cursor values for database queries\n *\n * Converts cursor values (which are in JSON-compatible format after decode)\n * to database format using the column serialization rules.\n *\n * This function performs a two-step process:\n * 1. Deserialize from JSON format to application format (e.g., ISO string → Date)\n * 2. Serialize from application format to database format (e.g., Date → driver format)\n *\n * @param cursor - The cursor object\n * @param indexColumns - The columns that make up the index\n * @param provider - The SQL provider\n * @returns Serialized values ready for database queries\n *\n * @example\n * ```ts\n * const serialized = serializeCursorValues(\n * cursor,\n * [table.columns.createdAt],\n * \"postgresql\"\n * );\n * ```\n */\nexport function serializeCursorValues(\n cursor: Cursor,\n indexColumns: AnyColumn[],\n provider: SQLProvider,\n): Record<string, unknown> {\n const serialized: Record<string, unknown> = {};\n\n for (const col of indexColumns) {\n const value = cursor.indexValues[col.ormName];\n if (value !== undefined) {\n // First deserialize from JSON format to application format\n // (e.g., \"2025-11-07T09:36:57.959Z\" string → Date object)\n const deserialized = deserialize(value, col, provider);\n // Then serialize to database format\n // (e.g., Date → database driver format)\n serialized[col.ormName] = serialize(deserialized, col, provider);\n }\n }\n\n return serialized;\n}\n"],"mappings":";;;;;;AAOA,IAAa,SAAb,MAAoB;CAClB,CAASA;CACT,CAASC;CACT,CAASC;CACT,CAASC;CAET,YAAY,MAKT;AACD,QAAKH,YAAa,KAAK;AACvB,QAAKC,iBAAkB,KAAK;AAC5B,QAAKC,WAAY,KAAK;AACtB,QAAKC,cAAe,KAAK;;;;;CAM3B,IAAI,YAAoB;AACtB,SAAO,MAAKH;;;;;CAMd,IAAI,iBAAiC;AACnC,SAAO,MAAKC;;;;;CAMd,IAAI,WAAmB;AACrB,SAAO,MAAKC;;;;;CAMd,IAAI,cAAuC;AACzC,SAAO,MAAKC;;;;;CAMd,SAAiB;AAQf,SAAO,iBAPkB;GACvB,GAAG;GACH,WAAW,MAAKH;GAChB,gBAAgB,MAAKC;GACrB,UAAU,MAAKC;GACf,aAAa,MAAKC;GACnB,CAC4B;;;;;;AAoCjC,SAAS,iBAAiB,MAA0B;CAClD,MAAM,OAAO,KAAK,UAAU,KAAK;AAEjC,KAAI,OAAO,WAAW,YACpB,QAAO,OAAO,KAAK,MAAM,QAAQ,CAAC,SAAS,SAAS;AAEtD,QAAO,KAAK,KAAK;;;;;;;;;;;;;;AAenB,SAAgB,aAAa,QAAwB;AACnD,KAAI;EACF,IAAIC;AACJ,MAAI,OAAO,WAAW,YACpB,QAAO,OAAO,KAAK,QAAQ,SAAS,CAAC,SAAS,QAAQ;MAEtD,QAAO,KAAK,OAAO;EAErB,MAAM,OAAO,KAAK,MAAM,KAAK;AAG7B,MACE,CAAC,QACD,OAAO,SAAS,YAChB,CAAC,KAAK,eACN,OAAO,KAAK,gBAAgB,YAC5B,OAAO,KAAK,aAAa,YACzB,CAAC,KAAK,aACN,CAAC,KAAK,kBACL,KAAK,mBAAmB,SAAS,KAAK,mBAAmB,OAE1D,OAAM,IAAI,MAAM,2BAA2B;EAI7C,MAAM,UAAU,OAAO,KAAK,MAAM,WAAW,KAAK,IAAI;AACtD,MAAI,YAAY,EACd,OAAM,IAAI,MAAM,+BAA+B,QAAQ,yBAAyB;AAGlF,SAAO,IAAI,OAAO;GAChB,WAAW,KAAK;GAChB,gBAAgB,KAAK;GACrB,UAAU,KAAK;GACf,aAAa,KAAK;GACnB,CAAC;UACK,OAAO;AACd,QAAM,IAAI,MAAM,mBAAmB,iBAAiB,QAAQ,MAAM,UAAU,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;AAyBnG,SAAgB,uBACd,QACA,cACA,UAKQ;CACR,MAAMC,cAAuC,EAAE;AAE/C,MAAK,MAAM,OAAO,aAChB,aAAY,IAAI,WAAW,OAAO,IAAI;AAGxC,QAAO,IAAI,OAAO;EAChB,WAAW,SAAS;EACpB,gBAAgB,SAAS;EACzB,UAAU,SAAS;EACnB;EACD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BJ,SAAgB,sBACd,QACA,cACA,UACyB;CACzB,MAAMC,aAAsC,EAAE;AAE9C,MAAK,MAAM,OAAO,cAAc;EAC9B,MAAM,QAAQ,OAAO,YAAY,IAAI;AACrC,MAAI,UAAU,QAAW;GAGvB,MAAM,eAAe,YAAY,OAAO,KAAK,SAAS;AAGtD,cAAW,IAAI,WAAW,UAAU,cAAc,KAAK,SAAS;;;AAIpE,QAAO"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { AnySchema, FragnoId } from "../schema/create.js";
|
|
2
|
+
import { IUnitOfWork, TypedUnitOfWork } from "./unit-of-work.js";
|
|
3
|
+
import { RetryPolicy } from "./retry-policy.js";
|
|
4
|
+
|
|
5
|
+
//#region src/query/execute-unit-of-work.d.ts
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Type utility that unwraps promises 1 level deep in objects, arrays, or direct promises
|
|
9
|
+
* Handles tuples, arrays, objects, and direct promises
|
|
10
|
+
*/
|
|
11
|
+
type AwaitedPromisesInObject<T> = T extends Promise<infer U> ? Awaited<U> : T extends readonly [unknown, ...unknown[]] ? { [K in keyof T]: AwaitedPromisesInObject<T[K]> } : T extends [unknown, ...unknown[]] ? { [K in keyof T]: AwaitedPromisesInObject<T[K]> } : T extends (infer U)[] ? Awaited<U>[] : T extends readonly (infer U)[] ? readonly Awaited<U>[] : T extends Record<string, unknown> ? { [K in keyof T]: T[K] extends Promise<infer U> ? Awaited<U> : T[K] } : T;
|
|
12
|
+
/**
|
|
13
|
+
* Result of executing a Unit of Work with retry support
|
|
14
|
+
* Promises in mutationResult are unwrapped 1 level deep
|
|
15
|
+
*/
|
|
16
|
+
type ExecuteUnitOfWorkResult<TRetrievalResults, TMutationResult> = {
|
|
17
|
+
success: true;
|
|
18
|
+
results: TRetrievalResults;
|
|
19
|
+
mutationResult: AwaitedPromisesInObject<TMutationResult>;
|
|
20
|
+
createdIds: FragnoId[];
|
|
21
|
+
nonce: string;
|
|
22
|
+
} | {
|
|
23
|
+
success: false;
|
|
24
|
+
reason: "conflict";
|
|
25
|
+
} | {
|
|
26
|
+
success: false;
|
|
27
|
+
reason: "aborted";
|
|
28
|
+
} | {
|
|
29
|
+
success: false;
|
|
30
|
+
reason: "error";
|
|
31
|
+
error: unknown;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Callbacks for executing a Unit of Work
|
|
35
|
+
*/
|
|
36
|
+
interface ExecuteUnitOfWorkCallbacks<TSchema extends AnySchema, TRetrievalResults extends unknown[], TMutationResult, TRawInput> {
|
|
37
|
+
/**
|
|
38
|
+
* Retrieval phase callback - adds retrieval operations to the UOW
|
|
39
|
+
*/
|
|
40
|
+
retrieve?: (uow: TypedUnitOfWork<TSchema, [], TRawInput>) => TypedUnitOfWork<TSchema, TRetrievalResults, TRawInput>;
|
|
41
|
+
/**
|
|
42
|
+
* Mutation phase callback - receives UOW and retrieval results, adds mutation operations
|
|
43
|
+
*/
|
|
44
|
+
mutate?: (uow: TypedUnitOfWork<TSchema, TRetrievalResults, TRawInput>, results: TRetrievalResults) => TMutationResult | Promise<TMutationResult>;
|
|
45
|
+
/**
|
|
46
|
+
* Success callback - invoked after successful execution
|
|
47
|
+
* Promises in mutationResult are already unwrapped 1 level deep
|
|
48
|
+
*/
|
|
49
|
+
onSuccess?: (result: {
|
|
50
|
+
results: TRetrievalResults;
|
|
51
|
+
mutationResult: AwaitedPromisesInObject<TMutationResult>;
|
|
52
|
+
createdIds: FragnoId[];
|
|
53
|
+
nonce: string;
|
|
54
|
+
}) => void | Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Options for executing a Unit of Work
|
|
58
|
+
*/
|
|
59
|
+
interface ExecuteUnitOfWorkOptions<TSchema extends AnySchema, TRawInput> {
|
|
60
|
+
/**
|
|
61
|
+
* Factory function that creates or resets a UOW instance for each attempt
|
|
62
|
+
*/
|
|
63
|
+
createUnitOfWork: () => TypedUnitOfWork<TSchema, [], TRawInput>;
|
|
64
|
+
/**
|
|
65
|
+
* Retry policy for handling optimistic concurrency conflicts
|
|
66
|
+
*/
|
|
67
|
+
retryPolicy?: RetryPolicy;
|
|
68
|
+
/**
|
|
69
|
+
* Abort signal to cancel execution
|
|
70
|
+
*/
|
|
71
|
+
signal?: AbortSignal;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Execute a Unit of Work with automatic retry support for optimistic concurrency conflicts.
|
|
75
|
+
*
|
|
76
|
+
* This function orchestrates the two-phase execution (retrieval + mutation) with retry logic.
|
|
77
|
+
* It creates fresh UOW instances for each attempt.
|
|
78
|
+
*
|
|
79
|
+
* @param callbacks - Object containing retrieve, mutate, and onSuccess callbacks
|
|
80
|
+
* @param options - Configuration including UOW factory, retry policy, and abort signal
|
|
81
|
+
* @returns Promise resolving to the execution result
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```ts
|
|
85
|
+
* const result = await executeUnitOfWork(
|
|
86
|
+
* {
|
|
87
|
+
* retrieve: (uow) => uow.find("users", (b) => b.whereIndex("primary")),
|
|
88
|
+
* mutate: (uow, [users]) => {
|
|
89
|
+
* const user = users[0];
|
|
90
|
+
* uow.update("users", user.id, (b) => b.set({ balance: newBalance }));
|
|
91
|
+
* },
|
|
92
|
+
* onSuccess: async ({ results, mutationResult }) => {
|
|
93
|
+
* console.log("Update successful!");
|
|
94
|
+
* }
|
|
95
|
+
* },
|
|
96
|
+
* {
|
|
97
|
+
* createUnitOfWork: () => queryEngine.createUnitOfWork(),
|
|
98
|
+
* retryPolicy: new ExponentialBackoffRetryPolicy({ maxRetries: 3 })
|
|
99
|
+
* }
|
|
100
|
+
* );
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
declare function executeUnitOfWork<TSchema extends AnySchema, TRetrievalResults extends unknown[], TMutationResult = void, TRawInput = unknown>(callbacks: ExecuteUnitOfWorkCallbacks<TSchema, TRetrievalResults, TMutationResult, TRawInput>, options: ExecuteUnitOfWorkOptions<TSchema, TRawInput>): Promise<ExecuteUnitOfWorkResult<TRetrievalResults, TMutationResult>>;
|
|
104
|
+
/**
|
|
105
|
+
* Options for executing a Unit of Work with restricted access
|
|
106
|
+
*/
|
|
107
|
+
interface ExecuteRestrictedUnitOfWorkOptions {
|
|
108
|
+
/**
|
|
109
|
+
* Factory function that creates or resets a UOW instance for each attempt
|
|
110
|
+
*/
|
|
111
|
+
createUnitOfWork: () => IUnitOfWork;
|
|
112
|
+
/**
|
|
113
|
+
* Retry policy for handling optimistic concurrency conflicts
|
|
114
|
+
*/
|
|
115
|
+
retryPolicy?: RetryPolicy;
|
|
116
|
+
/**
|
|
117
|
+
* Abort signal to cancel execution
|
|
118
|
+
*/
|
|
119
|
+
signal?: AbortSignal;
|
|
120
|
+
}
|
|
121
|
+
//#endregion
|
|
122
|
+
export { AwaitedPromisesInObject, ExecuteRestrictedUnitOfWorkOptions, ExecuteUnitOfWorkCallbacks, ExecuteUnitOfWorkOptions, ExecuteUnitOfWorkResult, executeUnitOfWork };
|
|
123
|
+
//# sourceMappingURL=execute-unit-of-work.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute-unit-of-work.d.ts","names":[],"sources":["../../src/query/execute-unit-of-work.ts"],"sourcesContent":[],"mappings":"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"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { ExponentialBackoffRetryPolicy, NoRetryPolicy } from "./retry-policy.js";
|
|
2
|
+
|
|
3
|
+
//#region src/query/execute-unit-of-work.ts
|
|
4
|
+
/**
|
|
5
|
+
* Await promises in an object 1 level deep
|
|
6
|
+
*/
|
|
7
|
+
async function awaitPromisesInObject(obj) {
|
|
8
|
+
if (obj === null || obj === void 0) return obj;
|
|
9
|
+
if (typeof obj !== "object") return obj;
|
|
10
|
+
if (obj instanceof Promise) return await obj;
|
|
11
|
+
if (Array.isArray(obj)) return await Promise.all(obj.map((item) => item instanceof Promise ? item : Promise.resolve(item)));
|
|
12
|
+
const result = {};
|
|
13
|
+
const entries = Object.entries(obj);
|
|
14
|
+
const awaitedEntries = await Promise.all(entries.map(async ([key, value]) => {
|
|
15
|
+
return [key, value instanceof Promise ? await value : value];
|
|
16
|
+
}));
|
|
17
|
+
for (const [key, value] of awaitedEntries) result[key] = value;
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Execute a Unit of Work with automatic retry support for optimistic concurrency conflicts.
|
|
22
|
+
*
|
|
23
|
+
* This function orchestrates the two-phase execution (retrieval + mutation) with retry logic.
|
|
24
|
+
* It creates fresh UOW instances for each attempt.
|
|
25
|
+
*
|
|
26
|
+
* @param callbacks - Object containing retrieve, mutate, and onSuccess callbacks
|
|
27
|
+
* @param options - Configuration including UOW factory, retry policy, and abort signal
|
|
28
|
+
* @returns Promise resolving to the execution result
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* const result = await executeUnitOfWork(
|
|
33
|
+
* {
|
|
34
|
+
* retrieve: (uow) => uow.find("users", (b) => b.whereIndex("primary")),
|
|
35
|
+
* mutate: (uow, [users]) => {
|
|
36
|
+
* const user = users[0];
|
|
37
|
+
* uow.update("users", user.id, (b) => b.set({ balance: newBalance }));
|
|
38
|
+
* },
|
|
39
|
+
* onSuccess: async ({ results, mutationResult }) => {
|
|
40
|
+
* console.log("Update successful!");
|
|
41
|
+
* }
|
|
42
|
+
* },
|
|
43
|
+
* {
|
|
44
|
+
* createUnitOfWork: () => queryEngine.createUnitOfWork(),
|
|
45
|
+
* retryPolicy: new ExponentialBackoffRetryPolicy({ maxRetries: 3 })
|
|
46
|
+
* }
|
|
47
|
+
* );
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
async function executeUnitOfWork(callbacks, options) {
|
|
51
|
+
if (!callbacks.retrieve && !callbacks.mutate) throw new Error("At least one of 'retrieve' or 'mutate' callbacks must be provided");
|
|
52
|
+
const retryPolicy = options.retryPolicy ?? new NoRetryPolicy();
|
|
53
|
+
const signal = options.signal;
|
|
54
|
+
let attempt = 0;
|
|
55
|
+
while (true) {
|
|
56
|
+
if (signal?.aborted) return {
|
|
57
|
+
success: false,
|
|
58
|
+
reason: "aborted"
|
|
59
|
+
};
|
|
60
|
+
try {
|
|
61
|
+
const uow = options.createUnitOfWork();
|
|
62
|
+
let retrievalUow;
|
|
63
|
+
if (callbacks.retrieve) retrievalUow = callbacks.retrieve(uow);
|
|
64
|
+
else retrievalUow = uow;
|
|
65
|
+
const results = await retrievalUow.executeRetrieve();
|
|
66
|
+
let mutationResult;
|
|
67
|
+
if (callbacks.mutate) mutationResult = await callbacks.mutate(retrievalUow, results);
|
|
68
|
+
else mutationResult = void 0;
|
|
69
|
+
const { success } = await retrievalUow.executeMutations();
|
|
70
|
+
if (success) {
|
|
71
|
+
const createdIds = retrievalUow.getCreatedIds();
|
|
72
|
+
const nonce = retrievalUow.nonce;
|
|
73
|
+
const awaitedMutationResult = await awaitPromisesInObject(mutationResult);
|
|
74
|
+
if (callbacks.onSuccess) await callbacks.onSuccess({
|
|
75
|
+
results,
|
|
76
|
+
mutationResult: awaitedMutationResult,
|
|
77
|
+
createdIds,
|
|
78
|
+
nonce
|
|
79
|
+
});
|
|
80
|
+
return {
|
|
81
|
+
success: true,
|
|
82
|
+
results,
|
|
83
|
+
mutationResult: awaitedMutationResult,
|
|
84
|
+
createdIds,
|
|
85
|
+
nonce
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (!retryPolicy.shouldRetry(attempt, void 0, signal)) return {
|
|
89
|
+
success: false,
|
|
90
|
+
reason: "conflict"
|
|
91
|
+
};
|
|
92
|
+
const delayMs = retryPolicy.getDelayMs(attempt);
|
|
93
|
+
if (delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
94
|
+
attempt++;
|
|
95
|
+
} catch (error) {
|
|
96
|
+
return {
|
|
97
|
+
success: false,
|
|
98
|
+
reason: "error",
|
|
99
|
+
error
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Execute a Unit of Work with explicit phase control and automatic retry support.
|
|
106
|
+
*
|
|
107
|
+
* This function provides an alternative API where users write a single callback that receives
|
|
108
|
+
* a context object with forSchema, executeRetrieve, and executeMutate methods. The user can
|
|
109
|
+
* create schema-specific UOWs via forSchema, then call executeRetrieve() and executeMutate()
|
|
110
|
+
* to execute the retrieval and mutation phases. The entire callback is re-executed on optimistic
|
|
111
|
+
* concurrency conflicts, ensuring retries work properly.
|
|
112
|
+
*
|
|
113
|
+
* @param callback - Async function that receives a context with forSchema, executeRetrieve, executeMutate, nonce, and currentAttempt
|
|
114
|
+
* @param options - Configuration including UOW factory, retry policy, and abort signal
|
|
115
|
+
* @returns Promise resolving to the callback's return value
|
|
116
|
+
* @throws Error if retries are exhausted or callback throws an error
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```ts
|
|
120
|
+
* const { userId, profileId } = await executeRestrictedUnitOfWork(
|
|
121
|
+
* async ({ forSchema, executeRetrieve, executeMutate, nonce, currentAttempt }) => {
|
|
122
|
+
* const uow = forSchema(schema);
|
|
123
|
+
* const userId = uow.create("users", { name: "John" });
|
|
124
|
+
*
|
|
125
|
+
* // Execute retrieval phase
|
|
126
|
+
* await executeRetrieve();
|
|
127
|
+
*
|
|
128
|
+
* const profileId = uow.create("profiles", { userId });
|
|
129
|
+
*
|
|
130
|
+
* // Execute mutation phase
|
|
131
|
+
* await executeMutate();
|
|
132
|
+
*
|
|
133
|
+
* return { userId, profileId };
|
|
134
|
+
* },
|
|
135
|
+
* {
|
|
136
|
+
* createUnitOfWork: () => db.createUnitOfWork(),
|
|
137
|
+
* retryPolicy: new ExponentialBackoffRetryPolicy({ maxRetries: 5 })
|
|
138
|
+
* }
|
|
139
|
+
* );
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
async function executeRestrictedUnitOfWork(callback, options) {
|
|
143
|
+
const retryPolicy = options.retryPolicy ?? new ExponentialBackoffRetryPolicy({
|
|
144
|
+
maxRetries: 5,
|
|
145
|
+
initialDelayMs: 10,
|
|
146
|
+
maxDelayMs: 100
|
|
147
|
+
});
|
|
148
|
+
const signal = options.signal;
|
|
149
|
+
let attempt = 0;
|
|
150
|
+
while (true) {
|
|
151
|
+
if (signal?.aborted) throw new Error("Unit of Work execution aborted");
|
|
152
|
+
try {
|
|
153
|
+
const baseUow = options.createUnitOfWork();
|
|
154
|
+
return await awaitPromisesInObject(await callback({
|
|
155
|
+
forSchema: (schema) => {
|
|
156
|
+
return baseUow.forSchema(schema);
|
|
157
|
+
},
|
|
158
|
+
executeRetrieve: async () => {
|
|
159
|
+
await baseUow.executeRetrieve();
|
|
160
|
+
},
|
|
161
|
+
executeMutate: async () => {
|
|
162
|
+
if (baseUow.state === "executed") return;
|
|
163
|
+
if (baseUow.state === "building-retrieval") await baseUow.executeRetrieve();
|
|
164
|
+
if (!(await baseUow.executeMutations()).success) throw new Error("Mutations failed due to conflict");
|
|
165
|
+
},
|
|
166
|
+
nonce: baseUow.nonce,
|
|
167
|
+
currentAttempt: attempt
|
|
168
|
+
}));
|
|
169
|
+
} catch (error) {
|
|
170
|
+
if (signal?.aborted) throw new Error("Unit of Work execution aborted");
|
|
171
|
+
if (!retryPolicy.shouldRetry(attempt, error, signal)) {
|
|
172
|
+
if (signal?.aborted) throw new Error("Unit of Work execution aborted");
|
|
173
|
+
throw new Error("Unit of Work execution failed: optimistic concurrency conflict", { cause: error });
|
|
174
|
+
}
|
|
175
|
+
const delayMs = retryPolicy.getDelayMs(attempt);
|
|
176
|
+
if (delayMs > 0) await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
177
|
+
attempt++;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
//#endregion
|
|
183
|
+
export { executeRestrictedUnitOfWork, executeUnitOfWork };
|
|
184
|
+
//# sourceMappingURL=execute-unit-of-work.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute-unit-of-work.js","names":["retrievalUow: TypedUnitOfWork<TSchema, TRetrievalResults, TRawInput>","mutationResult: TMutationResult"],"sources":["../../src/query/execute-unit-of-work.ts"],"sourcesContent":["import type { AnySchema } from \"../schema/create\";\nimport type { TypedUnitOfWork, IUnitOfWork } from \"./unit-of-work\";\nimport { NoRetryPolicy, ExponentialBackoffRetryPolicy, type RetryPolicy } from \"./retry-policy\";\nimport type { FragnoId } from \"../schema/create\";\n\n/**\n * Type utility that unwraps promises 1 level deep in objects, arrays, or direct promises\n * Handles tuples, arrays, objects, and direct promises\n */\nexport type AwaitedPromisesInObject<T> =\n // First check if it's a Promise\n T extends Promise<infer U>\n ? Awaited<U>\n : // Check for arrays with known length (tuples) - preserves tuple structure\n T extends readonly [unknown, ...unknown[]]\n ? { [K in keyof T]: AwaitedPromisesInObject<T[K]> }\n : T extends [unknown, ...unknown[]]\n ? { [K in keyof T]: AwaitedPromisesInObject<T[K]> }\n : // Check for regular arrays (unknown length)\n T extends (infer U)[]\n ? Awaited<U>[]\n : T extends readonly (infer U)[]\n ? readonly Awaited<U>[]\n : // Check for objects\n T extends Record<string, unknown>\n ? {\n [K in keyof T]: T[K] extends Promise<infer U> ? Awaited<U> : T[K];\n }\n : // Otherwise return as-is\n T;\n\n/**\n * Await promises in an object 1 level deep\n */\nasync function awaitPromisesInObject<T>(obj: T): Promise<AwaitedPromisesInObject<T>> {\n if (obj === null || obj === undefined) {\n return obj as AwaitedPromisesInObject<T>;\n }\n\n if (typeof obj !== \"object\") {\n return obj as AwaitedPromisesInObject<T>;\n }\n\n // Check if it's a Promise\n if (obj instanceof Promise) {\n return (await obj) as AwaitedPromisesInObject<T>;\n }\n\n // Check if it's an array\n if (Array.isArray(obj)) {\n const awaited = await Promise.all(\n obj.map((item) => (item instanceof Promise ? item : Promise.resolve(item))),\n );\n return awaited as AwaitedPromisesInObject<T>;\n }\n\n // It's a plain object - await promises in each property\n const result = {} as T;\n const entries = Object.entries(obj as Record<string, unknown>);\n const awaitedEntries = await Promise.all(\n entries.map(async ([key, value]) => {\n const awaitedValue = value instanceof Promise ? await value : value;\n return [key, awaitedValue] as const;\n }),\n );\n\n for (const [key, value] of awaitedEntries) {\n (result as Record<string, unknown>)[key] = value;\n }\n\n return result as AwaitedPromisesInObject<T>;\n}\n\n/**\n * Result of executing a Unit of Work with retry support\n * Promises in mutationResult are unwrapped 1 level deep\n */\nexport type ExecuteUnitOfWorkResult<TRetrievalResults, TMutationResult> =\n | {\n success: true;\n results: TRetrievalResults;\n mutationResult: AwaitedPromisesInObject<TMutationResult>;\n createdIds: FragnoId[];\n nonce: string;\n }\n | {\n success: false;\n reason: \"conflict\";\n }\n | {\n success: false;\n reason: \"aborted\";\n }\n | {\n success: false;\n reason: \"error\";\n error: unknown;\n };\n\n/**\n * Callbacks for executing a Unit of Work\n */\nexport interface ExecuteUnitOfWorkCallbacks<\n TSchema extends AnySchema,\n TRetrievalResults extends unknown[],\n TMutationResult,\n TRawInput,\n> {\n /**\n * Retrieval phase callback - adds retrieval operations to the UOW\n */\n retrieve?: (\n uow: TypedUnitOfWork<TSchema, [], TRawInput>,\n ) => TypedUnitOfWork<TSchema, TRetrievalResults, TRawInput>;\n\n /**\n * Mutation phase callback - receives UOW and retrieval results, adds mutation operations\n */\n mutate?: (\n uow: TypedUnitOfWork<TSchema, TRetrievalResults, TRawInput>,\n results: TRetrievalResults,\n ) => TMutationResult | Promise<TMutationResult>;\n\n /**\n * Success callback - invoked after successful execution\n * Promises in mutationResult are already unwrapped 1 level deep\n */\n onSuccess?: (result: {\n results: TRetrievalResults;\n mutationResult: AwaitedPromisesInObject<TMutationResult>;\n createdIds: FragnoId[];\n nonce: string;\n }) => void | Promise<void>;\n}\n\n/**\n * Options for executing a Unit of Work\n */\nexport interface ExecuteUnitOfWorkOptions<TSchema extends AnySchema, TRawInput> {\n /**\n * Factory function that creates or resets a UOW instance for each attempt\n */\n createUnitOfWork: () => TypedUnitOfWork<TSchema, [], TRawInput>;\n\n /**\n * Retry policy for handling optimistic concurrency conflicts\n */\n retryPolicy?: RetryPolicy;\n\n /**\n * Abort signal to cancel execution\n */\n signal?: AbortSignal;\n}\n\n/**\n * Create a bound version of executeUnitOfWork with a pre-configured UOW factory.\n * This is useful for handler contexts where the factory is already known.\n *\n * @param createUnitOfWork - Factory function that creates a fresh UOW instance\n * @returns A bound executeUnitOfWork function that doesn't require the factory parameter\n *\n * @example\n * ```ts\n * const boundExecute = createExecuteUnitOfWork(() => db.createUnitOfWork());\n * const result = await boundExecute({\n * retrieve: (uow) => uow.find(\"users\", (b) => b.whereIndex(\"primary\")),\n * mutate: (uow, [users]) => {\n * uow.update(\"users\", users[0].id, (b) => b.set({ balance: newBalance }));\n * }\n * });\n * ```\n */\nexport function createExecuteUnitOfWork<TSchema extends AnySchema, TRawInput>(\n createUnitOfWork: () => TypedUnitOfWork<TSchema, [], TRawInput>,\n) {\n return async function <TRetrievalResults extends unknown[], TMutationResult = void>(\n callbacks: ExecuteUnitOfWorkCallbacks<TSchema, TRetrievalResults, TMutationResult, TRawInput>,\n options?: Omit<ExecuteUnitOfWorkOptions<TSchema, TRawInput>, \"createUnitOfWork\">,\n ): Promise<ExecuteUnitOfWorkResult<TRetrievalResults, TMutationResult>> {\n return executeUnitOfWork(callbacks, { ...options, createUnitOfWork });\n };\n}\n\n/**\n * Execute a Unit of Work with automatic retry support for optimistic concurrency conflicts.\n *\n * This function orchestrates the two-phase execution (retrieval + mutation) with retry logic.\n * It creates fresh UOW instances for each attempt.\n *\n * @param callbacks - Object containing retrieve, mutate, and onSuccess callbacks\n * @param options - Configuration including UOW factory, retry policy, and abort signal\n * @returns Promise resolving to the execution result\n *\n * @example\n * ```ts\n * const result = await executeUnitOfWork(\n * {\n * retrieve: (uow) => uow.find(\"users\", (b) => b.whereIndex(\"primary\")),\n * mutate: (uow, [users]) => {\n * const user = users[0];\n * uow.update(\"users\", user.id, (b) => b.set({ balance: newBalance }));\n * },\n * onSuccess: async ({ results, mutationResult }) => {\n * console.log(\"Update successful!\");\n * }\n * },\n * {\n * createUnitOfWork: () => queryEngine.createUnitOfWork(),\n * retryPolicy: new ExponentialBackoffRetryPolicy({ maxRetries: 3 })\n * }\n * );\n * ```\n */\nexport async function executeUnitOfWork<\n TSchema extends AnySchema,\n TRetrievalResults extends unknown[],\n TMutationResult = void,\n TRawInput = unknown,\n>(\n callbacks: ExecuteUnitOfWorkCallbacks<TSchema, TRetrievalResults, TMutationResult, TRawInput>,\n options: ExecuteUnitOfWorkOptions<TSchema, TRawInput>,\n): Promise<ExecuteUnitOfWorkResult<TRetrievalResults, TMutationResult>> {\n // Validate that at least one of retrieve or mutate is provided\n if (!callbacks.retrieve && !callbacks.mutate) {\n throw new Error(\"At least one of 'retrieve' or 'mutate' callbacks must be provided\");\n }\n\n const retryPolicy = options.retryPolicy ?? new NoRetryPolicy();\n const signal = options.signal;\n let attempt = 0;\n\n while (true) {\n // Check if aborted before starting attempt\n if (signal?.aborted) {\n return { success: false, reason: \"aborted\" };\n }\n\n try {\n // Create a fresh UOW for this attempt\n const uow = options.createUnitOfWork();\n\n // Apply retrieval phase if provided\n let retrievalUow: TypedUnitOfWork<TSchema, TRetrievalResults, TRawInput>;\n if (callbacks.retrieve) {\n retrievalUow = callbacks.retrieve(uow);\n } else {\n // No retrieval phase, use empty UOW with type cast\n // This is safe because when there's no retrieve, TRetrievalResults should be []\n retrievalUow = uow as unknown as TypedUnitOfWork<TSchema, TRetrievalResults, TRawInput>;\n }\n\n // Execute retrieval phase\n const results = (await retrievalUow.executeRetrieve()) as TRetrievalResults;\n\n // Invoke mutation phase callback if provided\n let mutationResult: TMutationResult;\n if (callbacks.mutate) {\n mutationResult = await callbacks.mutate(retrievalUow, results);\n } else {\n mutationResult = undefined as TMutationResult;\n }\n\n // Execute mutation phase\n const { success } = await retrievalUow.executeMutations();\n\n if (success) {\n // Success! Get created IDs and nonce, then invoke onSuccess if provided\n const createdIds = retrievalUow.getCreatedIds();\n const nonce = retrievalUow.nonce;\n\n // Await promises in mutationResult (1 level deep)\n const awaitedMutationResult = await awaitPromisesInObject(mutationResult);\n\n if (callbacks.onSuccess) {\n await callbacks.onSuccess({\n results,\n mutationResult: awaitedMutationResult,\n createdIds,\n nonce,\n });\n }\n\n return {\n success: true,\n results,\n mutationResult: awaitedMutationResult,\n createdIds,\n nonce,\n };\n }\n\n // Failed - check if we should retry\n // attempt represents the number of attempts completed so far\n if (!retryPolicy.shouldRetry(attempt, undefined, signal)) {\n // No more retries\n return { success: false, reason: \"conflict\" };\n }\n\n // Wait before retrying\n const delayMs = retryPolicy.getDelayMs(attempt);\n if (delayMs > 0) {\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n }\n\n // Increment attempt counter for next iteration\n attempt++;\n } catch (error) {\n // An error was thrown during execution\n return { success: false, reason: \"error\", error };\n }\n }\n}\n\n/**\n * Options for executing a Unit of Work with restricted access\n */\nexport interface ExecuteRestrictedUnitOfWorkOptions {\n /**\n * Factory function that creates or resets a UOW instance for each attempt\n */\n createUnitOfWork: () => IUnitOfWork;\n\n /**\n * Retry policy for handling optimistic concurrency conflicts\n */\n retryPolicy?: RetryPolicy;\n\n /**\n * Abort signal to cancel execution\n */\n signal?: AbortSignal;\n}\n\n/**\n * Execute a Unit of Work with explicit phase control and automatic retry support.\n *\n * This function provides an alternative API where users write a single callback that receives\n * a context object with forSchema, executeRetrieve, and executeMutate methods. The user can\n * create schema-specific UOWs via forSchema, then call executeRetrieve() and executeMutate()\n * to execute the retrieval and mutation phases. The entire callback is re-executed on optimistic\n * concurrency conflicts, ensuring retries work properly.\n *\n * @param callback - Async function that receives a context with forSchema, executeRetrieve, executeMutate, nonce, and currentAttempt\n * @param options - Configuration including UOW factory, retry policy, and abort signal\n * @returns Promise resolving to the callback's return value\n * @throws Error if retries are exhausted or callback throws an error\n *\n * @example\n * ```ts\n * const { userId, profileId } = await executeRestrictedUnitOfWork(\n * async ({ forSchema, executeRetrieve, executeMutate, nonce, currentAttempt }) => {\n * const uow = forSchema(schema);\n * const userId = uow.create(\"users\", { name: \"John\" });\n *\n * // Execute retrieval phase\n * await executeRetrieve();\n *\n * const profileId = uow.create(\"profiles\", { userId });\n *\n * // Execute mutation phase\n * await executeMutate();\n *\n * return { userId, profileId };\n * },\n * {\n * createUnitOfWork: () => db.createUnitOfWork(),\n * retryPolicy: new ExponentialBackoffRetryPolicy({ maxRetries: 5 })\n * }\n * );\n * ```\n */\nexport async function executeRestrictedUnitOfWork<TResult>(\n callback: (context: {\n forSchema: <S extends AnySchema>(schema: S) => TypedUnitOfWork<S, [], unknown>;\n executeRetrieve: () => Promise<void>;\n executeMutate: () => Promise<void>;\n nonce: string;\n currentAttempt: number;\n }) => Promise<TResult>,\n options: ExecuteRestrictedUnitOfWorkOptions,\n): Promise<AwaitedPromisesInObject<TResult>> {\n // Default retry policy with small, fast retries for optimistic concurrency\n const retryPolicy =\n options.retryPolicy ??\n new ExponentialBackoffRetryPolicy({\n maxRetries: 5,\n initialDelayMs: 10,\n maxDelayMs: 100,\n });\n const signal = options.signal;\n let attempt = 0;\n\n while (true) {\n // Check if aborted before starting attempt\n if (signal?.aborted) {\n throw new Error(\"Unit of Work execution aborted\");\n }\n\n try {\n // Create a fresh UOW for this attempt\n const baseUow = options.createUnitOfWork();\n\n // Create context object with forSchema, executeRetrieve, executeMutate, nonce, and currentAttempt\n const context = {\n forSchema: <S extends AnySchema>(schema: S) => {\n return baseUow.forSchema(schema);\n },\n executeRetrieve: async () => {\n await baseUow.executeRetrieve();\n },\n executeMutate: async () => {\n if (baseUow.state === \"executed\") {\n return;\n }\n\n if (baseUow.state === \"building-retrieval\") {\n await baseUow.executeRetrieve();\n }\n\n const result = await baseUow.executeMutations();\n if (!result.success) {\n throw new Error(\"Mutations failed due to conflict\");\n }\n },\n nonce: baseUow.nonce,\n currentAttempt: attempt,\n };\n\n // Execute the callback which will call executeRetrieve and executeMutate\n const result = await callback(context);\n\n // Await promises in the result object (1 level deep)\n const awaitedResult = await awaitPromisesInObject(result);\n\n // Return the awaited result\n return awaitedResult;\n } catch (error) {\n if (signal?.aborted) {\n throw new Error(\"Unit of Work execution aborted\");\n }\n\n if (!retryPolicy.shouldRetry(attempt, error, signal)) {\n // No more retries - check again if aborted or throw conflict error\n if (signal?.aborted) {\n throw new Error(\"Unit of Work execution aborted\");\n }\n throw new Error(\"Unit of Work execution failed: optimistic concurrency conflict\", {\n cause: error,\n });\n }\n\n // Wait before retrying\n const delayMs = retryPolicy.getDelayMs(attempt);\n if (delayMs > 0) {\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n }\n\n // Increment attempt counter for next iteration\n attempt++;\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/query/query.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AnySchema, AnyTable, FragnoId, IdColumn, Relation } from "../schema/create.js";
|
|
2
2
|
import { Condition, ConditionBuilder } from "./condition-builder.js";
|
|
3
3
|
import { CursorResult } from "./cursor.js";
|
|
4
|
-
import { DeleteBuilder, FindBuilder, UnitOfWork, UpdateBuilder, UpdateManyBuilder } from "./unit-of-work.js";
|
|
5
4
|
import { Prettify } from "../util/types.js";
|
|
5
|
+
import { DeleteBuilder, FindBuilder, TypedUnitOfWork, UpdateBuilder, UpdateManyBuilder } from "./unit-of-work.js";
|
|
6
6
|
|
|
7
7
|
//#region src/query/query.d.ts
|
|
8
8
|
type AnySelectClause = SelectClause<AnyTable>;
|
|
@@ -93,8 +93,8 @@ interface AbstractQuery<TSchema extends AnySchema, TUOWConfig = void> {
|
|
|
93
93
|
/**
|
|
94
94
|
* Create a Unit of Work bound to this query engine
|
|
95
95
|
*/
|
|
96
|
-
createUnitOfWork: (name?: string, config?: TUOWConfig) =>
|
|
96
|
+
createUnitOfWork: (name?: string, config?: TUOWConfig) => TypedUnitOfWork<TSchema, [], unknown>;
|
|
97
97
|
}
|
|
98
98
|
//#endregion
|
|
99
|
-
export { AbstractQuery, AnySelectClause, FindFirstOptions, FindManyOptions, JoinBuilder, OrderBy, RawColumnValues, SelectClause, SelectResult, TableToColumnValues, TableToInsertValues, TableToUpdateValues };
|
|
99
|
+
export { AbstractQuery, AnySelectClause, ExtractJoinOut, ExtractSelect, FindFirstOptions, FindManyOptions, JoinBuilder, OrderBy, RawColumnValues, SelectClause, SelectResult, TableToColumnValues, TableToInsertValues, TableToUpdateValues };
|
|
100
100
|
//# sourceMappingURL=query.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","names":[],"sources":["../../src/query/query.ts"],"sourcesContent":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"query.d.ts","names":[],"sources":["../../src/query/query.ts"],"sourcesContent":[],"mappings":"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"}
|
|
@@ -54,6 +54,8 @@ function generateRuntimeDefault(column) {
|
|
|
54
54
|
* @param table - The table schema definition containing column information
|
|
55
55
|
* @param generateDefault - Whether to generate default values for undefined columns
|
|
56
56
|
* @param provider - The SQL provider (sqlite, postgresql, mysql, etc.)
|
|
57
|
+
* @param skipDriverConversions - Skip driver-level type conversions (Date->number, boolean->0/1, bigint->Buffer).
|
|
58
|
+
* Set to true when using ORMs like Drizzle that handle these conversions internally.
|
|
57
59
|
* @returns A record with database-compatible column names and serialized values
|
|
58
60
|
*
|
|
59
61
|
* @example
|
|
@@ -67,7 +69,7 @@ function generateRuntimeDefault(column) {
|
|
|
67
69
|
* // Returns: { user_id: 123, created_at: 1234567890 }
|
|
68
70
|
* ```
|
|
69
71
|
*/
|
|
70
|
-
function encodeValues(values, table, generateDefault, provider) {
|
|
72
|
+
function encodeValues(values, table, generateDefault, provider, skipDriverConversions = false) {
|
|
71
73
|
const result = {};
|
|
72
74
|
for (const k in table.columns) {
|
|
73
75
|
const col = table.columns[k];
|
|
@@ -95,7 +97,7 @@ function encodeValues(values, table, generateDefault, provider) {
|
|
|
95
97
|
throw new Error(`Reference column ${k} not found in table ${table.name}`);
|
|
96
98
|
}
|
|
97
99
|
}
|
|
98
|
-
result[col.name] = serialize(value, col, provider);
|
|
100
|
+
result[col.name] = serialize(value, col, provider, skipDriverConversions);
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
return result;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"result-transform.js","names":["#referencedTable","#externalIdValue","result: Record<string, unknown>","output: Record<string, unknown>","columnValues: Record<string, unknown>","relationData: Record<string, Record<string, unknown>>"],"sources":["../../src/query/result-transform.ts"],"sourcesContent":["import type { AnyColumn, AnyTable } from \"../schema/create\";\nimport type { SQLProvider } from \"../shared/providers\";\nimport { deserialize, serialize } from \"../schema/serialize\";\nimport { FragnoId, FragnoReference } from \"../schema/create\";\nimport { createId } from \"../id\";\n\n/**\n * Marker class for reference column values that need subquery resolution.\n * When a reference column receives a string (external ID), this marker tells\n * the query builder to generate a subquery to look up the internal ID.\n * @internal\n */\nexport class ReferenceSubquery {\n #referencedTable: AnyTable;\n #externalIdValue: string;\n\n constructor(referencedTable: AnyTable, externalIdValue: string) {\n this.#referencedTable = referencedTable;\n this.#externalIdValue = externalIdValue;\n }\n\n get referencedTable() {\n return this.#referencedTable;\n }\n\n get externalIdValue() {\n return this.#externalIdValue;\n }\n}\n\n/**\n * Generate a runtime default value for a column that has defaultTo$()\n *\n * Only generates values for runtime defaults (defaultTo$), NOT static defaults (defaultTo).\n * Static defaults should be handled by the database via DEFAULT constraints.\n *\n * @param column - The column with a default value configuration\n * @returns The generated default value, or undefined if the column has no runtime default\n *\n * @internal\n */\nexport function generateRuntimeDefault(column: AnyColumn): unknown {\n // Check if column has a default value configuration\n if (!column.default) {\n return undefined;\n }\n\n // If it's a static default value (defaultTo), return undefined\n // as the database should handle this via DEFAULT constraint\n if (\"value\" in column.default) {\n return undefined;\n }\n\n // If it's a database-level special function (defaultTo(b => b.now())), return undefined\n // as the database should handle this via DEFAULT NOW() or equivalent\n if (\"dbSpecial\" in column.default) {\n return undefined;\n }\n\n // Handle runtime defaults (defaultTo$)\n const runtime = column.default.runtime;\n\n if (runtime === \"cuid\") {\n return createId();\n }\n\n if (runtime === \"now\") {\n return new Date();\n }\n\n if (typeof runtime === \"function\") {\n return runtime();\n }\n\n return undefined;\n}\n\n/**\n * Encodes a record of values from the application format to database format.\n *\n * This function transforms object keys to match SQL column names and serializes\n * values according to the database provider's requirements (e.g., converting\n * JavaScript Date objects to numbers for SQLite).\n *\n * @param values - The record of values to encode in application format\n * @param table - The table schema definition containing column information\n * @param generateDefault - Whether to generate default values for undefined columns\n * @param provider - The SQL provider (sqlite, postgresql, mysql, etc.)\n * @returns A record with database-compatible column names and serialized values\n *\n * @example\n * ```ts\n * const encoded = encodeValues(\n * { userId: 123, createdAt: new Date() },\n * userTable,\n * true,\n * 'sqlite'\n * );\n * // Returns: { user_id: 123, created_at: 1234567890 }\n * ```\n */\nexport function encodeValues(\n values: Record<string, unknown>,\n table: AnyTable,\n generateDefault: boolean,\n provider: SQLProvider,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const k in table.columns) {\n const col = table.columns[k];\n\n // Skip internal ID - never provided by user, auto-generated by database\n if (col.role === \"internal-id\") {\n continue;\n }\n let value = values[k];\n\n if (generateDefault && value === undefined) {\n // Only generate runtime defaults (defaultTo$), not static defaults (defaultTo).\n // Static defaults should be handled by the database via DEFAULT constraints.\n value = generateRuntimeDefault(col);\n }\n\n if (value !== undefined) {\n // Handle string references and FragnoId objects\n if (col.role === \"reference\") {\n if (typeof value === \"string\") {\n // String external ID - generate subquery\n const relation = Object.values(table.relations).find((rel) =>\n rel.on.some(([localCol]) => localCol === k),\n );\n if (relation) {\n result[col.name] = new ReferenceSubquery(relation.table, value);\n continue;\n }\n throw new Error(`Reference column ${k} not found in table ${table.name}`);\n } else if (value instanceof FragnoId) {\n // FragnoId object\n if (value.internalId !== undefined) {\n // If internal ID is populated, use it directly (no subquery needed)\n result[col.name] = value.internalId;\n continue;\n } else {\n // If internal ID is not populated, use external ID via subquery\n const relation = Object.values(table.relations).find((rel) =>\n rel.on.some(([localCol]) => localCol === k),\n );\n if (relation) {\n result[col.name] = new ReferenceSubquery(relation.table, value.externalId);\n continue;\n }\n throw new Error(`Reference column ${k} not found in table ${table.name}`);\n }\n }\n }\n\n result[col.name] = serialize(value, col, provider);\n }\n }\n\n return result;\n}\n\n/**\n * Decodes a database result record to application format.\n *\n * This function transforms database column names back to application property names\n * and deserializes values according to the database provider's format (e.g., converting\n * SQLite integers back to JavaScript Date objects).\n *\n * Supports relation data encoded with the pattern `relationName:columnName`.\n *\n * @param result - The raw database result record\n * @param table - The table schema definition containing column and relation information\n * @param provider - The SQL provider (sqlite, postgresql, mysql, etc.)\n * @returns A record in application format with deserialized values\n *\n * @example\n * ```ts\n * const decoded = decodeResult(\n * { user_id: 123, created_at: 1234567890, 'posts:title': 'Hello' },\n * userTable,\n * 'sqlite'\n * );\n * // Returns: { userId: 123, createdAt: Date, posts: { title: 'Hello' } }\n * ```\n */\nexport function decodeResult(\n result: Record<string, unknown>,\n table: AnyTable,\n provider: SQLProvider,\n): Record<string, unknown> {\n const output: Record<string, unknown> = {};\n // First pass: collect all column values\n const columnValues: Record<string, unknown> = {};\n\n // Collect all relation data (including nested) keyed by relation name\n const relationData: Record<string, Record<string, unknown>> = {};\n\n for (const k in result) {\n const colonIndex = k.indexOf(\":\");\n const value = result[k];\n\n // Direct column (no colon)\n if (colonIndex === -1) {\n const col = table.columns[k];\n if (!col) {\n continue;\n }\n\n // Store all column values (including hidden ones for FragnoId creation)\n columnValues[k] = deserialize(value, col, provider);\n continue;\n }\n\n // Relation column (has colon)\n const relationName = k.slice(0, colonIndex);\n const remainder = k.slice(colonIndex + 1);\n\n const relation = table.relations[relationName];\n if (relation === undefined) {\n continue;\n }\n\n // Collect relation data with the remaining key path\n relationData[relationName] ??= {};\n relationData[relationName][remainder] = value;\n }\n\n // Process each relation's data recursively\n for (const relationName in relationData) {\n const relation = table.relations[relationName];\n if (!relation) {\n continue;\n }\n\n // Recursively decode the relation data\n output[relationName] = decodeResult(relationData[relationName], relation.table, provider);\n }\n\n // Second pass: create output with FragnoId objects where appropriate\n for (const k in columnValues) {\n const col = table.columns[k];\n if (!col) {\n continue;\n }\n\n // Filter out hidden columns (like _internalId, _version) from results\n if (col.isHidden) {\n continue;\n }\n\n // For external ID columns, create FragnoId if we have both external and internal IDs\n if (col.role === \"external-id\" && columnValues[\"_internalId\"] !== undefined) {\n output[k] = new FragnoId({\n externalId: columnValues[k] as string,\n internalId: columnValues[\"_internalId\"] as bigint,\n // _version is always selected as a hidden column, so it should always be present\n version: columnValues[\"_version\"] as number,\n });\n } else if (col.role === \"reference\") {\n // For reference columns, create FragnoReference with internal ID\n output[k] = FragnoReference.fromInternal(columnValues[k] as bigint);\n } else {\n output[k] = columnValues[k];\n }\n }\n\n return output;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"result-transform.js","names":["#referencedTable","#externalIdValue","result: Record<string, unknown>","output: Record<string, unknown>","columnValues: Record<string, unknown>","relationData: Record<string, Record<string, unknown>>"],"sources":["../../src/query/result-transform.ts"],"sourcesContent":["import type { AnyColumn, AnyTable } from \"../schema/create\";\nimport type { SQLProvider } from \"../shared/providers\";\nimport { deserialize, serialize } from \"../schema/serialize\";\nimport { FragnoId, FragnoReference } from \"../schema/create\";\nimport { createId } from \"../id\";\n\n/**\n * Marker class for reference column values that need subquery resolution.\n * When a reference column receives a string (external ID), this marker tells\n * the query builder to generate a subquery to look up the internal ID.\n * @internal\n */\nexport class ReferenceSubquery {\n #referencedTable: AnyTable;\n #externalIdValue: string;\n\n constructor(referencedTable: AnyTable, externalIdValue: string) {\n this.#referencedTable = referencedTable;\n this.#externalIdValue = externalIdValue;\n }\n\n get referencedTable() {\n return this.#referencedTable;\n }\n\n get externalIdValue() {\n return this.#externalIdValue;\n }\n}\n\n/**\n * Generate a runtime default value for a column that has defaultTo$()\n *\n * Only generates values for runtime defaults (defaultTo$), NOT static defaults (defaultTo).\n * Static defaults should be handled by the database via DEFAULT constraints.\n *\n * @param column - The column with a default value configuration\n * @returns The generated default value, or undefined if the column has no runtime default\n *\n * @internal\n */\nexport function generateRuntimeDefault(column: AnyColumn): unknown {\n // Check if column has a default value configuration\n if (!column.default) {\n return undefined;\n }\n\n // If it's a static default value (defaultTo), return undefined\n // as the database should handle this via DEFAULT constraint\n if (\"value\" in column.default) {\n return undefined;\n }\n\n // If it's a database-level special function (defaultTo(b => b.now())), return undefined\n // as the database should handle this via DEFAULT NOW() or equivalent\n if (\"dbSpecial\" in column.default) {\n return undefined;\n }\n\n // Handle runtime defaults (defaultTo$)\n const runtime = column.default.runtime;\n\n if (runtime === \"cuid\") {\n return createId();\n }\n\n if (runtime === \"now\") {\n return new Date();\n }\n\n if (typeof runtime === \"function\") {\n return runtime();\n }\n\n return undefined;\n}\n\n/**\n * Encodes a record of values from the application format to database format.\n *\n * This function transforms object keys to match SQL column names and serializes\n * values according to the database provider's requirements (e.g., converting\n * JavaScript Date objects to numbers for SQLite).\n *\n * @param values - The record of values to encode in application format\n * @param table - The table schema definition containing column information\n * @param generateDefault - Whether to generate default values for undefined columns\n * @param provider - The SQL provider (sqlite, postgresql, mysql, etc.)\n * @param skipDriverConversions - Skip driver-level type conversions (Date->number, boolean->0/1, bigint->Buffer).\n * Set to true when using ORMs like Drizzle that handle these conversions internally.\n * @returns A record with database-compatible column names and serialized values\n *\n * @example\n * ```ts\n * const encoded = encodeValues(\n * { userId: 123, createdAt: new Date() },\n * userTable,\n * true,\n * 'sqlite'\n * );\n * // Returns: { user_id: 123, created_at: 1234567890 }\n * ```\n */\nexport function encodeValues(\n values: Record<string, unknown>,\n table: AnyTable,\n generateDefault: boolean,\n provider: SQLProvider,\n skipDriverConversions = false,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const k in table.columns) {\n const col = table.columns[k];\n\n // Skip internal ID - never provided by user, auto-generated by database\n if (col.role === \"internal-id\") {\n continue;\n }\n let value = values[k];\n\n if (generateDefault && value === undefined) {\n // Only generate runtime defaults (defaultTo$), not static defaults (defaultTo).\n // Static defaults should be handled by the database via DEFAULT constraints.\n value = generateRuntimeDefault(col);\n }\n\n if (value !== undefined) {\n // Handle string references and FragnoId objects\n if (col.role === \"reference\") {\n if (typeof value === \"string\") {\n // String external ID - generate subquery\n const relation = Object.values(table.relations).find((rel) =>\n rel.on.some(([localCol]) => localCol === k),\n );\n if (relation) {\n result[col.name] = new ReferenceSubquery(relation.table, value);\n continue;\n }\n throw new Error(`Reference column ${k} not found in table ${table.name}`);\n } else if (value instanceof FragnoId) {\n // FragnoId object\n if (value.internalId !== undefined) {\n // If internal ID is populated, use it directly (no subquery needed)\n result[col.name] = value.internalId;\n continue;\n } else {\n // If internal ID is not populated, use external ID via subquery\n const relation = Object.values(table.relations).find((rel) =>\n rel.on.some(([localCol]) => localCol === k),\n );\n if (relation) {\n result[col.name] = new ReferenceSubquery(relation.table, value.externalId);\n continue;\n }\n throw new Error(`Reference column ${k} not found in table ${table.name}`);\n }\n }\n }\n\n result[col.name] = serialize(value, col, provider, skipDriverConversions);\n }\n }\n\n return result;\n}\n\n/**\n * Decodes a database result record to application format.\n *\n * This function transforms database column names back to application property names\n * and deserializes values according to the database provider's format (e.g., converting\n * SQLite integers back to JavaScript Date objects).\n *\n * Supports relation data encoded with the pattern `relationName:columnName`.\n *\n * @param result - The raw database result record\n * @param table - The table schema definition containing column and relation information\n * @param provider - The SQL provider (sqlite, postgresql, mysql, etc.)\n * @returns A record in application format with deserialized values\n *\n * @example\n * ```ts\n * const decoded = decodeResult(\n * { user_id: 123, created_at: 1234567890, 'posts:title': 'Hello' },\n * userTable,\n * 'sqlite'\n * );\n * // Returns: { userId: 123, createdAt: Date, posts: { title: 'Hello' } }\n * ```\n */\nexport function decodeResult(\n result: Record<string, unknown>,\n table: AnyTable,\n provider: SQLProvider,\n): Record<string, unknown> {\n const output: Record<string, unknown> = {};\n // First pass: collect all column values\n const columnValues: Record<string, unknown> = {};\n\n // Collect all relation data (including nested) keyed by relation name\n const relationData: Record<string, Record<string, unknown>> = {};\n\n for (const k in result) {\n const colonIndex = k.indexOf(\":\");\n const value = result[k];\n\n // Direct column (no colon)\n if (colonIndex === -1) {\n const col = table.columns[k];\n if (!col) {\n continue;\n }\n\n // Store all column values (including hidden ones for FragnoId creation)\n columnValues[k] = deserialize(value, col, provider);\n continue;\n }\n\n // Relation column (has colon)\n const relationName = k.slice(0, colonIndex);\n const remainder = k.slice(colonIndex + 1);\n\n const relation = table.relations[relationName];\n if (relation === undefined) {\n continue;\n }\n\n // Collect relation data with the remaining key path\n relationData[relationName] ??= {};\n relationData[relationName][remainder] = value;\n }\n\n // Process each relation's data recursively\n for (const relationName in relationData) {\n const relation = table.relations[relationName];\n if (!relation) {\n continue;\n }\n\n // Recursively decode the relation data\n output[relationName] = decodeResult(relationData[relationName], relation.table, provider);\n }\n\n // Second pass: create output with FragnoId objects where appropriate\n for (const k in columnValues) {\n const col = table.columns[k];\n if (!col) {\n continue;\n }\n\n // Filter out hidden columns (like _internalId, _version) from results\n if (col.isHidden) {\n continue;\n }\n\n // For external ID columns, create FragnoId if we have both external and internal IDs\n if (col.role === \"external-id\" && columnValues[\"_internalId\"] !== undefined) {\n output[k] = new FragnoId({\n externalId: columnValues[k] as string,\n internalId: columnValues[\"_internalId\"] as bigint,\n // _version is always selected as a hidden column, so it should always be present\n version: columnValues[\"_version\"] as number,\n });\n } else if (col.role === \"reference\") {\n // For reference columns, create FragnoReference with internal ID\n output[k] = FragnoReference.fromInternal(columnValues[k] as bigint);\n } else {\n output[k] = columnValues[k];\n }\n }\n\n return output;\n}\n"],"mappings":";;;;;;;;;;;AAYA,IAAa,oBAAb,MAA+B;CAC7B;CACA;CAEA,YAAY,iBAA2B,iBAAyB;AAC9D,QAAKA,kBAAmB;AACxB,QAAKC,kBAAmB;;CAG1B,IAAI,kBAAkB;AACpB,SAAO,MAAKD;;CAGd,IAAI,kBAAkB;AACpB,SAAO,MAAKC;;;;;;;;;;;;;;AAehB,SAAgB,uBAAuB,QAA4B;AAEjE,KAAI,CAAC,OAAO,QACV;AAKF,KAAI,WAAW,OAAO,QACpB;AAKF,KAAI,eAAe,OAAO,QACxB;CAIF,MAAM,UAAU,OAAO,QAAQ;AAE/B,KAAI,YAAY,OACd,QAAO,UAAU;AAGnB,KAAI,YAAY,MACd,wBAAO,IAAI,MAAM;AAGnB,KAAI,OAAO,YAAY,WACrB,QAAO,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCpB,SAAgB,aACd,QACA,OACA,iBACA,UACA,wBAAwB,OACC;CACzB,MAAMC,SAAkC,EAAE;AAE1C,MAAK,MAAM,KAAK,MAAM,SAAS;EAC7B,MAAM,MAAM,MAAM,QAAQ;AAG1B,MAAI,IAAI,SAAS,cACf;EAEF,IAAI,QAAQ,OAAO;AAEnB,MAAI,mBAAmB,UAAU,OAG/B,SAAQ,uBAAuB,IAAI;AAGrC,MAAI,UAAU,QAAW;AAEvB,OAAI,IAAI,SAAS,aACf;QAAI,OAAO,UAAU,UAAU;KAE7B,MAAM,WAAW,OAAO,OAAO,MAAM,UAAU,CAAC,MAAM,QACpD,IAAI,GAAG,MAAM,CAAC,cAAc,aAAa,EAAE,CAC5C;AACD,SAAI,UAAU;AACZ,aAAO,IAAI,QAAQ,IAAI,kBAAkB,SAAS,OAAO,MAAM;AAC/D;;AAEF,WAAM,IAAI,MAAM,oBAAoB,EAAE,sBAAsB,MAAM,OAAO;eAChE,iBAAiB,SAE1B,KAAI,MAAM,eAAe,QAAW;AAElC,YAAO,IAAI,QAAQ,MAAM;AACzB;WACK;KAEL,MAAM,WAAW,OAAO,OAAO,MAAM,UAAU,CAAC,MAAM,QACpD,IAAI,GAAG,MAAM,CAAC,cAAc,aAAa,EAAE,CAC5C;AACD,SAAI,UAAU;AACZ,aAAO,IAAI,QAAQ,IAAI,kBAAkB,SAAS,OAAO,MAAM,WAAW;AAC1E;;AAEF,WAAM,IAAI,MAAM,oBAAoB,EAAE,sBAAsB,MAAM,OAAO;;;AAK/E,UAAO,IAAI,QAAQ,UAAU,OAAO,KAAK,UAAU,sBAAsB;;;AAI7E,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BT,SAAgB,aACd,QACA,OACA,UACyB;CACzB,MAAMC,SAAkC,EAAE;CAE1C,MAAMC,eAAwC,EAAE;CAGhD,MAAMC,eAAwD,EAAE;AAEhE,MAAK,MAAM,KAAK,QAAQ;EACtB,MAAM,aAAa,EAAE,QAAQ,IAAI;EACjC,MAAM,QAAQ,OAAO;AAGrB,MAAI,eAAe,IAAI;GACrB,MAAM,MAAM,MAAM,QAAQ;AAC1B,OAAI,CAAC,IACH;AAIF,gBAAa,KAAK,YAAY,OAAO,KAAK,SAAS;AACnD;;EAIF,MAAM,eAAe,EAAE,MAAM,GAAG,WAAW;EAC3C,MAAM,YAAY,EAAE,MAAM,aAAa,EAAE;AAGzC,MADiB,MAAM,UAAU,kBAChB,OACf;AAIF,eAAa,kBAAkB,EAAE;AACjC,eAAa,cAAc,aAAa;;AAI1C,MAAK,MAAM,gBAAgB,cAAc;EACvC,MAAM,WAAW,MAAM,UAAU;AACjC,MAAI,CAAC,SACH;AAIF,SAAO,gBAAgB,aAAa,aAAa,eAAe,SAAS,OAAO,SAAS;;AAI3F,MAAK,MAAM,KAAK,cAAc;EAC5B,MAAM,MAAM,MAAM,QAAQ;AAC1B,MAAI,CAAC,IACH;AAIF,MAAI,IAAI,SACN;AAIF,MAAI,IAAI,SAAS,iBAAiB,aAAa,mBAAmB,OAChE,QAAO,KAAK,IAAI,SAAS;GACvB,YAAY,aAAa;GACzB,YAAY,aAAa;GAEzB,SAAS,aAAa;GACvB,CAAC;WACO,IAAI,SAAS,YAEtB,QAAO,KAAK,gBAAgB,aAAa,aAAa,GAAa;MAEnE,QAAO,KAAK,aAAa;;AAI7B,QAAO"}
|