@tanstack/db 0.0.1

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 (154) hide show
  1. package/README.md +37 -0
  2. package/dist/cjs/SortedMap.cjs +140 -0
  3. package/dist/cjs/SortedMap.cjs.map +1 -0
  4. package/dist/cjs/SortedMap.d.cts +91 -0
  5. package/dist/cjs/collection.cjs +597 -0
  6. package/dist/cjs/collection.cjs.map +1 -0
  7. package/dist/cjs/collection.d.cts +176 -0
  8. package/dist/cjs/deferred.cjs +25 -0
  9. package/dist/cjs/deferred.cjs.map +1 -0
  10. package/dist/cjs/deferred.d.cts +20 -0
  11. package/dist/cjs/errors.cjs +10 -0
  12. package/dist/cjs/errors.cjs.map +1 -0
  13. package/dist/cjs/errors.d.cts +3 -0
  14. package/dist/cjs/index.cjs +33 -0
  15. package/dist/cjs/index.cjs.map +1 -0
  16. package/dist/cjs/index.d.cts +9 -0
  17. package/dist/cjs/proxy.cjs +654 -0
  18. package/dist/cjs/proxy.cjs.map +1 -0
  19. package/dist/cjs/proxy.d.cts +59 -0
  20. package/dist/cjs/query/compiled-query.cjs +162 -0
  21. package/dist/cjs/query/compiled-query.cjs.map +1 -0
  22. package/dist/cjs/query/compiled-query.d.cts +22 -0
  23. package/dist/cjs/query/evaluators.cjs +146 -0
  24. package/dist/cjs/query/evaluators.cjs.map +1 -0
  25. package/dist/cjs/query/evaluators.d.cts +9 -0
  26. package/dist/cjs/query/extractors.cjs +122 -0
  27. package/dist/cjs/query/extractors.cjs.map +1 -0
  28. package/dist/cjs/query/extractors.d.cts +22 -0
  29. package/dist/cjs/query/functions.cjs +152 -0
  30. package/dist/cjs/query/functions.cjs.map +1 -0
  31. package/dist/cjs/query/functions.d.cts +21 -0
  32. package/dist/cjs/query/group-by.cjs +91 -0
  33. package/dist/cjs/query/group-by.cjs.map +1 -0
  34. package/dist/cjs/query/group-by.d.cts +40 -0
  35. package/dist/cjs/query/index.d.cts +5 -0
  36. package/dist/cjs/query/joins.cjs +155 -0
  37. package/dist/cjs/query/joins.cjs.map +1 -0
  38. package/dist/cjs/query/joins.d.cts +14 -0
  39. package/dist/cjs/query/key-by.cjs +43 -0
  40. package/dist/cjs/query/key-by.cjs.map +1 -0
  41. package/dist/cjs/query/key-by.d.cts +3 -0
  42. package/dist/cjs/query/order-by.cjs +229 -0
  43. package/dist/cjs/query/order-by.cjs.map +1 -0
  44. package/dist/cjs/query/order-by.d.cts +3 -0
  45. package/dist/cjs/query/pipeline-compiler.cjs +94 -0
  46. package/dist/cjs/query/pipeline-compiler.cjs.map +1 -0
  47. package/dist/cjs/query/pipeline-compiler.d.cts +9 -0
  48. package/dist/cjs/query/query-builder.cjs +314 -0
  49. package/dist/cjs/query/query-builder.cjs.map +1 -0
  50. package/dist/cjs/query/query-builder.d.cts +219 -0
  51. package/dist/cjs/query/schema.d.cts +98 -0
  52. package/dist/cjs/query/select.cjs +107 -0
  53. package/dist/cjs/query/select.cjs.map +1 -0
  54. package/dist/cjs/query/select.d.cts +3 -0
  55. package/dist/cjs/query/types.d.cts +188 -0
  56. package/dist/cjs/query/utils.cjs +154 -0
  57. package/dist/cjs/query/utils.cjs.map +1 -0
  58. package/dist/cjs/query/utils.d.cts +37 -0
  59. package/dist/cjs/transactions.cjs +137 -0
  60. package/dist/cjs/transactions.cjs.map +1 -0
  61. package/dist/cjs/transactions.d.cts +27 -0
  62. package/dist/cjs/types.d.cts +94 -0
  63. package/dist/cjs/utils.cjs +17 -0
  64. package/dist/cjs/utils.cjs.map +1 -0
  65. package/dist/cjs/utils.d.cts +3 -0
  66. package/dist/esm/SortedMap.d.ts +91 -0
  67. package/dist/esm/SortedMap.js +140 -0
  68. package/dist/esm/SortedMap.js.map +1 -0
  69. package/dist/esm/collection.d.ts +176 -0
  70. package/dist/esm/collection.js +597 -0
  71. package/dist/esm/collection.js.map +1 -0
  72. package/dist/esm/deferred.d.ts +20 -0
  73. package/dist/esm/deferred.js +25 -0
  74. package/dist/esm/deferred.js.map +1 -0
  75. package/dist/esm/errors.d.ts +3 -0
  76. package/dist/esm/errors.js +10 -0
  77. package/dist/esm/errors.js.map +1 -0
  78. package/dist/esm/index.d.ts +9 -0
  79. package/dist/esm/index.js +33 -0
  80. package/dist/esm/index.js.map +1 -0
  81. package/dist/esm/proxy.d.ts +59 -0
  82. package/dist/esm/proxy.js +654 -0
  83. package/dist/esm/proxy.js.map +1 -0
  84. package/dist/esm/query/compiled-query.d.ts +22 -0
  85. package/dist/esm/query/compiled-query.js +162 -0
  86. package/dist/esm/query/compiled-query.js.map +1 -0
  87. package/dist/esm/query/evaluators.d.ts +9 -0
  88. package/dist/esm/query/evaluators.js +146 -0
  89. package/dist/esm/query/evaluators.js.map +1 -0
  90. package/dist/esm/query/extractors.d.ts +22 -0
  91. package/dist/esm/query/extractors.js +122 -0
  92. package/dist/esm/query/extractors.js.map +1 -0
  93. package/dist/esm/query/functions.d.ts +21 -0
  94. package/dist/esm/query/functions.js +152 -0
  95. package/dist/esm/query/functions.js.map +1 -0
  96. package/dist/esm/query/group-by.d.ts +40 -0
  97. package/dist/esm/query/group-by.js +91 -0
  98. package/dist/esm/query/group-by.js.map +1 -0
  99. package/dist/esm/query/index.d.ts +5 -0
  100. package/dist/esm/query/joins.d.ts +14 -0
  101. package/dist/esm/query/joins.js +155 -0
  102. package/dist/esm/query/joins.js.map +1 -0
  103. package/dist/esm/query/key-by.d.ts +3 -0
  104. package/dist/esm/query/key-by.js +43 -0
  105. package/dist/esm/query/key-by.js.map +1 -0
  106. package/dist/esm/query/order-by.d.ts +3 -0
  107. package/dist/esm/query/order-by.js +229 -0
  108. package/dist/esm/query/order-by.js.map +1 -0
  109. package/dist/esm/query/pipeline-compiler.d.ts +9 -0
  110. package/dist/esm/query/pipeline-compiler.js +94 -0
  111. package/dist/esm/query/pipeline-compiler.js.map +1 -0
  112. package/dist/esm/query/query-builder.d.ts +219 -0
  113. package/dist/esm/query/query-builder.js +314 -0
  114. package/dist/esm/query/query-builder.js.map +1 -0
  115. package/dist/esm/query/schema.d.ts +98 -0
  116. package/dist/esm/query/select.d.ts +3 -0
  117. package/dist/esm/query/select.js +107 -0
  118. package/dist/esm/query/select.js.map +1 -0
  119. package/dist/esm/query/types.d.ts +188 -0
  120. package/dist/esm/query/utils.d.ts +37 -0
  121. package/dist/esm/query/utils.js +154 -0
  122. package/dist/esm/query/utils.js.map +1 -0
  123. package/dist/esm/transactions.d.ts +27 -0
  124. package/dist/esm/transactions.js +137 -0
  125. package/dist/esm/transactions.js.map +1 -0
  126. package/dist/esm/types.d.ts +94 -0
  127. package/dist/esm/utils.d.ts +3 -0
  128. package/dist/esm/utils.js +17 -0
  129. package/dist/esm/utils.js.map +1 -0
  130. package/package.json +57 -0
  131. package/src/SortedMap.ts +163 -0
  132. package/src/collection.ts +919 -0
  133. package/src/deferred.ts +47 -0
  134. package/src/errors.ts +6 -0
  135. package/src/index.ts +12 -0
  136. package/src/proxy.ts +1104 -0
  137. package/src/query/compiled-query.ts +193 -0
  138. package/src/query/evaluators.ts +222 -0
  139. package/src/query/extractors.ts +211 -0
  140. package/src/query/functions.ts +297 -0
  141. package/src/query/group-by.ts +137 -0
  142. package/src/query/index.ts +5 -0
  143. package/src/query/joins.ts +247 -0
  144. package/src/query/key-by.ts +61 -0
  145. package/src/query/order-by.ts +312 -0
  146. package/src/query/pipeline-compiler.ts +152 -0
  147. package/src/query/query-builder.ts +898 -0
  148. package/src/query/schema.ts +255 -0
  149. package/src/query/select.ts +173 -0
  150. package/src/query/types.ts +417 -0
  151. package/src/query/utils.ts +245 -0
  152. package/src/transactions.ts +198 -0
  153. package/src/types.ts +125 -0
  154. package/src/utils.ts +15 -0
