@tanstack/db 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/dist/cjs/collection.cjs +182 -113
  2. package/dist/cjs/collection.cjs.map +1 -1
  3. package/dist/cjs/collection.d.cts +43 -15
  4. package/dist/cjs/index.cjs +1 -0
  5. package/dist/cjs/index.cjs.map +1 -1
  6. package/dist/cjs/proxy.cjs +87 -248
  7. package/dist/cjs/proxy.cjs.map +1 -1
  8. package/dist/cjs/proxy.d.cts +5 -5
  9. package/dist/cjs/query/compiled-query.cjs +23 -14
  10. package/dist/cjs/query/compiled-query.cjs.map +1 -1
  11. package/dist/cjs/query/compiled-query.d.cts +3 -1
  12. package/dist/cjs/query/evaluators.cjs +35 -20
  13. package/dist/cjs/query/evaluators.cjs.map +1 -1
  14. package/dist/cjs/query/evaluators.d.cts +8 -3
  15. package/dist/cjs/query/extractors.cjs +20 -20
  16. package/dist/cjs/query/extractors.cjs.map +1 -1
  17. package/dist/cjs/query/extractors.d.cts +3 -3
  18. package/dist/cjs/query/group-by.cjs +12 -15
  19. package/dist/cjs/query/group-by.cjs.map +1 -1
  20. package/dist/cjs/query/group-by.d.cts +7 -7
  21. package/dist/cjs/query/joins.cjs +41 -55
  22. package/dist/cjs/query/joins.cjs.map +1 -1
  23. package/dist/cjs/query/joins.d.cts +3 -3
  24. package/dist/cjs/query/order-by.cjs +37 -84
  25. package/dist/cjs/query/order-by.cjs.map +1 -1
  26. package/dist/cjs/query/order-by.d.cts +2 -2
  27. package/dist/cjs/query/pipeline-compiler.cjs +13 -18
  28. package/dist/cjs/query/pipeline-compiler.cjs.map +1 -1
  29. package/dist/cjs/query/pipeline-compiler.d.cts +2 -1
  30. package/dist/cjs/query/query-builder.cjs +22 -29
  31. package/dist/cjs/query/query-builder.cjs.map +1 -1
  32. package/dist/cjs/query/query-builder.d.cts +16 -10
  33. package/dist/cjs/query/schema.d.cts +12 -11
  34. package/dist/cjs/query/select.cjs +47 -24
  35. package/dist/cjs/query/select.cjs.map +1 -1
  36. package/dist/cjs/query/select.d.cts +2 -2
  37. package/dist/cjs/query/types.d.cts +1 -0
  38. package/dist/cjs/transactions.cjs +20 -9
  39. package/dist/cjs/transactions.cjs.map +1 -1
  40. package/dist/cjs/types.d.cts +66 -7
  41. package/dist/esm/collection.d.ts +43 -15
  42. package/dist/esm/collection.js +183 -114
  43. package/dist/esm/collection.js.map +1 -1
  44. package/dist/esm/index.js +2 -1
  45. package/dist/esm/proxy.d.ts +5 -5
  46. package/dist/esm/proxy.js +87 -248
  47. package/dist/esm/proxy.js.map +1 -1
  48. package/dist/esm/query/compiled-query.d.ts +3 -1
  49. package/dist/esm/query/compiled-query.js +23 -14
  50. package/dist/esm/query/compiled-query.js.map +1 -1
  51. package/dist/esm/query/evaluators.d.ts +8 -3
  52. package/dist/esm/query/evaluators.js +36 -21
  53. package/dist/esm/query/evaluators.js.map +1 -1
  54. package/dist/esm/query/extractors.d.ts +3 -3
  55. package/dist/esm/query/extractors.js +20 -20
  56. package/dist/esm/query/extractors.js.map +1 -1
  57. package/dist/esm/query/group-by.d.ts +7 -7
  58. package/dist/esm/query/group-by.js +14 -17
  59. package/dist/esm/query/group-by.js.map +1 -1
  60. package/dist/esm/query/joins.d.ts +3 -3
  61. package/dist/esm/query/joins.js +42 -56
  62. package/dist/esm/query/joins.js.map +1 -1
  63. package/dist/esm/query/order-by.d.ts +2 -2
  64. package/dist/esm/query/order-by.js +39 -86
  65. package/dist/esm/query/order-by.js.map +1 -1
  66. package/dist/esm/query/pipeline-compiler.d.ts +2 -1
  67. package/dist/esm/query/pipeline-compiler.js +14 -19
  68. package/dist/esm/query/pipeline-compiler.js.map +1 -1
  69. package/dist/esm/query/query-builder.d.ts +16 -10
  70. package/dist/esm/query/query-builder.js +22 -29
  71. package/dist/esm/query/query-builder.js.map +1 -1
  72. package/dist/esm/query/schema.d.ts +12 -11
  73. package/dist/esm/query/select.d.ts +2 -2
  74. package/dist/esm/query/select.js +48 -25
  75. package/dist/esm/query/select.js.map +1 -1
  76. package/dist/esm/query/types.d.ts +1 -0
  77. package/dist/esm/transactions.js +20 -9
  78. package/dist/esm/transactions.js.map +1 -1
  79. package/dist/esm/types.d.ts +66 -7
  80. package/package.json +2 -2
  81. package/src/collection.ts +286 -146
  82. package/src/proxy.ts +141 -358
  83. package/src/query/compiled-query.ts +30 -15
  84. package/src/query/evaluators.ts +49 -21
  85. package/src/query/extractors.ts +24 -21
  86. package/src/query/group-by.ts +24 -22
  87. package/src/query/joins.ts +88 -75
  88. package/src/query/order-by.ts +56 -106
  89. package/src/query/pipeline-compiler.ts +34 -37
  90. package/src/query/query-builder.ts +49 -46
  91. package/src/query/schema.ts +18 -15
  92. package/src/query/select.ts +68 -33
  93. package/src/query/types.ts +1 -0
  94. package/src/transactions.ts +30 -14
  95. package/src/types.ts +76 -7
  96. package/dist/cjs/query/key-by.cjs +0 -43
  97. package/dist/cjs/query/key-by.cjs.map +0 -1
  98. package/dist/cjs/query/key-by.d.cts +0 -3
  99. package/dist/esm/query/key-by.d.ts +0 -3
  100. package/dist/esm/query/key-by.js +0 -43
  101. package/dist/esm/query/key-by.js.map +0 -1
  102. package/src/query/key-by.ts +0 -61
