@routier/core 0.0.1-alpha.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 (168) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/package.json +91 -0
  3. package/rspack.config.mjs +65 -0
  4. package/src/assertions/index.ts +37 -0
  5. package/src/codegen/SlotPath.ts +20 -0
  6. package/src/codegen/blocks.ts +578 -0
  7. package/src/codegen/handlers/CloneHandlerBuilder.ts +13 -0
  8. package/src/codegen/handlers/CompareHandlerBuilder.ts +13 -0
  9. package/src/codegen/handlers/DeserializeHandlerBuilder.ts +20 -0
  10. package/src/codegen/handlers/EnableChangeTrackingHandlerBuilder.ts +13 -0
  11. package/src/codegen/handlers/EnrichmentHandlerBuilder.ts +28 -0
  12. package/src/codegen/handlers/FreezeHandlerBuilder.ts +13 -0
  13. package/src/codegen/handlers/HashHandlerBuilder.ts +25 -0
  14. package/src/codegen/handlers/HashTypeHandlerBuilder.ts +11 -0
  15. package/src/codegen/handlers/IdSelectorHandlerBuilder.ts +10 -0
  16. package/src/codegen/handlers/MergeHandlerBuilder.ts +19 -0
  17. package/src/codegen/handlers/PrepareHandlerBuilder.ts +23 -0
  18. package/src/codegen/handlers/SerializeHandlerBuilder.ts +15 -0
  19. package/src/codegen/handlers/StripHandlerBuilder.ts +18 -0
  20. package/src/codegen/handlers/clone/CloneObjectHandler.ts +29 -0
  21. package/src/codegen/handlers/clone/CloneValueHandler.ts +33 -0
  22. package/src/codegen/handlers/compare/CompareObjectHandler.ts +15 -0
  23. package/src/codegen/handlers/compare/CompareValueHandler.ts +27 -0
  24. package/src/codegen/handlers/deserialize/DeserializeComputedValueHandler.ts +16 -0
  25. package/src/codegen/handlers/deserialize/DeserializeDateHandler.ts +45 -0
  26. package/src/codegen/handlers/deserialize/DeserializeFunctionHandler.ts +16 -0
  27. package/src/codegen/handlers/deserialize/DeserializeObjectHandler.ts +29 -0
  28. package/src/codegen/handlers/deserialize/DeserializeValueHandler.ts +33 -0
  29. package/src/codegen/handlers/enableChangeTracking/EnableChangeTrackingObjectHandler.ts +20 -0
  30. package/src/codegen/handlers/enableChangeTracking/EnableChangeTrackingPrimitiveValueHandler.ts +15 -0
  31. package/src/codegen/handlers/enrichment/EnrichmentComputedValueHandler.ts +37 -0
  32. package/src/codegen/handlers/enrichment/EnrichmentDefaultFunctionHandler.ts +50 -0
  33. package/src/codegen/handlers/enrichment/EnrichmentDefaultValueHandler.ts +18 -0
  34. package/src/codegen/handlers/enrichment/EnrichmentFunctionHandler.ts +38 -0
  35. package/src/codegen/handlers/enrichment/EnrichmentNullableObjectHandler.ts +44 -0
  36. package/src/codegen/handlers/enrichment/EnrichmentObjectHandler.ts +29 -0
  37. package/src/codegen/handlers/enrichment/EnrichmentObjectIdentityHandler.ts +16 -0
  38. package/src/codegen/handlers/enrichment/EnrichmentPrimitiveHandler.ts +13 -0
  39. package/src/codegen/handlers/enrichment/EnrichmentPrimitiveIdentityHandler.ts +21 -0
  40. package/src/codegen/handlers/freeze/FreezeObjectHandler.ts +20 -0
  41. package/src/codegen/handlers/freeze/FreezePrimitiveValueHandler.ts +15 -0
  42. package/src/codegen/handlers/hash/HashComputedValueHandler.ts +15 -0
  43. package/src/codegen/handlers/hash/HashDateHandler.ts +26 -0
  44. package/src/codegen/handlers/hash/HashFunctionHandler.ts +16 -0
  45. package/src/codegen/handlers/hash/HashIdentityHandler.ts +15 -0
  46. package/src/codegen/handlers/hash/HashKeyHandler.ts +26 -0
  47. package/src/codegen/handlers/hash/HashValueHandler.ts +26 -0
  48. package/src/codegen/handlers/hashType/HashTypeValueHandler.ts +20 -0
  49. package/src/codegen/handlers/idSelector/IdSelectorValueHandler.ts +28 -0
  50. package/src/codegen/handlers/index.ts +13 -0
  51. package/src/codegen/handlers/merge/MergeComputedValueHandler.ts +37 -0
  52. package/src/codegen/handlers/merge/MergeDefaultFunctionHandler.ts +40 -0
  53. package/src/codegen/handlers/merge/MergeDefaultValueHandler.ts +32 -0
  54. package/src/codegen/handlers/merge/MergeFunctionHandler.ts +16 -0
  55. package/src/codegen/handlers/merge/MergePrimitiveHandler.ts +21 -0
  56. package/src/codegen/handlers/prepare/PrepareComputedValueHandler.ts +16 -0
  57. package/src/codegen/handlers/prepare/PrepareFunctionHandler.ts +16 -0
  58. package/src/codegen/handlers/prepare/PrepareIdentityHandler.ts +21 -0
  59. package/src/codegen/handlers/prepare/PrepareKeyHandler.ts +21 -0
  60. package/src/codegen/handlers/prepare/PrepareObjectHandler.ts +38 -0
  61. package/src/codegen/handlers/prepare/PrepareValueHandler.ts +36 -0
  62. package/src/codegen/handlers/serialize/SerializeDateHandler.ts +45 -0
  63. package/src/codegen/handlers/serialize/SerializeObjectHandler.ts +28 -0
  64. package/src/codegen/handlers/serialize/SerializeValueHandler.ts +33 -0
  65. package/src/codegen/handlers/strip/StripIdentityHandler.ts +15 -0
  66. package/src/codegen/handlers/strip/StripKeyHandler.ts +15 -0
  67. package/src/codegen/handlers/strip/StripObjectHandler.ts +35 -0
  68. package/src/codegen/handlers/strip/StripValueHandler.ts +36 -0
  69. package/src/codegen/handlers/types.ts +119 -0
  70. package/src/codegen/index.ts +2 -0
  71. package/src/codegen/types.ts +2 -0
  72. package/src/codegen/utils.ts +74 -0
  73. package/src/collections/Changes.test.ts +337 -0
  74. package/src/collections/Changes.ts +177 -0
  75. package/src/collections/IdSet.ts +28 -0
  76. package/src/collections/MemoryDataCollection.test.ts +424 -0
  77. package/src/collections/MemoryDataCollection.ts +134 -0
  78. package/src/collections/SchemaCollection.ts +22 -0
  79. package/src/collections/TagCollection.test.ts +443 -0
  80. package/src/collections/TagCollection.ts +62 -0
  81. package/src/collections/index.ts +5 -0
  82. package/src/errors/SchemaError.ts +6 -0
  83. package/src/errors/index.ts +1 -0
  84. package/src/expressions/index.ts +3 -0
  85. package/src/expressions/parser.test.ts +913 -0
  86. package/src/expressions/parser.ts +661 -0
  87. package/src/expressions/types.ts +184 -0
  88. package/src/expressions/utils.test.ts +346 -0
  89. package/src/expressions/utils.ts +59 -0
  90. package/src/index.ts +12 -0
  91. package/src/performance/index.ts +26 -0
  92. package/src/pipeline/SyncronousQueue.test.ts +269 -0
  93. package/src/pipeline/SyncronousQueue.ts +30 -0
  94. package/src/pipeline/TrampolinePipeline.test.ts +374 -0
  95. package/src/pipeline/TrampolinePipeline.ts +437 -0
  96. package/src/pipeline/index.ts +2 -0
  97. package/src/plugins/EphemeralDataPlugin.ts +132 -0
  98. package/src/plugins/capabilities/DbPluginCapability.ts +111 -0
  99. package/src/plugins/capabilities/index.ts +2 -0
  100. package/src/plugins/capabilities/logging/DbPluginLoggingCapability.ts +261 -0
  101. package/src/plugins/capabilities/logging/index.ts +1 -0
  102. package/src/plugins/index.ts +6 -0
  103. package/src/plugins/query/Query.ts +67 -0
  104. package/src/plugins/query/QueryOptionsCollection.test.ts +86 -0
  105. package/src/plugins/query/QueryOptionsCollection.ts +154 -0
  106. package/src/plugins/query/index.ts +3 -0
  107. package/src/plugins/query/types.ts +46 -0
  108. package/src/plugins/replication/OptimisticReplicationDbPlugin.ts +202 -0
  109. package/src/plugins/replication/ReplicationDbPlugin.ts +137 -0
  110. package/src/plugins/replication/index.ts +3 -0
  111. package/src/plugins/replication/types.ts +5 -0
  112. package/src/plugins/translators/DataTranslator.ts +46 -0
  113. package/src/plugins/translators/JsonTranslator.test.ts +618 -0
  114. package/src/plugins/translators/JsonTranslator.ts +211 -0
  115. package/src/plugins/translators/SqlTranslator.ts +45 -0
  116. package/src/plugins/translators/index.ts +3 -0
  117. package/src/plugins/types.ts +114 -0
  118. package/src/results/Result.ts +91 -0
  119. package/src/results/index.ts +3 -0
  120. package/src/results/types.ts +17 -0
  121. package/src/results/utils.ts +15 -0
  122. package/src/schema/PropertyInfo.test.ts +479 -0
  123. package/src/schema/PropertyInfo.ts +374 -0
  124. package/src/schema/SchemaDefinition.ts +626 -0
  125. package/src/schema/builder.ts +18 -0
  126. package/src/schema/index.ts +7 -0
  127. package/src/schema/property/base/SchemaBase.ts +49 -0
  128. package/src/schema/property/base/index.ts +1 -0
  129. package/src/schema/property/modifiers/SchemaDefault.ts +29 -0
  130. package/src/schema/property/modifiers/SchemaDeserialize.ts +23 -0
  131. package/src/schema/property/modifiers/SchemaDistinct.ts +14 -0
  132. package/src/schema/property/modifiers/SchemaFrom.ts +54 -0
  133. package/src/schema/property/modifiers/SchemaIdentity.ts +13 -0
  134. package/src/schema/property/modifiers/SchemaIndex.ts +60 -0
  135. package/src/schema/property/modifiers/SchemaKey.ts +28 -0
  136. package/src/schema/property/modifiers/SchemaNullable.ts +18 -0
  137. package/src/schema/property/modifiers/SchemaOptional.ts +19 -0
  138. package/src/schema/property/modifiers/SchemaReadonly.ts +34 -0
  139. package/src/schema/property/modifiers/SchemaSerialize.ts +18 -0
  140. package/src/schema/property/modifiers/SchemaTracked.ts +14 -0
  141. package/src/schema/property/modifiers/index.ts +12 -0
  142. package/src/schema/property/types/SchemaArray.ts +50 -0
  143. package/src/schema/property/types/SchemaBoolean.ts +59 -0
  144. package/src/schema/property/types/SchemaDate.ts +58 -0
  145. package/src/schema/property/types/SchemaNumber.ts +69 -0
  146. package/src/schema/property/types/SchemaObject.ts +44 -0
  147. package/src/schema/property/types/SchemaString.ts +69 -0
  148. package/src/schema/property/types/index.ts +6 -0
  149. package/src/schema/table/SchemaComputed.ts +20 -0
  150. package/src/schema/table/SchemaFunction.ts +15 -0
  151. package/src/schema/table/index.ts +2 -0
  152. package/src/schema/types.ts +238 -0
  153. package/src/types/index.ts +5 -0
  154. package/src/utilities/arrays.test.ts +312 -0
  155. package/src/utilities/arrays.ts +12 -0
  156. package/src/utilities/dates.test.ts +388 -0
  157. package/src/utilities/dates.ts +11 -0
  158. package/src/utilities/dbPluginEventUtils.ts +44 -0
  159. package/src/utilities/index.ts +10 -0
  160. package/src/utilities/objects.ts +7 -0
  161. package/src/utilities/queryOptionsCollection.ts +16 -0
  162. package/src/utilities/replication.ts +23 -0
  163. package/src/utilities/runtime.ts +3 -0
  164. package/src/utilities/strings.ts +18 -0
  165. package/src/utilities/types.ts +1 -0
  166. package/src/utilities/uuid.ts +56 -0
  167. package/tsconfig.json +28 -0
  168. package/vitest.config.ts +11 -0