@@ -0,0 +1,188 @@
1
+ import { ConditionOperand, ExplicitLiteral, FunctionCall, LiteralValue, Select } from './schema.js';
2
+ export type Input = Record<string, unknown>;
3
+ export type Schema = Record<string, Input>;
4
+ export type Context<TBaseSchema extends Schema = Schema, TSchema extends Schema = Schema> = {
5
+ baseSchema: TBaseSchema;
6
+ schema: TSchema;
7
+ default?: keyof TSchema;
8
+ result?: Record<string, unknown>;
9
+ };
10
+ export type Flatten<T> = {
11
+ [K in keyof T]: T[K];
12
+ } & {};
13
+ type UniqueSecondLevelKeys<T> = {
14
+ [K in keyof T]: Exclude<keyof T[K], {
15
+ [P in Exclude<keyof T, K>]: keyof T[P];
16
+ }[Exclude<keyof T, K>]>;
17
+ }[keyof T];
18
+ type InputNames<TSchema extends Schema> = RemoveIndexSignature<{
19
+ [I in keyof TSchema]: I;
20
+ }>[keyof RemoveIndexSignature<{
21
+ [I in keyof TSchema]: I;
22
+ }>];
23
+ type UniquePropertyNames<TSchema extends Schema> = UniqueSecondLevelKeys<RemoveIndexSignature<TSchema>>;
24
+ export type RemoveIndexSignature<T> = {
25
+ [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K];
26
+ };
27
+ type QualifiedReferencesOfSchemaString<TSchema extends Schema> = RemoveIndexSignature<{
28
+ [I in keyof TSchema]: {
29
+ [P in keyof RemoveIndexSignature<TSchema[I]>]: `@${string & I}.${string & P}`;
30
+ }[keyof RemoveIndexSignature<TSchema[I]>];
31
+ }>;
32
+ type QualifiedReferenceString<TContext extends Context<Schema>> = QualifiedReferencesOfSchemaString<TContext[`schema`]>[keyof QualifiedReferencesOfSchemaString<TContext[`schema`]>];
33
+ type QualifiedReferencesOfSchemaObject<TSchema extends Schema> = RemoveIndexSignature<{
34
+ [I in keyof TSchema]: {
35
+ [P in keyof RemoveIndexSignature<TSchema[I]>]: {
36
+ col: `${string & I}.${string & P}`;
37
+ };
38
+ }[keyof RemoveIndexSignature<TSchema[I]>];
39
+ }>;
40
+ type QualifiedReferenceObject<TContext extends Context<Schema>> = QualifiedReferencesOfSchemaObject<TContext[`schema`]>[keyof QualifiedReferencesOfSchemaObject<TContext[`schema`]>];
41
+ type QualifiedReference<TContext extends Context<Schema>> = QualifiedReferenceString<TContext> | QualifiedReferenceObject<TContext>;
42
+ type DefaultReferencesOfSchemaString<TSchema extends Schema, TDefault extends keyof TSchema> = RemoveIndexSignature<{
43
+ [P in keyof TSchema[TDefault]]: `@${string & P}`;
44
+ }>;
45
+ type DefaultReferenceString<TContext extends Context<Schema>> = TContext[`default`] extends undefined ? never : DefaultReferencesOfSchemaString<TContext[`schema`], Exclude<TContext[`default`], undefined>>[keyof DefaultReferencesOfSchemaString<TContext[`schema`], Exclude<TContext[`default`], undefined>>];
46
+ type DefaultReferencesOfSchemaObject<TSchema extends Schema, TDefault extends keyof TSchema> = RemoveIndexSignature<{
47
+ [P in keyof TSchema[TDefault]]: {
48
+ col: `${string & P}`;
49
+ };
50
+ }>;
51
+ type DefaultReferenceObject<TContext extends Context<Schema>> = TContext[`default`] extends undefined ? never : DefaultReferencesOfSchemaObject<TContext[`schema`], Exclude<TContext[`default`], undefined>>[keyof DefaultReferencesOfSchemaObject<TContext[`schema`], Exclude<TContext[`default`], undefined>>];
52
+ type DefaultReference<TContext extends Context<Schema>> = DefaultReferenceString<TContext> | DefaultReferenceObject<TContext>;
53
+ type UniqueReferencesOfSchemaString<TSchema extends Schema> = RemoveIndexSignature<{
54
+ [I in keyof TSchema]: {
55
+ [P in keyof TSchema[I]]: P extends UniquePropertyNames<TSchema> ? `@${string & P}` : never;
56
+ }[keyof TSchema[I]];
57
+ }>;
58
+ type UniqueReferenceString<TContext extends Context<Schema>> = UniqueReferencesOfSchemaString<TContext[`schema`]>[keyof UniqueReferencesOfSchemaString<TContext[`schema`]>];
59
+ type UniqueReferencesOfSchemaObject<TSchema extends Schema> = RemoveIndexSignature<{
60
+ [I in keyof TSchema]: {
61
+ [P in keyof TSchema[I]]: P extends UniquePropertyNames<TSchema> ? {
62
+ col: `${string & P}`;
63
+ } : never;
64
+ }[keyof TSchema[I]];
65
+ }>;
66
+ type UniqueReferenceObject<TContext extends Context<Schema>> = UniqueReferencesOfSchemaObject<TContext[`schema`]>[keyof UniqueReferencesOfSchemaObject<TContext[`schema`]>];
67
+ type UniqueReference<TContext extends Context<Schema>> = UniqueReferenceString<TContext> | UniqueReferenceObject<TContext>;
68
+ type InputWildcardString<TContext extends Context<Schema>> = Flatten<{
69
+ [I in InputNames<TContext[`schema`]>]: `@${I}.*`;
70
+ }[InputNames<TContext[`schema`]>]>;
71
+ type InputWildcardObject<TContext extends Context<Schema>> = Flatten<{
72
+ [I in InputNames<TContext[`schema`]>]: {
73
+ col: `${I}.*`;
74
+ };
75
+ }[InputNames<TContext[`schema`]>]>;
76
+ type InputWildcard<TContext extends Context<Schema>> = InputWildcardString<TContext> | InputWildcardObject<TContext>;
77
+ type AllWildcardString = `@*`;
78
+ type AllWildcardObject = {
79
+ col: `*`;
80
+ };
81
+ type AllWildcard = AllWildcardString | AllWildcardObject;
82
+ export type PropertyReferenceString<TContext extends Context<Schema>> = DefaultReferenceString<TContext> | QualifiedReferenceString<TContext> | UniqueReferenceString<TContext>;
83
+ export type WildcardReferenceString<TContext extends Context<Schema>> = InputWildcardString<TContext> | AllWildcardString;
84
+ export type PropertyReferenceObject<TContext extends Context<Schema>> = DefaultReferenceObject<TContext> | QualifiedReferenceObject<TContext> | UniqueReferenceObject<TContext>;
85
+ export type WildcardReferenceObject<TContext extends Context<Schema>> = InputWildcardObject<TContext> | AllWildcardObject;
86
+ export type PropertyReference<TContext extends Context<Schema>> = DefaultReference<TContext> | QualifiedReference<TContext> | UniqueReference<TContext>;
87
+ export type WildcardReference<TContext extends Context<Schema>> = InputWildcard<TContext> | AllWildcard;
88
+ type InputWithProperty<TSchema extends Schema, TProperty extends string> = {
89
+ [I in keyof RemoveIndexSignature<TSchema>]: TProperty extends keyof TSchema[I] ? I : never;
90
+ }[keyof RemoveIndexSignature<TSchema>];
91
+ export type TypeFromPropertyReference<TContext extends Context<Schema>, TReference extends PropertyReference<TContext>> = TReference extends `@${infer InputName}.${infer PropName}` | {
92
+ col: `${infer InputName}.${infer PropName}`;
93
+ } ? InputName extends keyof TContext[`schema`] ? PropName extends keyof TContext[`schema`][InputName] ? TContext[`schema`][InputName][PropName] : never : never : TReference extends `@${infer PropName}` | {
94
+ col: `${infer PropName}`;
95
+ } ? PropName extends keyof TContext[`schema`][Exclude<TContext[`default`], undefined>] ? TContext[`schema`][Exclude<TContext[`default`], undefined>][PropName] : TContext[`schema`][InputWithProperty<TContext[`schema`], PropName>][PropName] : never;
96
+ /**
97
+ * Return the key that would be used in the result of the query for a given property
98
+ * reference.
99
+ * - `@id` -> `id`
100
+ * - `@employees.id` -> `id`
101
+ * - `{ col: 'id' }` -> `id`
102
+ * - `{ col: 'employees.id' }` -> `id`
103
+ */
104
+ export type ResultKeyFromPropertyReference<TContext extends Context<Schema>, TReference extends PropertyReference<TContext>> = TReference extends `@${infer _InputName}.${infer PropName}` ? PropName : TReference extends {
105
+ col: `${infer _InputName}.${infer PropName}`;
106
+ } ? PropName : TReference extends `@${infer PropName}` ? PropName : TReference extends {
107
+ col: `${infer PropName}`;
108
+ } ? PropName : never;
109
+ export type InputReference<TContext extends Context<Schema>> = {
110
+ [I in InputNames<TContext[`schema`]>]: I;
111
+ }[InputNames<TContext[`schema`]>];
112
+ export type RenameInput<TSchema extends Schema, TInput extends keyof TSchema, TNewName extends string> = Flatten<{
113
+ [K in Exclude<keyof TSchema, TInput>]: TSchema[K];
114
+ } & {
115
+ [P in TNewName]: TSchema[TInput];
116
+ }>;
117
+ export type MaybeRenameInput<TSchema extends Schema, TInput extends keyof TSchema, TNewName extends string | undefined> = TNewName extends undefined ? TSchema : RenameInput<TSchema, TInput, Exclude<TNewName, undefined>>;
118
+ /**
119
+ * Helper type to combine result types from each select item in a tuple
120
+ */
121
+ export type InferResultTypeFromSelectTuple<TContext extends Context<Schema>, TSelects extends ReadonlyArray<Select<TContext>>> = UnionToIntersection<{
122
+ [K in keyof TSelects]: TSelects[K] extends Select<TContext> ? InferResultType<TContext, TSelects[K]> : never;
123
+ }[number]>;
124
+ /**
125
+ * Convert a union type to an intersection type
126
+ */
127
+ type UnionToIntersection<TUnion> = (TUnion extends any ? (x: TUnion) => void : never) extends (x: infer I) => void ? I : never;
128
+ /**
129
+ * Infers the result type from a single select item
130
+ */
131
+ type InferResultType<TContext extends Context<Schema>, TSelect extends Select<TContext>> = TSelect extends PropertyReferenceString<TContext> ? {
132
+ [K in ResultKeyFromPropertyReference<TContext, TSelect>]: TypeFromPropertyReference<TContext, TSelect>;
133
+ } : TSelect extends WildcardReferenceString<TContext> ? TSelect extends `@*` ? InferAllColumnsType<TContext> : TSelect extends `@${infer TableName}.*` ? TableName extends keyof TContext[`schema`] ? InferTableColumnsType<TContext, TableName> : {} : {} : TSelect extends {
134
+ [alias: string]: PropertyReference<TContext> | FunctionCall<TContext>;
135
+ } ? {
136
+ [K in keyof TSelect]: TSelect[K] extends PropertyReference<TContext> ? TypeFromPropertyReference<TContext, TSelect[K]> : TSelect[K] extends FunctionCall<TContext> ? InferFunctionCallResultType<TContext, TSelect[K]> : never;
137
+ } : {};
138
+ /**
139
+ * Infers the result type for all columns from all tables
140
+ */
141
+ type InferAllColumnsType<TContext extends Context<Schema>> = {
142
+ [K in keyof TContext[`schema`]]: {
143
+ [P in keyof TContext[`schema`][K]]: TContext[`schema`][K][P];
144
+ };
145
+ }[keyof TContext[`schema`]];
146
+ /**
147
+ * Infers the result type for all columns from a specific table
148
+ */
149
+ type InferTableColumnsType<TContext extends Context<Schema>, TTable extends keyof TContext[`schema`]> = {
150
+ [P in keyof TContext[`schema`][TTable]]: TContext[`schema`][TTable][P];
151
+ };
152
+ /**
153
+ * Infers the result type for a function call
154
+ */
155
+ type InferFunctionCallResultType<TContext extends Context<Schema>, TFunctionCall extends FunctionCall<TContext>> = TFunctionCall extends {
156
+ SUM: any;
157
+ } ? number : TFunctionCall extends {
158
+ COUNT: any;
159
+ } ? number : TFunctionCall extends {
160
+ AVG: any;
161
+ } ? number : TFunctionCall extends {
162
+ MIN: any;
163
+ } ? InferOperandType<TContext, TFunctionCall[`MIN`]> : TFunctionCall extends {
164
+ MAX: any;
165
+ } ? InferOperandType<TContext, TFunctionCall[`MAX`]> : TFunctionCall extends {
166
+ DATE: any;
167
+ } ? string : TFunctionCall extends {
168
+ JSON_EXTRACT: any;
169
+ } ? unknown : TFunctionCall extends {
170
+ JSON_EXTRACT_PATH: any;
171
+ } ? unknown : TFunctionCall extends {
172
+ UPPER: any;
173
+ } ? string : TFunctionCall extends {
174
+ LOWER: any;
175
+ } ? string : TFunctionCall extends {
176
+ COALESCE: any;
177
+ } ? InferOperandType<TContext, TFunctionCall[`COALESCE`]> : TFunctionCall extends {
178
+ CONCAT: any;
179
+ } ? string : TFunctionCall extends {
180
+ LENGTH: any;
181
+ } ? number : TFunctionCall extends {
182
+ ORDER_INDEX: any;
183
+ } ? number : unknown;
184
+ /**
185
+ * Infers the type of an operand
186
+ */
187
+ type InferOperandType<TContext extends Context<Schema>, TOperand extends ConditionOperand<TContext>> = TOperand extends PropertyReference<TContext> ? TypeFromPropertyReference<TContext, TOperand> : TOperand extends LiteralValue ? TOperand : TOperand extends ExplicitLiteral ? TOperand[`value`] : TOperand extends FunctionCall<TContext> ? InferFunctionCallResultType<TContext, TOperand> : TOperand extends Array<ConditionOperand<TContext>> ? InferOperandType<TContext, TOperand[number]> : unknown;
188
+ export {};
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Helper function to determine if an object is a function call with an aggregate function
3
+ */
4
+ export declare function isAggregateFunctionCall(obj: any): boolean;
5
+ /**
6
+ * Helper function to determine if an object is an ORDER_INDEX function call
7
+ */
8
+ export declare function isOrderIndexFunctionCall(obj: any): boolean;
9
+ /**
10
+ * Type guard to check if a value is comparable (can be used with <, >, <=, >= operators)
11
+ * @param value The value to check
12
+ * @returns True if the value is comparable
13
+ */
14
+ export declare function isComparable(value: unknown): value is number | string | Date | boolean;
15
+ /**
16
+ * Performs a comparison between two values, ensuring they are of compatible types
17
+ * @param left The left operand
18
+ * @param right The right operand
19
+ * @param operator The comparison operator
20
+ * @returns The result of the comparison
21
+ * @throws Error if the values are not comparable
22
+ */
23
+ export declare function compareValues(left: unknown, right: unknown, operator: `<` | `<=` | `>` | `>=`): boolean;
24
+ /**
25
+ * Converts a SQL LIKE pattern to a JavaScript regex pattern
26
+ * @param pattern The SQL LIKE pattern to convert
27
+ * @returns A regex-compatible pattern string
28
+ */
29
+ export declare function convertLikeToRegex(pattern: string): string;
30
+ /**
31
+ * Helper function to check if a value is in an array, with special handling for various types
32
+ * @param value The value to check for
33
+ * @param array The array to search in
34
+ * @param caseInsensitive Optional flag to enable case-insensitive matching for strings (default: false)
35
+ * @returns True if the value is found in the array
36
+ */
37
+ export declare function isValueInArray(value: unknown, array: Array<unknown>, caseInsensitive?: boolean): boolean;
@@ -0,0 +1,154 @@
1
+ function isAggregateFunctionCall(obj) {
2
+ if (!obj || typeof obj !== `object`) return false;
3
+ const aggregateFunctions = [
4
+ `SUM`,
5
+ `COUNT`,
6
+ `AVG`,
7
+ `MIN`,
8
+ `MAX`,
9
+ `MEDIAN`,
10
+ `MODE`
11
+ ];
12
+ const keys = Object.keys(obj);
13
+ return keys.length === 1 && aggregateFunctions.includes(keys[0]);
14
+ }
15
+ function isOrderIndexFunctionCall(obj) {
16
+ if (!obj || typeof obj !== `object`) return false;
17
+ const keys = Object.keys(obj);
18
+ return keys.length === 1 && keys[0] === `ORDER_INDEX`;
19
+ }
20
+ function isComparable(value) {
21
+ return typeof value === `number` || typeof value === `string` || typeof value === `boolean` || value instanceof Date;
22
+ }
23
+ function compareValues(left, right, operator) {
24
+ if (!isComparable(left) || !isComparable(right)) {
25
+ throw new Error(
26
+ `Cannot compare non-comparable values: ${typeof left} and ${typeof right}`
27
+ );
28
+ }
29
+ if (typeof left !== typeof right && (typeof left === `string` || typeof left === `number`) && (typeof right === `string` || typeof right === `number`)) {
30
+ const leftStr = String(left);
31
+ const rightStr = String(right);
32
+ switch (operator) {
33
+ case `<`:
34
+ return leftStr < rightStr;
35
+ case `<=`:
36
+ return leftStr <= rightStr;
37
+ case `>`:
38
+ return leftStr > rightStr;
39
+ case `>=`:
40
+ return leftStr >= rightStr;
41
+ }
42
+ }
43
+ if (left instanceof Date && right instanceof Date) {
44
+ const leftTime = left.getTime();
45
+ const rightTime = right.getTime();
46
+ switch (operator) {
47
+ case `<`:
48
+ return leftTime < rightTime;
49
+ case `<=`:
50
+ return leftTime <= rightTime;
51
+ case `>`:
52
+ return leftTime > rightTime;
53
+ case `>=`:
54
+ return leftTime >= rightTime;
55
+ }
56
+ }
57
+ if (typeof left === typeof right) {
58
+ switch (operator) {
59
+ case `<`:
60
+ return left < right;
61
+ case `<=`:
62
+ return left <= right;
63
+ case `>`:
64
+ return left > right;
65
+ case `>=`:
66
+ return left >= right;
67
+ }
68
+ }
69
+ throw new Error(
70
+ `Cannot compare incompatible types: ${typeof left} and ${typeof right}`
71
+ );
72
+ }
73
+ function convertLikeToRegex(pattern) {
74
+ let finalPattern = ``;
75
+ let i = 0;
76
+ while (i < pattern.length) {
77
+ const char = pattern[i];
78
+ if (char === `\\` && i + 1 < pattern.length) {
79
+ finalPattern += pattern[i + 1];
80
+ i += 2;
81
+ continue;
82
+ }
83
+ switch (char) {
84
+ case `%`:
85
+ finalPattern += `.*`;
86
+ break;
87
+ case `_`:
88
+ finalPattern += `.`;
89
+ break;
90
+ // Handle regex special characters
91
+ case `.`:
92
+ case `^`:
93
+ case `$`:
94
+ case `*`:
95
+ case `+`:
96
+ case `?`:
97
+ case `(`:
98
+ case `)`:
99
+ case `[`:
100
+ case `]`:
101
+ case `{`:
102
+ case `}`:
103
+ case `|`:
104
+ case `/`:
105
+ finalPattern += `\\` + char;
106
+ break;
107
+ default:
108
+ finalPattern += char;
109
+ }
110
+ i++;
111
+ }
112
+ return finalPattern;
113
+ }
114
+ function isValueInArray(value, array, caseInsensitive = false) {
115
+ if (array.includes(value)) {
116
+ return true;
117
+ }
118
+ if (value === null || value === void 0) {
119
+ return array.some((item) => item === null || item === void 0);
120
+ }
121
+ if (typeof value === `number` || typeof value === `string`) {
122
+ return array.some((item) => {
123
+ if (typeof item === typeof value) {
124
+ if (typeof value === `string` && caseInsensitive) {
125
+ return value.toLowerCase() === item.toLowerCase();
126
+ }
127
+ return item === value;
128
+ }
129
+ if ((typeof item === `number` || typeof item === `string`) && (typeof value === `number` || typeof value === `string`)) {
130
+ return String(item) === String(value);
131
+ }
132
+ return false;
133
+ });
134
+ }
135
+ if (typeof value === `object`) {
136
+ const valueStr = JSON.stringify(value);
137
+ return array.some((item) => {
138
+ if (typeof item === `object` && item !== null) {
139
+ return JSON.stringify(item) === valueStr;
140
+ }
141
+ return false;
142
+ });
143
+ }
144
+ return false;
145
+ }
146
+ export {
147
+ compareValues,
148
+ convertLikeToRegex,
149
+ isAggregateFunctionCall,
150
+ isComparable,
151
+ isOrderIndexFunctionCall,
152
+ isValueInArray
153
+ };
154
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sources":["../../../src/query/utils.ts"],"sourcesContent":["/**\n * Helper function to determine if an object is a function call with an aggregate function\n */\nexport function isAggregateFunctionCall(obj: any): boolean {\n if (!obj || typeof obj !== `object`) return false\n\n const aggregateFunctions = [\n `SUM`,\n `COUNT`,\n `AVG`,\n `MIN`,\n `MAX`,\n `MEDIAN`,\n `MODE`,\n ]\n const keys = Object.keys(obj)\n\n return keys.length === 1 && aggregateFunctions.includes(keys[0]!)\n}\n\n/**\n * Helper function to determine if an object is an ORDER_INDEX function call\n */\nexport function isOrderIndexFunctionCall(obj: any): boolean {\n if (!obj || typeof obj !== `object`) return false\n\n const keys = Object.keys(obj)\n return keys.length === 1 && keys[0] === `ORDER_INDEX`\n}\n\n/**\n * Type guard to check if a value is comparable (can be used with <, >, <=, >= operators)\n * @param value The value to check\n * @returns True if the value is comparable\n */\nexport function isComparable(\n value: unknown\n): value is number | string | Date | boolean {\n return (\n typeof value === `number` ||\n typeof value === `string` ||\n typeof value === `boolean` ||\n value instanceof Date\n )\n}\n\n/**\n * Performs a comparison between two values, ensuring they are of compatible types\n * @param left The left operand\n * @param right The right operand\n * @param operator The comparison operator\n * @returns The result of the comparison\n * @throws Error if the values are not comparable\n */\nexport function compareValues(\n left: unknown,\n right: unknown,\n operator: `<` | `<=` | `>` | `>=`\n): boolean {\n // First check if both values are comparable\n if (!isComparable(left) || !isComparable(right)) {\n throw new Error(\n `Cannot compare non-comparable values: ${typeof left} and ${typeof right}`\n )\n }\n\n // If they're different types but both are strings or numbers, convert to strings\n if (\n typeof left !== typeof right &&\n (typeof left === `string` || typeof left === `number`) &&\n (typeof right === `string` || typeof right === `number`)\n ) {\n // Convert to strings for comparison (follows JavaScript's coercion rules)\n const leftStr = String(left)\n const rightStr = String(right)\n\n switch (operator) {\n case `<`:\n return leftStr < rightStr\n case `<=`:\n return leftStr <= rightStr\n case `>`:\n return leftStr > rightStr\n case `>=`:\n return leftStr >= rightStr\n }\n }\n\n // For Date objects, convert to timestamps\n if (left instanceof Date && right instanceof Date) {\n const leftTime = left.getTime()\n const rightTime = right.getTime()\n\n switch (operator) {\n case `<`:\n return leftTime < rightTime\n case `<=`:\n return leftTime <= rightTime\n case `>`:\n return leftTime > rightTime\n case `>=`:\n return leftTime >= rightTime\n }\n }\n\n // For other cases where types match\n if (typeof left === typeof right) {\n switch (operator) {\n case `<`:\n return left < right\n case `<=`:\n return left <= right\n case `>`:\n return left > right\n case `>=`:\n return left >= right\n }\n }\n\n // If we get here, it means the values are technically comparable but not compatible\n throw new Error(\n `Cannot compare incompatible types: ${typeof left} and ${typeof right}`\n )\n}\n\n/**\n * Converts a SQL LIKE pattern to a JavaScript regex pattern\n * @param pattern The SQL LIKE pattern to convert\n * @returns A regex-compatible pattern string\n */\nexport function convertLikeToRegex(pattern: string): string {\n let finalPattern = ``\n let i = 0\n\n while (i < pattern.length) {\n const char = pattern[i]\n\n // Handle escape character\n if (char === `\\\\` && i + 1 < pattern.length) {\n // Add the next character as a literal (escaped)\n finalPattern += pattern[i + 1]\n i += 2 // Skip both the escape and the escaped character\n continue\n }\n\n // Handle SQL LIKE special characters\n switch (char) {\n case `%`:\n // % matches any sequence of characters (including empty)\n finalPattern += `.*`\n break\n case `_`:\n // _ matches any single character\n finalPattern += `.`\n break\n // Handle regex special characters\n case `.`:\n case `^`:\n case `$`:\n case `*`:\n case `+`:\n case `?`:\n case `(`:\n case `)`:\n case `[`:\n case `]`:\n case `{`:\n case `}`:\n case `|`:\n case `/`:\n // Escape regex special characters\n finalPattern += `\\\\` + char\n break\n default:\n // Regular character, just add it\n finalPattern += char\n }\n\n i++\n }\n\n return finalPattern\n}\n\n/**\n * Helper function to check if a value is in an array, with special handling for various types\n * @param value The value to check for\n * @param array The array to search in\n * @param caseInsensitive Optional flag to enable case-insensitive matching for strings (default: false)\n * @returns True if the value is found in the array\n */\nexport function isValueInArray(\n value: unknown,\n array: Array<unknown>,\n caseInsensitive: boolean = false\n): boolean {\n // Direct inclusion check first (fastest path)\n if (array.includes(value)) {\n return true\n }\n\n // Handle null/undefined\n if (value === null || value === undefined) {\n return array.some((item) => item === null || item === undefined)\n }\n\n // Handle numbers and strings with type coercion\n if (typeof value === `number` || typeof value === `string`) {\n return array.some((item) => {\n // Same type, direct comparison\n if (typeof item === typeof value) {\n if (typeof value === `string` && caseInsensitive) {\n // Case-insensitive comparison for strings (only if explicitly enabled)\n return value.toLowerCase() === (item as string).toLowerCase()\n }\n return item === value\n }\n\n // Different types, try coercion for number/string\n if (\n (typeof item === `number` || typeof item === `string`) &&\n (typeof value === `number` || typeof value === `string`)\n ) {\n // Convert both to strings for comparison\n return String(item) === String(value)\n }\n\n return false\n })\n }\n\n // Handle objects/arrays by comparing stringified versions\n if (typeof value === `object`) {\n const valueStr = JSON.stringify(value)\n return array.some((item) => {\n if (typeof item === `object` && item !== null) {\n return JSON.stringify(item) === valueStr\n }\n return false\n })\n }\n\n // Fallback\n return false\n}\n"],"names":[],"mappings":"AAGO,SAAS,wBAAwB,KAAmB;AACzD,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAiB,QAAA;AAE5C,QAAM,qBAAqB;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACM,QAAA,OAAO,OAAO,KAAK,GAAG;AAE5B,SAAO,KAAK,WAAW,KAAK,mBAAmB,SAAS,KAAK,CAAC,CAAE;AAClE;AAKO,SAAS,yBAAyB,KAAmB;AAC1D,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAiB,QAAA;AAEtC,QAAA,OAAO,OAAO,KAAK,GAAG;AAC5B,SAAO,KAAK,WAAW,KAAK,KAAK,CAAC,MAAM;AAC1C;AAOO,SAAS,aACd,OAC2C;AAEzC,SAAA,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,OAAO,UAAU,aACjB,iBAAiB;AAErB;AAUgB,SAAA,cACd,MACA,OACA,UACS;AAET,MAAI,CAAC,aAAa,IAAI,KAAK,CAAC,aAAa,KAAK,GAAG;AAC/C,UAAM,IAAI;AAAA,MACR,yCAAyC,OAAO,IAAI,QAAQ,OAAO,KAAK;AAAA,IAC1E;AAAA,EAAA;AAIF,MACE,OAAO,SAAS,OAAO,UACtB,OAAO,SAAS,YAAY,OAAO,SAAS,cAC5C,OAAO,UAAU,YAAY,OAAO,UAAU,WAC/C;AAEM,UAAA,UAAU,OAAO,IAAI;AACrB,UAAA,WAAW,OAAO,KAAK;AAE7B,YAAQ,UAAU;AAAA,MAChB,KAAK;AACH,eAAO,UAAU;AAAA,MACnB,KAAK;AACH,eAAO,WAAW;AAAA,MACpB,KAAK;AACH,eAAO,UAAU;AAAA,MACnB,KAAK;AACH,eAAO,WAAW;AAAA,IAAA;AAAA,EACtB;AAIE,MAAA,gBAAgB,QAAQ,iBAAiB,MAAM;AAC3C,UAAA,WAAW,KAAK,QAAQ;AACxB,UAAA,YAAY,MAAM,QAAQ;AAEhC,YAAQ,UAAU;AAAA,MAChB,KAAK;AACH,eAAO,WAAW;AAAA,MACpB,KAAK;AACH,eAAO,YAAY;AAAA,MACrB,KAAK;AACH,eAAO,WAAW;AAAA,MACpB,KAAK;AACH,eAAO,YAAY;AAAA,IAAA;AAAA,EACvB;AAIE,MAAA,OAAO,SAAS,OAAO,OAAO;AAChC,YAAQ,UAAU;AAAA,MAChB,KAAK;AACH,eAAO,OAAO;AAAA,MAChB,KAAK;AACH,eAAO,QAAQ;AAAA,MACjB,KAAK;AACH,eAAO,OAAO;AAAA,MAChB,KAAK;AACH,eAAO,QAAQ;AAAA,IAAA;AAAA,EACnB;AAIF,QAAM,IAAI;AAAA,IACR,sCAAsC,OAAO,IAAI,QAAQ,OAAO,KAAK;AAAA,EACvE;AACF;AAOO,SAAS,mBAAmB,SAAyB;AAC1D,MAAI,eAAe;AACnB,MAAI,IAAI;AAED,SAAA,IAAI,QAAQ,QAAQ;AACnB,UAAA,OAAO,QAAQ,CAAC;AAGtB,QAAI,SAAS,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AAE3B,sBAAA,QAAQ,IAAI,CAAC;AACxB,WAAA;AACL;AAAA,IAAA;AAIF,YAAQ,MAAM;AAAA,MACZ,KAAK;AAEa,wBAAA;AAChB;AAAA,MACF,KAAK;AAEa,wBAAA;AAChB;AAAA;AAAA,MAEF,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAEH,wBAAgB,OAAO;AACvB;AAAA,MACF;AAEkB,wBAAA;AAAA,IAAA;AAGpB;AAAA,EAAA;AAGK,SAAA;AACT;AASO,SAAS,eACd,OACA,OACA,kBAA2B,OAClB;AAEL,MAAA,MAAM,SAAS,KAAK,GAAG;AAClB,WAAA;AAAA,EAAA;AAIL,MAAA,UAAU,QAAQ,UAAU,QAAW;AACzC,WAAO,MAAM,KAAK,CAAC,SAAS,SAAS,QAAQ,SAAS,MAAS;AAAA,EAAA;AAIjE,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,UAAU;AACnD,WAAA,MAAM,KAAK,CAAC,SAAS;AAEtB,UAAA,OAAO,SAAS,OAAO,OAAO;AAC5B,YAAA,OAAO,UAAU,YAAY,iBAAiB;AAEhD,iBAAO,MAAM,kBAAmB,KAAgB,YAAY;AAAA,QAAA;AAE9D,eAAO,SAAS;AAAA,MAAA;AAKf,WAAA,OAAO,SAAS,YAAY,OAAO,SAAS,cAC5C,OAAO,UAAU,YAAY,OAAO,UAAU,WAC/C;AAEA,eAAO,OAAO,IAAI,MAAM,OAAO,KAAK;AAAA,MAAA;AAG/B,aAAA;AAAA,IAAA,CACR;AAAA,EAAA;AAIC,MAAA,OAAO,UAAU,UAAU;AACvB,UAAA,WAAW,KAAK,UAAU,KAAK;AAC9B,WAAA,MAAM,KAAK,CAAC,SAAS;AAC1B,UAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AACtC,eAAA,KAAK,UAAU,IAAI,MAAM;AAAA,MAAA;AAE3B,aAAA;AAAA,IAAA,CACR;AAAA,EAAA;AAII,SAAA;AACT;"}
@@ -0,0 +1,27 @@
1
+ import { Deferred } from './deferred.js';
2
+ import { PendingMutation, TransactionConfig, TransactionState } from './types.js';
3
+ export declare function createTransaction(config: TransactionConfig): Transaction;
4
+ export declare function getActiveTransaction(): Transaction | undefined;
5
+ export declare class Transaction {
6
+ id: string;
7
+ state: TransactionState;
8
+ mutationFn: import('./types.js').MutationFn;
9
+ mutations: Array<PendingMutation<any>>;
10
+ isPersisted: Deferred<Transaction>;
11
+ autoCommit: boolean;
12
+ createdAt: Date;
13
+ metadata: Record<string, unknown>;
14
+ error?: {
15
+ message: string;
16
+ error: Error;
17
+ };
18
+ constructor(config: TransactionConfig);
19
+ setState(newState: TransactionState): void;
20
+ mutate(callback: () => void): Transaction;
21
+ applyMutations(mutations: Array<PendingMutation<any>>): void;
22
+ rollback(config?: {
23
+ isSecondaryRollback?: boolean;
24
+ }): Transaction;
25
+ touchCollection(): void;
26
+ commit(): Promise<Transaction>;
27
+ }
@@ -0,0 +1,137 @@
1
+ import { createDeferred } from "./deferred.js";
2
+ function generateUUID() {
3
+ if (typeof crypto !== `undefined` && typeof crypto.randomUUID === `function`) {
4
+ return crypto.randomUUID();
5
+ }
6
+ return `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g, function(c) {
7
+ const r = Math.random() * 16 | 0;
8
+ const v = c === `x` ? r : r & 3 | 8;
9
+ return v.toString(16);
10
+ });
11
+ }
12
+ const transactions = [];
13
+ function createTransaction(config) {
14
+ if (typeof config.mutationFn === `undefined`) {
15
+ throw `mutationFn is required when creating a transaction`;
16
+ }
17
+ let transactionId = config.id;
18
+ if (!transactionId) {
19
+ transactionId = generateUUID();
20
+ }
21
+ const newTransaction = new Transaction({ ...config, id: transactionId });
22
+ transactions.push(newTransaction);
23
+ return newTransaction;
24
+ }
25
+ let transactionStack = [];
26
+ function getActiveTransaction() {
27
+ if (transactionStack.length > 0) {
28
+ return transactionStack.slice(-1)[0];
29
+ } else {
30
+ return void 0;
31
+ }
32
+ }
33
+ function registerTransaction(tx) {
34
+ transactionStack.push(tx);
35
+ }
36
+ function unregisterTransaction(tx) {
37
+ transactionStack = transactionStack.filter((t) => t.id !== tx.id);
38
+ }
39
+ class Transaction {
40
+ constructor(config) {
41
+ this.id = config.id;
42
+ this.mutationFn = config.mutationFn;
43
+ this.state = `pending`;
44
+ this.mutations = [];
45
+ this.isPersisted = createDeferred();
46
+ this.autoCommit = config.autoCommit ?? true;
47
+ this.createdAt = /* @__PURE__ */ new Date();
48
+ this.metadata = config.metadata ?? {};
49
+ }
50
+ setState(newState) {
51
+ this.state = newState;
52
+ }
53
+ mutate(callback) {
54
+ if (this.state !== `pending`) {
55
+ throw `You can no longer call .mutate() as the transaction is no longer pending`;
56
+ }
57
+ registerTransaction(this);
58
+ try {
59
+ callback();
60
+ } finally {
61
+ unregisterTransaction(this);
62
+ }
63
+ if (this.autoCommit) {
64
+ this.commit();
65
+ }
66
+ return this;
67
+ }
68
+ applyMutations(mutations) {
69
+ for (const newMutation of mutations) {
70
+ const existingIndex = this.mutations.findIndex(
71
+ (m) => m.key === newMutation.key
72
+ );
73
+ if (existingIndex >= 0) {
74
+ this.mutations[existingIndex] = newMutation;
75
+ } else {
76
+ this.mutations.push(newMutation);
77
+ }
78
+ }
79
+ }
80
+ rollback(config) {
81
+ var _a;
82
+ const isSecondaryRollback = (config == null ? void 0 : config.isSecondaryRollback) ?? false;
83
+ if (this.state === `completed`) {
84
+ throw `You can no longer call .rollback() as the transaction is already completed`;
85
+ }
86
+ this.setState(`failed`);
87
+ if (!isSecondaryRollback) {
88
+ const mutationKeys = /* @__PURE__ */ new Set();
89
+ this.mutations.forEach((m) => mutationKeys.add(m.key));
90
+ transactions.forEach(
91
+ (t) => t.state === `pending` && t.mutations.some((m) => mutationKeys.has(m.key)) && t.rollback({ isSecondaryRollback: true })
92
+ );
93
+ }
94
+ this.isPersisted.reject((_a = this.error) == null ? void 0 : _a.error);
95
+ this.touchCollection();
96
+ return this;
97
+ }
98
+ // Tell collection that something has changed with the transaction
99
+ touchCollection() {
100
+ const hasCalled = /* @__PURE__ */ new Set();
101
+ this.mutations.forEach((mutation) => {
102
+ if (!hasCalled.has(mutation.collection.id)) {
103
+ mutation.collection.transactions.setState((state) => state);
104
+ mutation.collection.commitPendingTransactions();
105
+ hasCalled.add(mutation.collection.id);
106
+ }
107
+ });
108
+ }
109
+ async commit() {
110
+ if (this.state !== `pending`) {
111
+ throw `You can no longer call .commit() as the transaction is no longer pending`;
112
+ }
113
+ this.setState(`persisting`);
114
+ if (this.mutations.length === 0) {
115
+ this.setState(`completed`);
116
+ }
117
+ try {
118
+ await this.mutationFn({ transaction: this });
119
+ this.setState(`completed`);
120
+ this.touchCollection();
121
+ this.isPersisted.resolve(this);
122
+ } catch (error) {
123
+ this.error = {
124
+ message: error instanceof Error ? error.message : String(error),
125
+ error: error instanceof Error ? error : new Error(String(error))
126
+ };
127
+ return this.rollback();
128
+ }
129
+ return this;
130
+ }
131
+ }
132
+ export {
133
+ Transaction,
134
+ createTransaction,
135
+ getActiveTransaction
136
+ };
137
+ //# sourceMappingURL=transactions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transactions.js","sources":["../../src/transactions.ts"],"sourcesContent":["import { createDeferred } from \"./deferred\"\nimport type { Deferred } from \"./deferred\"\nimport type {\n PendingMutation,\n TransactionConfig,\n TransactionState,\n} from \"./types\"\n\nfunction generateUUID() {\n // Check if crypto.randomUUID is available (modern browsers and Node.js 15+)\n if (\n typeof crypto !== `undefined` &&\n typeof crypto.randomUUID === `function`\n ) {\n return crypto.randomUUID()\n }\n\n // Fallback implementation for older environments\n return `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g, function (c) {\n const r = (Math.random() * 16) | 0\n const v = c === `x` ? r : (r & 0x3) | 0x8\n return v.toString(16)\n })\n}\n\nconst transactions: Array<Transaction> = []\n\nexport function createTransaction(config: TransactionConfig): Transaction {\n if (typeof config.mutationFn === `undefined`) {\n throw `mutationFn is required when creating a transaction`\n }\n\n let transactionId = config.id\n if (!transactionId) {\n transactionId = generateUUID()\n }\n const newTransaction = new Transaction({ ...config, id: transactionId })\n transactions.push(newTransaction)\n\n return newTransaction\n}\n\nlet transactionStack: Array<Transaction> = []\n\nexport function getActiveTransaction(): Transaction | undefined {\n if (transactionStack.length > 0) {\n return transactionStack.slice(-1)[0]\n } else {\n return undefined\n }\n}\n\nfunction registerTransaction(tx: Transaction) {\n transactionStack.push(tx)\n}\n\nfunction unregisterTransaction(tx: Transaction) {\n transactionStack = transactionStack.filter((t) => t.id !== tx.id)\n}\n\nexport class Transaction {\n public id: string\n public state: TransactionState\n public mutationFn\n public mutations: Array<PendingMutation<any>>\n public isPersisted: Deferred<Transaction>\n public autoCommit: boolean\n public createdAt: Date\n public metadata: Record<string, unknown>\n public error?: {\n message: string\n error: Error\n }\n\n constructor(config: TransactionConfig) {\n this.id = config.id!\n this.mutationFn = config.mutationFn\n this.state = `pending`\n this.mutations = []\n this.isPersisted = createDeferred()\n this.autoCommit = config.autoCommit ?? true\n this.createdAt = new Date()\n this.metadata = config.metadata ?? {}\n }\n\n setState(newState: TransactionState) {\n this.state = newState\n }\n\n mutate(callback: () => void): Transaction {\n if (this.state !== `pending`) {\n throw `You can no longer call .mutate() as the transaction is no longer pending`\n }\n\n registerTransaction(this)\n try {\n callback()\n } finally {\n unregisterTransaction(this)\n }\n\n if (this.autoCommit) {\n this.commit()\n }\n\n return this\n }\n\n applyMutations(mutations: Array<PendingMutation<any>>): void {\n for (const newMutation of mutations) {\n const existingIndex = this.mutations.findIndex(\n (m) => m.key === newMutation.key\n )\n\n if (existingIndex >= 0) {\n // Replace existing mutation\n this.mutations[existingIndex] = newMutation\n } else {\n // Insert new mutation\n this.mutations.push(newMutation)\n }\n }\n }\n\n rollback(config?: { isSecondaryRollback?: boolean }): Transaction {\n const isSecondaryRollback = config?.isSecondaryRollback ?? false\n if (this.state === `completed`) {\n throw `You can no longer call .rollback() as the transaction is already completed`\n }\n\n this.setState(`failed`)\n\n // See if there's any other transactions w/ mutations on the same keys\n // and roll them back as well.\n if (!isSecondaryRollback) {\n const mutationKeys = new Set()\n this.mutations.forEach((m) => mutationKeys.add(m.key))\n transactions.forEach(\n (t) =>\n t.state === `pending` &&\n t.mutations.some((m) => mutationKeys.has(m.key)) &&\n t.rollback({ isSecondaryRollback: true })\n )\n }\n\n // Reject the promise\n this.isPersisted.reject(this.error?.error)\n\n this.touchCollection()\n\n return this\n }\n\n // Tell collection that something has changed with the transaction\n touchCollection(): void {\n const hasCalled = new Set()\n this.mutations.forEach((mutation) => {\n if (!hasCalled.has(mutation.collection.id)) {\n mutation.collection.transactions.setState((state) => state)\n mutation.collection.commitPendingTransactions()\n hasCalled.add(mutation.collection.id)\n }\n })\n }\n\n async commit(): Promise<Transaction> {\n if (this.state !== `pending`) {\n throw `You can no longer call .commit() as the transaction is no longer pending`\n }\n\n this.setState(`persisting`)\n\n if (this.mutations.length === 0) {\n this.setState(`completed`)\n }\n\n // Run mutationFn\n try {\n await this.mutationFn({ transaction: this })\n\n this.setState(`completed`)\n this.touchCollection()\n\n this.isPersisted.resolve(this)\n } catch (error) {\n // Update transaction with error information\n this.error = {\n message: error instanceof Error ? error.message : String(error),\n error: error instanceof Error ? error : new Error(String(error)),\n }\n\n // rollback the transaction\n return this.rollback()\n }\n\n return this\n }\n}\n"],"names":[],"mappings":"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"}