package/src/types.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { IStreamBuilder } from "@electric-sql/d2ts"
1
2
  import type { Collection } from "./collection"
2
3
  import type { StandardSchemaV1 } from "@standard-schema/spec"
3
4
  import type { Transaction } from "./transactions"
@@ -13,7 +14,7 @@ export interface PendingMutation<T extends object = Record<string, unknown>> {
13
14
  original: Record<string, unknown>
14
15
  modified: Record<string, unknown>
15
16
  changes: Record<string, unknown>
16
- key: string
17
+ key: any
17
18
  type: OperationType
18
19
  metadata: unknown
19
20
  syncMetadata: Record<string, unknown>
@@ -31,6 +32,16 @@ export type MutationFnParams = {
31
32
 
32
33
  export type MutationFn = (params: MutationFnParams) => Promise<any>
33
34
 
35
+ /**
36
+ * Utility type for a Transaction with at least one mutation
37
+ * This is used internally by the Transaction.commit method
38
+ */
39
+ export type TransactionWithMutations<
40
+ T extends object = Record<string, unknown>,
41
+ > = Transaction & {
42
+ mutations: [PendingMutation<T>, ...Array<PendingMutation<T>>]
43
+ }
44
+
34
45
  export interface TransactionConfig {
35
46
  /** Unique identifier for the transaction */
36
47
  id?: string
@@ -51,7 +62,7 @@ type Value<TExtensions = never> =
51
62
  | null
52
63
  | TExtensions
53
64
  | Array<Value<TExtensions>>
54
- | { [key: string]: Value<TExtensions> }
65
+ | { [key: string | number | symbol]: Value<TExtensions> }
55
66
 
56
67
  export type Row<TExtensions = never> = Record<string, Value<TExtensions>>
57
68
 
@@ -61,19 +72,19 @@ export interface SyncConfig<T extends object = Record<string, unknown>> {
61
72
  sync: (params: {
62
73
  collection: Collection<T>
63
74
  begin: () => void
64
- write: (message: ChangeMessage<T>) => void
75
+ write: (message: Omit<ChangeMessage<T>, `key`>) => void
65
76
  commit: () => void
66
77
  }) => void
67
78
 
68
79
  /**
69
80
  * Get the sync metadata for insert operations
70
- * @returns Record containing primaryKey and relation information
81
+ * @returns Record containing relation information
71
82
  */
72
83
  getSyncMetadata?: () => Record<string, unknown>
73
84
  }
74
85
 
75
86
  export interface ChangeMessage<T extends object = Record<string, unknown>> {
76
- key: string
87
+ key: any
77
88
  value: T
78
89
  previousValue?: T
79
90
  type: OperationType
@@ -110,16 +121,74 @@ export interface OperationConfig {
110
121
  }
111
122
 
112
123
  export interface InsertConfig {
113
- key?: string | Array<string | undefined>
114
124
  metadata?: Record<string, unknown>
115
125
  }
116
126
 
117
127
  export interface CollectionConfig<T extends object = Record<string, unknown>> {
118
- id: string
128
+ // If an id isn't passed in, a UUID will be
129
+ // generated for it.
130
+ id?: string
119
131
  sync: SyncConfig<T>
120
132
  schema?: StandardSchema<T>
133
+ /**
134
+ * Function to extract the ID from an object
135
+ * This is required for update/delete operations which now only accept IDs
136
+ * @param item The item to extract the ID from
137
+ * @returns The ID string for the item
138
+ * @example
139
+ * // For a collection with a 'uuid' field as the primary key
140
+ * getId: (item) => item.uuid
141
+ */
142
+ getId: (item: T) => any
143
+ /**
144
+ * Optional asynchronous handler function called before an insert operation
145
+ * @param params Object containing transaction and mutation information
146
+ * @returns Promise resolving to any value
147
+ */
148
+ onInsert?: MutationFn
149
+ /**
150
+ * Optional asynchronous handler function called before an update operation
151
+ * @param params Object containing transaction and mutation information
152
+ * @returns Promise resolving to any value
153
+ */
154
+ onUpdate?: MutationFn
155
+ /**
156
+ * Optional asynchronous handler function called before a delete operation
157
+ * @param params Object containing transaction and mutation information
158
+ * @returns Promise resolving to any value
159
+ */
160
+ onDelete?: MutationFn
121
161
  }
122
162
 
123
163
  export type ChangesPayload<T extends object = Record<string, unknown>> = Array<
124
164
  ChangeMessage<T>
125
165
  >
166
+
167
+ /**
168
+ * An input row from a collection
169
+ */
170
+ export type InputRow = [unknown, Record<string, unknown>]
171
+
172
+ /**
173
+ * A keyed stream is a stream of rows
174
+ * This is used as the inputs from a collection to a query
175
+ */
176
+ export type KeyedStream = IStreamBuilder<InputRow>
177
+
178
+ /**
179
+ * A namespaced row is a row withing a pipeline that had each table wrapped in its alias
180
+ */
181
+ export type NamespacedRow = Record<string, Record<string, unknown>>
182
+
183
+ /**
184
+ * A keyed namespaced row is a row with a key and a namespaced row
185
+ * This is the main representation of a row in a query pipeline
186
+ */
187
+ export type KeyedNamespacedRow = [unknown, NamespacedRow]
188
+
189
+ /**
190
+ * A namespaced and keyed stream is a stream of rows
191
+ * This is used throughout a query pipeline and as the output from a query without
192
+ * a `select` clause.
193
+ */
194
+ export type NamespacedAndKeyedStream = IStreamBuilder<KeyedNamespacedRow>
@@ -1,43 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const d2ts = require("@electric-sql/d2ts");
4
- function processKeyBy(resultPipeline, query) {
5
- if (!query.keyBy) {
6
- return resultPipeline;
7
- }
8
- const keyByParam = query.keyBy;
9
- resultPipeline = resultPipeline.pipe(
10
- d2ts.keyBy((row) => {
11
- if (Array.isArray(keyByParam)) {
12
- const keyValues = {};
13
- for (const keyColumn of keyByParam) {
14
- const columnName = keyColumn.startsWith(`@`) ? keyColumn.substring(1) : keyColumn;
15
- if (columnName in row) {
16
- keyValues[columnName] = row[columnName];
17
- } else {
18
- throw new Error(
19
- `Key column "${columnName}" not found in result set. Make sure it's included in the select clause.`
20
- );
21
- }
22
- }
23
- return JSON.stringify(keyValues);
24
- } else {
25
- const columnName = keyByParam.startsWith(`@`) ? keyByParam.substring(1) : keyByParam;
26
- if (!(columnName in row)) {
27
- throw new Error(
28
- `Key column "${columnName}" not found in result set. Make sure it's included in the select clause.`
29
- );
30
- }
31
- const keyValue = row[columnName];
32
- if (typeof keyValue === `string` || typeof keyValue === `number`) {
33
- return keyValue;
34
- } else {
35
- return JSON.stringify(keyValue);
36
- }
37
- }
38
- })
39
- );
40
- return resultPipeline;
41
- }
42
- exports.processKeyBy = processKeyBy;
43
- //# sourceMappingURL=key-by.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"key-by.cjs","sources":["../../../src/query/key-by.ts"],"sourcesContent":["import { keyBy } from \"@electric-sql/d2ts\"\nimport type { IStreamBuilder } from \"@electric-sql/d2ts\"\nimport type { Query } from \"./schema\"\n\nexport function processKeyBy(\n resultPipeline: IStreamBuilder<\n Record<string, unknown> | [string | number, Record<string, unknown>]\n >,\n query: Query\n) {\n if (!query.keyBy) {\n return resultPipeline\n }\n const keyByParam = query.keyBy\n\n resultPipeline = resultPipeline.pipe(\n keyBy((row: Record<string, unknown>) => {\n if (Array.isArray(keyByParam)) {\n // Multiple columns - extract values and JSON stringify\n const keyValues: Record<string, unknown> = {}\n for (const keyColumn of keyByParam) {\n // Remove @ prefix if present\n const columnName = (keyColumn as string).startsWith(`@`)\n ? (keyColumn as string).substring(1)\n : (keyColumn as string)\n\n if (columnName in row) {\n keyValues[columnName] = row[columnName]\n } else {\n throw new Error(\n `Key column \"${columnName}\" not found in result set. Make sure it's included in the select clause.`\n )\n }\n }\n return JSON.stringify(keyValues)\n } else {\n // Single column\n // Remove @ prefix if present\n const columnName = (keyByParam as string).startsWith(`@`)\n ? (keyByParam as string).substring(1)\n : (keyByParam as string)\n\n if (!(columnName in row)) {\n throw new Error(\n `Key column \"${columnName}\" not found in result set. Make sure it's included in the select clause.`\n )\n }\n\n const keyValue = row[columnName]\n // Use the value directly if it's a string or number, otherwise JSON stringify\n if (typeof keyValue === `string` || typeof keyValue === `number`) {\n return keyValue\n } else {\n return JSON.stringify(keyValue)\n }\n }\n })\n )\n\n return resultPipeline\n}\n"],"names":["keyBy"],"mappings":";;;AAIgB,SAAA,aACd,gBAGA,OACA;AACI,MAAA,CAAC,MAAM,OAAO;AACT,WAAA;AAAA,EAAA;AAET,QAAM,aAAa,MAAM;AAEzB,mBAAiB,eAAe;AAAA,IAC9BA,KAAA,MAAM,CAAC,QAAiC;AAClC,UAAA,MAAM,QAAQ,UAAU,GAAG;AAE7B,cAAM,YAAqC,CAAC;AAC5C,mBAAW,aAAa,YAAY;AAE5B,gBAAA,aAAc,UAAqB,WAAW,GAAG,IAClD,UAAqB,UAAU,CAAC,IAChC;AAEL,cAAI,cAAc,KAAK;AACX,sBAAA,UAAU,IAAI,IAAI,UAAU;AAAA,UAAA,OACjC;AACL,kBAAM,IAAI;AAAA,cACR,eAAe,UAAU;AAAA,YAC3B;AAAA,UAAA;AAAA,QACF;AAEK,eAAA,KAAK,UAAU,SAAS;AAAA,MAAA,OAC1B;AAGC,cAAA,aAAc,WAAsB,WAAW,GAAG,IACnD,WAAsB,UAAU,CAAC,IACjC;AAED,YAAA,EAAE,cAAc,MAAM;AACxB,gBAAM,IAAI;AAAA,YACR,eAAe,UAAU;AAAA,UAC3B;AAAA,QAAA;AAGI,cAAA,WAAW,IAAI,UAAU;AAE/B,YAAI,OAAO,aAAa,YAAY,OAAO,aAAa,UAAU;AACzD,iBAAA;AAAA,QAAA,OACF;AACE,iBAAA,KAAK,UAAU,QAAQ;AAAA,QAAA;AAAA,MAChC;AAAA,IAEH,CAAA;AAAA,EACH;AAEO,SAAA;AACT;;"}
@@ -1,3 +0,0 @@
1
- import { IStreamBuilder } from '@electric-sql/d2ts';
2
- import { Query } from './schema.cjs';
3
- export declare function processKeyBy(resultPipeline: IStreamBuilder<Record<string, unknown> | [string | number, Record<string, unknown>]>, query: Query): IStreamBuilder<Record<string, unknown> | [string | number, Record<string, unknown>]>;
@@ -1,3 +0,0 @@
1
- import { IStreamBuilder } from '@electric-sql/d2ts';
2
- import { Query } from './schema.js';
3
- export declare function processKeyBy(resultPipeline: IStreamBuilder<Record<string, unknown> | [string | number, Record<string, unknown>]>, query: Query): IStreamBuilder<Record<string, unknown> | [string | number, Record<string, unknown>]>;
@@ -1,43 +0,0 @@
1
- import { keyBy } from "@electric-sql/d2ts";
2
- function processKeyBy(resultPipeline, query) {
3
- if (!query.keyBy) {
4
- return resultPipeline;
5
- }
6
- const keyByParam = query.keyBy;
7
- resultPipeline = resultPipeline.pipe(
8
- keyBy((row) => {
9
- if (Array.isArray(keyByParam)) {
10
- const keyValues = {};
11
- for (const keyColumn of keyByParam) {
12
- const columnName = keyColumn.startsWith(`@`) ? keyColumn.substring(1) : keyColumn;
13
- if (columnName in row) {
14
- keyValues[columnName] = row[columnName];
15
- } else {
16
- throw new Error(
17
- `Key column "${columnName}" not found in result set. Make sure it's included in the select clause.`
18
- );
19
- }
20
- }
21
- return JSON.stringify(keyValues);
22
- } else {
23
- const columnName = keyByParam.startsWith(`@`) ? keyByParam.substring(1) : keyByParam;
24
- if (!(columnName in row)) {
25
- throw new Error(
26
- `Key column "${columnName}" not found in result set. Make sure it's included in the select clause.`
27
- );
28
- }
29
- const keyValue = row[columnName];
30
- if (typeof keyValue === `string` || typeof keyValue === `number`) {
31
- return keyValue;
32
- } else {
33
- return JSON.stringify(keyValue);
34
- }
35
- }
36
- })
37
- );
38
- return resultPipeline;
39
- }
40
- export {
41
- processKeyBy
42
- };
43
- //# sourceMappingURL=key-by.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"key-by.js","sources":["../../../src/query/key-by.ts"],"sourcesContent":["import { keyBy } from \"@electric-sql/d2ts\"\nimport type { IStreamBuilder } from \"@electric-sql/d2ts\"\nimport type { Query } from \"./schema\"\n\nexport function processKeyBy(\n resultPipeline: IStreamBuilder<\n Record<string, unknown> | [string | number, Record<string, unknown>]\n >,\n query: Query\n) {\n if (!query.keyBy) {\n return resultPipeline\n }\n const keyByParam = query.keyBy\n\n resultPipeline = resultPipeline.pipe(\n keyBy((row: Record<string, unknown>) => {\n if (Array.isArray(keyByParam)) {\n // Multiple columns - extract values and JSON stringify\n const keyValues: Record<string, unknown> = {}\n for (const keyColumn of keyByParam) {\n // Remove @ prefix if present\n const columnName = (keyColumn as string).startsWith(`@`)\n ? (keyColumn as string).substring(1)\n : (keyColumn as string)\n\n if (columnName in row) {\n keyValues[columnName] = row[columnName]\n } else {\n throw new Error(\n `Key column \"${columnName}\" not found in result set. Make sure it's included in the select clause.`\n )\n }\n }\n return JSON.stringify(keyValues)\n } else {\n // Single column\n // Remove @ prefix if present\n const columnName = (keyByParam as string).startsWith(`@`)\n ? (keyByParam as string).substring(1)\n : (keyByParam as string)\n\n if (!(columnName in row)) {\n throw new Error(\n `Key column \"${columnName}\" not found in result set. Make sure it's included in the select clause.`\n )\n }\n\n const keyValue = row[columnName]\n // Use the value directly if it's a string or number, otherwise JSON stringify\n if (typeof keyValue === `string` || typeof keyValue === `number`) {\n return keyValue\n } else {\n return JSON.stringify(keyValue)\n }\n }\n })\n )\n\n return resultPipeline\n}\n"],"names":[],"mappings":";AAIgB,SAAA,aACd,gBAGA,OACA;AACI,MAAA,CAAC,MAAM,OAAO;AACT,WAAA;AAAA,EAAA;AAET,QAAM,aAAa,MAAM;AAEzB,mBAAiB,eAAe;AAAA,IAC9B,MAAM,CAAC,QAAiC;AAClC,UAAA,MAAM,QAAQ,UAAU,GAAG;AAE7B,cAAM,YAAqC,CAAC;AAC5C,mBAAW,aAAa,YAAY;AAE5B,gBAAA,aAAc,UAAqB,WAAW,GAAG,IAClD,UAAqB,UAAU,CAAC,IAChC;AAEL,cAAI,cAAc,KAAK;AACX,sBAAA,UAAU,IAAI,IAAI,UAAU;AAAA,UAAA,OACjC;AACL,kBAAM,IAAI;AAAA,cACR,eAAe,UAAU;AAAA,YAC3B;AAAA,UAAA;AAAA,QACF;AAEK,eAAA,KAAK,UAAU,SAAS;AAAA,MAAA,OAC1B;AAGC,cAAA,aAAc,WAAsB,WAAW,GAAG,IACnD,WAAsB,UAAU,CAAC,IACjC;AAED,YAAA,EAAE,cAAc,MAAM;AACxB,gBAAM,IAAI;AAAA,YACR,eAAe,UAAU;AAAA,UAC3B;AAAA,QAAA;AAGI,cAAA,WAAW,IAAI,UAAU;AAE/B,YAAI,OAAO,aAAa,YAAY,OAAO,aAAa,UAAU;AACzD,iBAAA;AAAA,QAAA,OACF;AACE,iBAAA,KAAK,UAAU,QAAQ;AAAA,QAAA;AAAA,MAChC;AAAA,IAEH,CAAA;AAAA,EACH;AAEO,SAAA;AACT;"}
@@ -1,61 +0,0 @@
1
- import { keyBy } from "@electric-sql/d2ts"
2
- import type { IStreamBuilder } from "@electric-sql/d2ts"
3
- import type { Query } from "./schema"
4
-
5
- export function processKeyBy(
6
- resultPipeline: IStreamBuilder<
7
- Record<string, unknown> | [string | number, Record<string, unknown>]
8
- >,
9
- query: Query
10
- ) {
11
- if (!query.keyBy) {
12
- return resultPipeline
13
- }
14
- const keyByParam = query.keyBy
15
-
16
- resultPipeline = resultPipeline.pipe(
17
- keyBy((row: Record<string, unknown>) => {
18
- if (Array.isArray(keyByParam)) {
19
- // Multiple columns - extract values and JSON stringify
20
- const keyValues: Record<string, unknown> = {}
21
- for (const keyColumn of keyByParam) {
22
- // Remove @ prefix if present
23
- const columnName = (keyColumn as string).startsWith(`@`)
24
- ? (keyColumn as string).substring(1)
25
- : (keyColumn as string)
26
-
27
- if (columnName in row) {
28
- keyValues[columnName] = row[columnName]
29
- } else {
30
- throw new Error(
31
- `Key column "${columnName}" not found in result set. Make sure it's included in the select clause.`
32
- )
33
- }
34
- }
35
- return JSON.stringify(keyValues)
36
- } else {
37
- // Single column
38
- // Remove @ prefix if present
39
- const columnName = (keyByParam as string).startsWith(`@`)
40
- ? (keyByParam as string).substring(1)
41
- : (keyByParam as string)
42
-
43
- if (!(columnName in row)) {
44
- throw new Error(
45
- `Key column "${columnName}" not found in result set. Make sure it's included in the select clause.`
46
- )
47
- }
48
-
49
- const keyValue = row[columnName]
50
- // Use the value directly if it's a string or number, otherwise JSON stringify
51
- if (typeof keyValue === `string` || typeof keyValue === `number`) {
52
- return keyValue
53
- } else {
54
- return JSON.stringify(keyValue)
55
- }
56
- }
57
- })
58
- )
59
-
60
- return resultPipeline
61
- }