@@ -0,0 +1,626 @@
1
+ import { SchemaFunction } from './table/SchemaFunction';
2
+ import { SchemaComputed } from './table/SchemaComputed';
3
+ import { SchemaBase } from "./property/base/SchemaBase";
4
+ import { PropertyInfo } from './PropertyInfo';
5
+ import { CodeBuilder } from '../codegen';
6
+ import { EnrichmentHandlerBuilder } from '../codegen/handlers/EnrichmentHandlerBuilder';
7
+ import { MergeHandlerBuilder } from '../codegen/handlers/MergeHandlerBuilder';
8
+ import { PrepareHandlerBuilder } from '../codegen/handlers/PrepareHandlerBuilder';
9
+ import { StripHandlerBuilder } from '../codegen/handlers/StripHandlerBuilder';
10
+ import { CloneHandlerBuilder } from '../codegen/handlers/CloneHandlerBuilder';
11
+ import { CompareHandlerBuilder } from '../codegen/handlers/CompareHandlerBuilder';
12
+ import { DeserializeHandlerBuilder } from '../codegen/handlers/DeserializeHandlerBuilder';
13
+ import { HashTypeHandlerBuilder } from '../codegen/handlers/HashTypeHandlerBuilder';
14
+ import { IdSelectorHandlerBuilder } from '../codegen/handlers/IdSelectorHandlerBuilder';
15
+ import { HashHandlerBuilder } from '../codegen/handlers/HashHandlerBuilder';
16
+ import { EnableChangeTrackingHandlerBuilder } from '../codegen/handlers/EnableChangeTrackingHandlerBuilder';
17
+ import { FreezeHandlerBuilder } from '../codegen/handlers/FreezeHandlerBuilder';
18
+ import { SchemaError } from '../errors/SchemaError';
19
+ import { SerializeHandlerBuilder } from "../codegen/handlers/SerializeHandlerBuilder";
20
+ import { uuid, hash } from "../utilities";
21
+ import { CompiledSchema, GetHashTypeFunction, HashFunction, HashType, ICollectionSubscription, IdType, Index, InferCreateType, InferType, SchemaTypes, SubscriptionChanges } from './types';
22
+ import { DeepPartial } from '../types';
23
+
24
+ type UniDirectionalSubscriptionPayload<T extends {}> = {
25
+ id: string;
26
+ changes: SubscriptionChanges<T>;
27
+ }
28
+
29
+ // This is intented to run the "OnMessage" event whenever we "send". For context
30
+ // when we make a change, we query to see if we have any changes. We need to run a fetch because we could have
31
+ // many db sets
32
+ class CollectionSubscription<T extends {}> implements ICollectionSubscription<T> {
33
+
34
+ private _channel;
35
+ private _id = uuid();
36
+ private _callback: ((changes: SubscriptionChanges<T>) => void) | null = null;
37
+
38
+ constructor(id: number, signal?: AbortSignal) {
39
+ // The id is the computed id from the collection. We need a static id so we can communicate across different
40
+ // instances of a data store
41
+ this._channel = new BroadcastChannel(`__routier-unidirectional-subscription-channel-${id}`);
42
+ this._channel.onmessage = (event: any) => {
43
+ const message = event.data as UniDirectionalSubscriptionPayload<T>;
44
+ if (message.id === this._id) {
45
+ return;
46
+ }
47
+
48
+ if (this._callback != null) {
49
+ this._callback(message.changes);
50
+ }
51
+ };
52
+
53
+ if (signal) {
54
+ signal.addEventListener("abort", () => {
55
+ this[Symbol.dispose]();
56
+ }, { once: true });
57
+ }
58
+ }
59
+
60
+ send(changes: SubscriptionChanges<T>) {
61
+ const message: UniDirectionalSubscriptionPayload<T> = {
62
+ id: this._id,
63
+ changes
64
+ }
65
+ this._channel.postMessage(message)
66
+ }
67
+
68
+ onMessage(callback: (changes: SubscriptionChanges<T>) => void) {
69
+ this._callback = callback;
70
+ }
71
+
72
+ dispose() {
73
+ this[Symbol.dispose]();
74
+ }
75
+
76
+ [Symbol.dispose](): void {
77
+ this._channel.onmessage = null;
78
+ this._channel.close();
79
+ this._channel = null;
80
+ }
81
+ }
82
+
83
+ export class SchemaDefinition<T extends {}> extends SchemaBase<T, any> {
84
+
85
+ instance: T;
86
+ type = SchemaTypes.Definition;
87
+ collectionName: string;
88
+
89
+ constructor(collectionName: string, schema: T) {
90
+ super();
91
+ this.collectionName = collectionName;
92
+ this.instance = schema;
93
+ this.isNullable = false;
94
+ this.isOptional = false;
95
+ }
96
+
97
+ modify<R>(builder: (d: {
98
+ function: <UU, I = never>(fn: (entity: InferType<CompiledSchema<T>>, collectionName: string, injected: I) => UU, injected?: I) => SchemaFunction<UU, I, "unmapped">;
99
+ computed: <UU, I = never>(fn: (entity: InferType<CompiledSchema<T>>, collectionName: string, injected: I) => UU, injected?: I) => SchemaComputed<UU, I, "unmapped">;
100
+ }) => R) {
101
+
102
+ const b = {
103
+ function: <UU, I = never>(fn: (entity: InferType<CompiledSchema<T>>, collectionName: string, injected?: I) => UU, injected?: I) => new SchemaFunction<UU, I, "unmapped">(fn as any, injected),
104
+ computed: <UU, I = never>(fn: (entity: InferType<CompiledSchema<T>>, collectionName: string, injected?: I) => UU, injected?: I) => new SchemaComputed<UU, I, "unmapped">(fn as any, injected)
105
+ }
106
+
107
+ const r = builder(b)
108
+
109
+ return new SchemaDefinition<R & T>(this.collectionName, { ...this.instance, ...r });
110
+ }
111
+
112
+ private _iterate(
113
+ instance: SchemaBase<any, any>,
114
+ callback: (property: PropertyInfo<any>) => void
115
+ ) {
116
+ const explore: {
117
+ path: string | null;
118
+ instance:
119
+ | SchemaBase<any, any>
120
+ | { [key: string]: SchemaBase<any, any> };
121
+ parents: {
122
+ schema: { [key: string]: SchemaBase<any, any> };
123
+ propertyInfo: PropertyInfo<any>;
124
+ }[];
125
+ propertyInfo: PropertyInfo<any> | null;
126
+ }[] = [
127
+ { path: null, instance, parents: [], propertyInfo: null }
128
+ ];
129
+ const properties: PropertyInfo<any>[] = [];
130
+
131
+ for (let i = 0; i < explore.length; i++) {
132
+ const item = explore[i];
133
+
134
+ if (item.instance.type === SchemaTypes.Definition) {
135
+ explore.push({
136
+ path: null,
137
+ instance: item.instance.instance,
138
+ parents: [],
139
+ propertyInfo: null
140
+ });
141
+ continue;
142
+ }
143
+
144
+ const instanceObj = item.instance as {
145
+ [key: string]: SchemaBase<any, any>;
146
+ };
147
+ for (const key in instanceObj) {
148
+ const propertyInstance = instanceObj[key] as SchemaBase<any, any>;
149
+ const previousParent =
150
+ item.parents.length === 0
151
+ ? null
152
+ : item.parents[item.parents.length - 1].propertyInfo;
153
+
154
+ if (propertyInstance.type === SchemaTypes.Object) {
155
+ const propertyInfo = new PropertyInfo<any>(
156
+ propertyInstance,
157
+ key,
158
+ previousParent
159
+ );
160
+ explore.push({
161
+ path: [item.path, key].filter((w) => w != null).join("."),
162
+ instance: propertyInstance.instance,
163
+ parents: [
164
+ ...item.parents,
165
+ { schema: instanceObj, propertyInfo: propertyInfo }
166
+ ],
167
+ propertyInfo
168
+ });
169
+
170
+ if (previousParent === null) {
171
+ properties.push(propertyInfo);
172
+ continue;
173
+ }
174
+
175
+ previousParent.children.push(propertyInfo);
176
+ continue;
177
+ }
178
+
179
+ const childPrimitivePropertyInfo = new PropertyInfo<any>(
180
+ (item.instance as any)[key],
181
+ key,
182
+ previousParent
183
+ );
184
+ if (previousParent === null) {
185
+ properties.push(childPrimitivePropertyInfo);
186
+ continue;
187
+ }
188
+
189
+ previousParent.children.push(childPrimitivePropertyInfo);
190
+ }
191
+ }
192
+
193
+ // Recursively trigger callbacks on properties and their children
194
+ function recursiveCallback(prop: PropertyInfo<any>) {
195
+ callback(prop);
196
+ for (const child of prop.children) {
197
+ recursiveCallback(child);
198
+ }
199
+ }
200
+
201
+ for (const prop of properties) {
202
+ recursiveCallback(prop);
203
+ }
204
+ }
205
+
206
+ private createChangeTracker() {
207
+
208
+ const DIRTY_ENTITY_MARKER: string = "isDirty";
209
+ const CHANGES_ENTITY_KEY: string = "changes";
210
+ const ORIGINAL_ENTITY_KEY: string = "original";
211
+ const PAUSED_ENTITY_KEY: string = "isPaused";
212
+ const TRACKING_KEY: string = "__tracking__";
213
+ const PROXY_MARKER: string = "__isProxy__";
214
+
215
+ return <TEntity extends {}>(entity: TEntity, path?: string, parent?: TEntity) => {
216
+
217
+ const proxyHandler: ProxyHandler<TEntity> = {
218
+ set: (entity, property, value) => {
219
+ const indexableEntity: { [key: string]: any } = entity;
220
+ const key = String(property);
221
+ const originalValue = indexableEntity[key];
222
+
223
+ // if values are the same, do nothing
224
+ if (originalValue === value) {
225
+ return true;
226
+ }
227
+
228
+ const resolvedParent: { [key: string]: any } = parent ?? entity;
229
+
230
+ if (resolvedParent[TRACKING_KEY] == null) {
231
+ resolvedParent[TRACKING_KEY] = {
232
+ [CHANGES_ENTITY_KEY]: {},
233
+ [DIRTY_ENTITY_MARKER]: false,
234
+ [ORIGINAL_ENTITY_KEY]: {},
235
+ [PAUSED_ENTITY_KEY]: false
236
+ }
237
+ }
238
+
239
+ if (key == TRACKING_KEY) {
240
+ return true;
241
+ }
242
+
243
+ if (resolvedParent[TRACKING_KEY] != null && resolvedParent[TRACKING_KEY][PAUSED_ENTITY_KEY] === true) {
244
+ Reflect.set(indexableEntity, property, value);
245
+ return true;
246
+ }
247
+
248
+ const resolvedPath = path == null ? key : `${path}.${key}`;
249
+ const changes = resolvedParent[TRACKING_KEY];
250
+
251
+ if (changes[CHANGES_ENTITY_KEY][resolvedPath] != null) {
252
+
253
+ if (changes[ORIGINAL_ENTITY_KEY][resolvedPath] === value) {
254
+ // we are changing the value back to the original value, remove the change
255
+ delete changes[ORIGINAL_ENTITY_KEY][resolvedPath];
256
+ delete changes[CHANGES_ENTITY_KEY][resolvedPath];
257
+ } else {
258
+ // track the change
259
+ changes[CHANGES_ENTITY_KEY][resolvedPath] = value;
260
+ }
261
+
262
+ } else if (changes[CHANGES_ENTITY_KEY][resolvedPath] == null) {
263
+ // don't keep updating, keep the original value
264
+ changes[CHANGES_ENTITY_KEY][resolvedPath] = value;
265
+ changes[ORIGINAL_ENTITY_KEY][resolvedPath] = originalValue;
266
+ }
267
+
268
+ const isDirty = Object.keys(changes[ORIGINAL_ENTITY_KEY]).length > 0;
269
+ changes[DIRTY_ENTITY_MARKER] = isDirty;
270
+
271
+ Reflect.set(indexableEntity, property, value);
272
+
273
+ return true;
274
+ },
275
+ get: (target, property, receiver) => {
276
+
277
+ if (property === PROXY_MARKER) {
278
+ return true;
279
+ }
280
+
281
+ return Reflect.get(target, property, receiver);
282
+ }
283
+ }
284
+
285
+ return new Proxy(entity, proxyHandler) as TEntity
286
+ }
287
+ }
288
+
289
+ compile(): CompiledSchema<T> {
290
+
291
+ try {
292
+ const schema = this;
293
+ const properties: PropertyInfo<T>[] = [];
294
+ const propertyMap: Map<string, PropertyInfo<T>> = new Map<string, PropertyInfo<T>>();
295
+
296
+ const enrichmentHandlerBuilder = new EnrichmentHandlerBuilder();
297
+ const mergeHandlerFactory = new MergeHandlerBuilder();
298
+ const prepareHandlerBuilder = new PrepareHandlerBuilder();
299
+ const stripHandlerBuilder = new StripHandlerBuilder();
300
+ const cloneHandlerBuilder = new CloneHandlerBuilder();
301
+ const compareHandlerBuilder = new CompareHandlerBuilder();
302
+ const deserializeHandlerBuilder = new DeserializeHandlerBuilder();
303
+ const hashTypeHandlerBuilder = new HashTypeHandlerBuilder();
304
+ const idSelectorHandlerBuilder = new IdSelectorHandlerBuilder();
305
+ const hashHandlerBuilder = new HashHandlerBuilder();
306
+ const enableChangeTrackingHandlerBuilder = new EnableChangeTrackingHandlerBuilder();
307
+ const freezeHandlerBuilder = new FreezeHandlerBuilder();
308
+ const serializeHandlerBuilder = new SerializeHandlerBuilder();
309
+
310
+ const enricher = enrichmentHandlerBuilder.build();
311
+ const merge = mergeHandlerFactory.build();
312
+ const prepare = prepareHandlerBuilder.build();
313
+ const strip = stripHandlerBuilder.build();
314
+ const clone = cloneHandlerBuilder.build();
315
+ const compare = compareHandlerBuilder.build();
316
+ const deserialize = deserializeHandlerBuilder.build();
317
+ const hashTypeHandler = hashTypeHandlerBuilder.build();
318
+ const idSelectorHandler = idSelectorHandlerBuilder.build();
319
+ const hashHandler = hashHandlerBuilder.build();
320
+ const enableChangeTrackingHandler = enableChangeTrackingHandlerBuilder.build();
321
+ const freezeHandler = freezeHandlerBuilder.build();
322
+ const serializeHandler = serializeHandlerBuilder.build();
323
+
324
+ const changeTrackingCodeBuilder = new CodeBuilder();
325
+ changeTrackingCodeBuilder.raw(`function ${this.createChangeTracker.toString()}`);
326
+ changeTrackingCodeBuilder.slot("declarations").variable("enableChangeTracking").value('createChangeTracker()');
327
+ changeTrackingCodeBuilder.slot("assignment");
328
+ changeTrackingCodeBuilder.slot("return").raw('\treturn enableChangeTracking(entity);');
329
+
330
+ const freezeCodeBuilder = new CodeBuilder();
331
+ freezeCodeBuilder.slot("assignment");
332
+ freezeCodeBuilder.slot("return").raw('\treturn Object.freeze(entity);');
333
+
334
+ const enricherCodeBuilder = new CodeBuilder();
335
+ const enricherFunctionRoot = enricherCodeBuilder.factory("factory", { name: "factory" }).parameters({ name: "collectionName", value: this.collectionName });
336
+ const enricherFunctionBody = enricherFunctionRoot.function(undefined, { name: "function" }).parameters("entity", "changeTrackingType").return();
337
+
338
+ enricherFunctionBody.raw(`function ${this.createChangeTracker.toString()}`);
339
+ enricherFunctionBody.variable("enableChangeTracking").value('changeTrackingType === "proxy" ? createChangeTracker() : e => e');
340
+
341
+ enricherFunctionBody.slot("enriched");
342
+ enricherFunctionBody.slot("declarations");
343
+ enricherFunctionBody.slot("assignment");
344
+ enricherFunctionBody.slot("ifs");
345
+ enricherFunctionBody.slot("tracking").if('changeTrackingType === "immutable"', { name: "freeze" });
346
+ enricherFunctionBody.raw('\treturn enableChangeTracking(enriched);');
347
+
348
+ const mergeCodeBuilder = new CodeBuilder();
349
+
350
+ const mergeFunctionRoot = mergeCodeBuilder.factory("factory", { name: "factory" }).parameters({ name: "collectionName", value: this.collectionName });
351
+ const mergeFunctionBody = mergeFunctionRoot.function(undefined, { name: "function" }).parameters("destination", "source").return();
352
+
353
+ const pauseFunctionBody = mergeFunctionBody.function("pause")
354
+ .appendBody("// initiate change tracking if needed");
355
+
356
+ pauseFunctionBody.if("destination.__tracking__ == null")
357
+ .appendBody("destination.__tracking__ = {};");
358
+
359
+ pauseFunctionBody.appendBody("destination.__tracking__.isPaused = true;");
360
+
361
+ mergeFunctionBody.function("unpause")
362
+ .appendBody("// unpause change tracking if needed")
363
+ .if("destination.__tracking__ != null")
364
+ .appendBody("destination.__tracking__.isPaused = false;");
365
+
366
+ mergeFunctionBody.slot("header").raw(`pause()`);
367
+ mergeFunctionBody.slot("assignments");
368
+ mergeFunctionBody.slot("ifs");
369
+ mergeFunctionBody.slot("return").raw(`
370
+ unpause();
371
+
372
+ return destination;`);
373
+
374
+ const prepareCodeBuilder = new CodeBuilder();
375
+ prepareCodeBuilder.slot("result");
376
+ prepareCodeBuilder.slot("assignments");
377
+ prepareCodeBuilder.slot("return").raw(` return result;`);
378
+
379
+ const stripCodeBuilder = new CodeBuilder();
380
+ stripCodeBuilder.slot("result");
381
+ stripCodeBuilder.slot("return").raw(` return result;`);
382
+
383
+ const cloneCodeBuilder = new CodeBuilder();
384
+ cloneCodeBuilder.slot("result");
385
+ cloneCodeBuilder.slot("return").raw(` return result;`);
386
+
387
+ const compareCodeBuilder = new CodeBuilder();
388
+ compareCodeBuilder.slot("result");
389
+ compareCodeBuilder.slot("return").raw(` return result;`);
390
+
391
+ const deserializeCodeBuilder = new CodeBuilder();
392
+ deserializeCodeBuilder.slot("result");
393
+ deserializeCodeBuilder.slot("if");
394
+ deserializeCodeBuilder.slot("return").raw(` return result;`);
395
+
396
+ const serializeCodeBuilder = new CodeBuilder();
397
+ serializeCodeBuilder.slot("result");
398
+ serializeCodeBuilder.slot("if");
399
+ serializeCodeBuilder.slot("return").raw(` return result;`);
400
+
401
+ const idSelectorCodeBuilder = new CodeBuilder();
402
+ idSelectorCodeBuilder.slot("result");
403
+ idSelectorCodeBuilder.slot("return").raw(` return result;`);
404
+
405
+ const hashTypeCodeBuilder = new CodeBuilder();
406
+ hashTypeCodeBuilder.slot("ifs");
407
+ hashTypeCodeBuilder.slot("return").raw(` return "Ids";`);
408
+
409
+ const hashCodeBuilder = new CodeBuilder();
410
+ hashCodeBuilder.slot("functions").raw(`
411
+ function stringifyDate(d) {
412
+
413
+ if (d == null) {
414
+ return "";
415
+ }
416
+
417
+ if (typeof d === "string") {
418
+ return d;
419
+ }
420
+
421
+ if (d.toISOString != null) {
422
+ return d.toISOString();
423
+ }
424
+
425
+ return d.toString();
426
+ }
427
+ `);
428
+ const hashCodeBuilderIfBlock = hashCodeBuilder.if(`type === "Ids"`, { name: "hash-id-if" });
429
+ hashCodeBuilderIfBlock.slot("if-body");
430
+ hashCodeBuilderIfBlock.appendBody("return result");
431
+ hashCodeBuilder.slot("hash-object-return");
432
+ hashCodeBuilder.raw(` return result;`)
433
+
434
+ const idProperties: PropertyInfo<any>[] = [];
435
+ const allPropertyNamesAndPaths: string[] = [];
436
+ let hashType: HashType = HashType.Ids;
437
+ let hasIdentities = false;
438
+ let hasIdentityKeys = false;
439
+
440
+ this._iterate(schema, (property) => {
441
+
442
+ properties.push(property);
443
+ propertyMap.set(property.id, property);
444
+ allPropertyNamesAndPaths.push(property.getSelectrorPath({ parent: "entity" }));
445
+
446
+ if (property.isIdentity === true) {
447
+ hasIdentities = true;
448
+ }
449
+
450
+ if (property.isKey === true) {
451
+ idProperties.push(property);
452
+ }
453
+
454
+ if (property.isKey === true && property.isIdentity === true) {
455
+ hasIdentityKeys = true;
456
+ }
457
+
458
+ enricher.handle(property, enricherCodeBuilder);
459
+ merge.handle(property, mergeCodeBuilder);
460
+ prepare.handle(property, prepareCodeBuilder);
461
+ strip.handle(property, stripCodeBuilder);
462
+ clone.handle(property, cloneCodeBuilder);
463
+ compare.handle(property, compareCodeBuilder);
464
+ deserialize.handle(property, deserializeCodeBuilder);
465
+ serializeHandler.handle(property, serializeCodeBuilder)
466
+ hashTypeHandler.handle(property, hashTypeCodeBuilder);
467
+ idSelectorHandler.handle(property, idSelectorCodeBuilder);
468
+ hashHandler.handle(property, hashCodeBuilder);
469
+ enableChangeTrackingHandler.handle(property, changeTrackingCodeBuilder);
470
+ freezeHandler.handle(property, freezeCodeBuilder);
471
+ });
472
+
473
+ if (idProperties.length === 0) {
474
+ throw new Error(`Schema must have a key. Use .key() to mark a property as a key. Collection Name: ${this.collectionName}`)
475
+ }
476
+
477
+ const enrichParams = enricherFunctionRoot.getParameters()
478
+ const mergeParams = mergeFunctionRoot.getParameters()
479
+ const enrichGenerator = Function(`return ${enricherCodeBuilder.toString()}`);
480
+ const mergeGenerator = Function(`return ${mergeCodeBuilder.toString()}`);
481
+
482
+ const getIdsFunction = Function("entity", idSelectorCodeBuilder.toString()) as (entity: InferType<T>) => [IdType];
483
+ const getHashTypeFunction = Function("entity", hashTypeCodeBuilder.toString()) as GetHashTypeFunction<T>;
484
+ const prepareFunction = Function("entity", prepareCodeBuilder.toString()) as (entity: InferCreateType<T>) => InferCreateType<T>;
485
+ const cloneFunction = Function("entity", cloneCodeBuilder.toString()) as (entity: InferType<T>) => InferType<T>;
486
+ const deserializeFunction = Function("entity", deserializeCodeBuilder.toString()) as (entity: InferType<T>) => InferType<T>;
487
+ const serializeFunction = Function("entity", serializeCodeBuilder.toString()) as (entity: InferType<T>) => InferType<T>;
488
+ const compareFunction = Function("a", "b", compareCodeBuilder.toString()) as (a: InferType<T>, fromDb: InferType<T>) => boolean;;
489
+ const stripFunction = Function("entity", stripCodeBuilder.toString()) as (entity: InferType<T>) => InferType<T>;
490
+ const hashFunction = Function("entity", "type", hashCodeBuilder.toString()) as HashFunction<T>;
491
+ const enableChangeTrackingFunction = Function("entity", changeTrackingCodeBuilder.toString()) as (entity: InferType<T>) => InferType<T>;
492
+ const freezeFunction = Function("entity", freezeCodeBuilder.toString()) as (entity: InferType<T>) => InferType<T>;
493
+
494
+ const enricherFactoryFunction = enrichGenerator();
495
+ const mergeFactoryFunction = mergeGenerator();
496
+ const enricherFunction = enricherFactoryFunction(...enrichParams.map(w => w.value));
497
+ const mergeFunction = mergeFactoryFunction(...mergeParams.map(w => w.value));
498
+
499
+ const idPropertyNames = idProperties.map(w => w.name);
500
+ const getId = (entity: InferType<T>) => {
501
+ if (idPropertyNames.length > 1) {
502
+ return hashFunction(entity, HashType.Ids) as IdType;
503
+ }
504
+
505
+ return getIdsFunction(entity as any)[0] as IdType;
506
+ }
507
+
508
+ const getProperty = (id: string) => propertyMap.get(id);
509
+ const id = hash([...allPropertyNamesAndPaths, this.collectionName].join(","));
510
+ const paths = new Set(properties.map(x => x.getAssignmentPath()));
511
+
512
+ // memoize this by the validProperties
513
+ // TODO: See if we can generate a function to do this and eliminate loops
514
+ const deserializePartial = (item: Record<string, unknown>, properties: PropertyInfo<T>[]) => {
515
+
516
+ for (let i = 0, length = properties.length; i < length; i++) {
517
+ const property = properties[i];
518
+ const value = property.getValue(item);
519
+
520
+ if (value == null) {
521
+ continue;
522
+ }
523
+
524
+ const deserialized = property.deserialize(value);
525
+ property.setValue(item, deserialized);
526
+ }
527
+
528
+ return item as DeepPartial<InferType<T>>;
529
+ }
530
+
531
+ return {
532
+ createSubscription: (signal?: AbortSignal) => new CollectionSubscription(id, signal),
533
+ getId,
534
+ getProperty,
535
+ properties,
536
+ idProperties,
537
+ hasIdentities,
538
+ hashType,
539
+ getHashType: getHashTypeFunction,
540
+ merge: mergeFunction,
541
+ prepare: prepareFunction,
542
+ clone: cloneFunction,
543
+ deserializePartial,
544
+ deserialize: deserializeFunction,
545
+ serialize: serializeFunction,
546
+ compare: compareFunction,
547
+ strip: stripFunction,
548
+ hash: hashFunction,
549
+ id,
550
+ getIds: getIdsFunction,
551
+ enrich: enricherFunction,
552
+ collectionName: this.collectionName,
553
+ hasIdentityKeys,
554
+ freeze: freezeFunction,
555
+ enableChangeTracking: enableChangeTrackingFunction,
556
+ definition: this,
557
+ getIndexes: () => {
558
+
559
+ const indexes: Index[] = [];
560
+
561
+ for (let i = 0, length = properties.length; i < length; i++) {
562
+ const property = properties[i];
563
+
564
+ if (property.indexes.length === 0) {
565
+ continue; // No Index
566
+ }
567
+
568
+ for (let j = 0, l = property.indexes.length; j < l; j++) {
569
+ const indexName = property.indexes[j];
570
+
571
+ // Test for compound indexes
572
+ const connections = properties.filter(w =>
573
+ w !== property && // Don't match with self
574
+ w.indexes.some(index => index === indexName)
575
+ );
576
+
577
+ const indexedProperties = [property, ...connections];
578
+
579
+ if (indexedProperties.length === 1) {
580
+ // Not a compound property
581
+ if (indexedProperties[0].isKey) {
582
+ indexes.push({
583
+ properties: indexedProperties,
584
+ type: "primary-key",
585
+ name: indexName
586
+ });
587
+ continue;
588
+ }
589
+
590
+ if (indexedProperties[0].isDistinct) {
591
+ indexes.push({
592
+ properties: indexedProperties,
593
+ type: "unique",
594
+ name: indexName
595
+ });
596
+ continue;
597
+ }
598
+
599
+ indexes.push({
600
+ properties: indexedProperties,
601
+ type: "single",
602
+ name: indexName
603
+ });
604
+ continue;
605
+ }
606
+
607
+ if (indexes.some(w => w.name === indexName)) {
608
+ continue; // Already in a compound index
609
+ }
610
+
611
+ indexes.push({
612
+ properties: indexedProperties,
613
+ type: "compound",
614
+ name: indexName
615
+ });
616
+ }
617
+ }
618
+
619
+ return indexes;
620
+ }
621
+ }
622
+ } catch (e) {
623
+ throw new SchemaError(e, `Error compiling schema for collection: ${this.collectionName}`);
624
+ }
625
+ }
626
+ }
@@ -0,0 +1,18 @@
1
+ import { SchemaArray } from "./property/types/SchemaArray";
2
+ import { SchemaBase } from "./property/base/SchemaBase";
3
+ import { SchemaBoolean } from "./property/types/SchemaBoolean";
4
+ import { SchemaDate } from "./property/types/SchemaDate";
5
+ import { SchemaDefinition } from "./SchemaDefinition";
6
+ import { SchemaNumber } from "./property/types/SchemaNumber";
7
+ import { SchemaObject } from "./property/types/SchemaObject";
8
+ import { SchemaString } from "./property/types/SchemaString";
9
+
10
+ export const s = {
11
+ number: <T extends number[] = number[]>(...literals: T) => new SchemaNumber<T[number] extends never ? number : T[number], never>(null, literals),
12
+ string: <T extends string[] = string[]>(...literals: T) => new SchemaString<T[number] extends never ? string : T[number], never>(null, literals),
13
+ boolean: <T extends boolean = boolean>() => new SchemaBoolean<T, never>(),
14
+ date: <T extends Date = Date>() => new SchemaDate<T, never>(),
15
+ array: <T extends any>(schema: SchemaBase<T, never>) => new SchemaArray<SchemaBase<T, never>, never>(schema as any),
16
+ object: <T extends {} = {}>(schema: T) => new SchemaObject<T, never>(schema),
17
+ define: <T extends {}>(collectionName: string, schema: T) => new SchemaDefinition<T>(collectionName, schema)
18
+ }
@@ -0,0 +1,7 @@
1
+ export * from './builder';
2
+ export * from './table';
3
+ export * from './property/base';
4
+ export * from './property/modifiers';
5
+ export * from './property/types';
6
+ export * from './PropertyInfo';
7
+ export * from './types';