@arcote.tech/arc 0.1.8 → 0.1.10

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 (55) hide show
  1. package/dist/context/context.d.ts +1 -0
  2. package/dist/context/element.d.ts +21 -1
  3. package/dist/context/event.d.ts +2 -1
  4. package/dist/context/query-builders.d.ts +1 -0
  5. package/dist/data-storage/data-storage-master.d.ts +2 -0
  6. package/dist/database/database-mappers.d.ts +39 -0
  7. package/dist/database/database-store.d.ts +58 -0
  8. package/dist/database/index.d.ts +3 -0
  9. package/dist/database/schema-extraction.d.ts +12 -0
  10. package/dist/db/index.d.ts +1 -0
  11. package/dist/db/interface.d.ts +1 -0
  12. package/dist/db/postgresAdapter.d.ts +90 -0
  13. package/dist/db/sqliteAdapter.d.ts +47 -3
  14. package/dist/elements/abstract.d.ts +28 -0
  15. package/dist/elements/any.d.ts +2 -0
  16. package/dist/elements/array.d.ts +3 -0
  17. package/dist/elements/blob.d.ts +2 -0
  18. package/dist/elements/boolean.d.ts +3 -0
  19. package/dist/elements/branded.d.ts +2 -0
  20. package/dist/elements/date.d.ts +3 -0
  21. package/dist/elements/default.d.ts +2 -0
  22. package/dist/elements/file.d.ts +2 -0
  23. package/dist/elements/number.d.ts +3 -0
  24. package/dist/elements/object.d.ts +5 -0
  25. package/dist/elements/optional.d.ts +2 -0
  26. package/dist/elements/or.d.ts +2 -0
  27. package/dist/elements/record.d.ts +2 -0
  28. package/dist/elements/string-enum.d.ts +2 -0
  29. package/dist/elements/string.d.ts +3 -0
  30. package/dist/index.d.ts +0 -1
  31. package/dist/index.js +1574 -658
  32. package/dist/telemetry/context.d.ts +65 -0
  33. package/dist/telemetry/index.d.ts +47 -0
  34. package/dist/telemetry/interfaces.d.ts +84 -0
  35. package/dist/telemetry/logger.d.ts +67 -0
  36. package/dist/telemetry/no-op.d.ts +54 -0
  37. package/dist/telemetry/tracer.d.ts +85 -0
  38. package/dist/utils.d.ts +0 -19
  39. package/dist/view/view.d.ts +5 -3
  40. package/package.json +1 -1
  41. package/dist/collection/collection.d.ts +0 -81
  42. package/dist/collection/index.d.ts +0 -4
  43. package/dist/collection/queries/abstract-collection-query.d.ts +0 -14
  44. package/dist/collection/queries/find.d.ts +0 -29
  45. package/dist/collection/queries/one-item.d.ts +0 -2
  46. package/dist/collection/queries/util.d.ts +0 -3
  47. package/dist/collection/query-builders/find-by-id.d.ts +0 -2
  48. package/dist/collection/query-builders/find-one.d.ts +0 -2
  49. package/dist/collection/query-builders/find.d.ts +0 -13
  50. package/dist/context/simple-query.d.ts +0 -33
  51. package/dist/data-storage/data-storage-builder.d.ts +0 -16
  52. package/dist/data-storage/query-processor.d.ts +0 -22
  53. package/dist/data-storage/store-state-authorized.d.ts +0 -26
  54. package/dist/utils/arcObjectToStoreSchema.d.ts +0 -4
  55. package/dist/utils/index.d.ts +0 -2
package/dist/index.js CHANGED
@@ -43,6 +43,19 @@ class ArcOptional {
43
43
  anyOf: [parentSchema, { type: "null" }]
44
44
  };
45
45
  }
46
+ getColumnData() {
47
+ const parentColumnData = this.parent.getColumnData?.();
48
+ if (!parentColumnData) {
49
+ throw new Error(`Parent element does not implement getColumnData()`);
50
+ }
51
+ return {
52
+ ...parentColumnData,
53
+ storeData: {
54
+ ...parentColumnData.storeData,
55
+ isNullable: true
56
+ }
57
+ };
58
+ }
46
59
  }
47
60
 
48
61
  // elements/branded.ts
@@ -71,6 +84,13 @@ class ArcBranded {
71
84
  toJsonSchema() {
72
85
  return this.parent.toJsonSchema?.() ?? {};
73
86
  }
87
+ getColumnData() {
88
+ const parentColumnData = this.parent.getColumnData?.();
89
+ if (!parentColumnData) {
90
+ throw new Error(`Parent element does not implement getColumnData()`);
91
+ }
92
+ return parentColumnData;
93
+ }
74
94
  }
75
95
 
76
96
  // elements/default.ts
@@ -111,12 +131,24 @@ class ArcDefault {
111
131
  default: defaultValue
112
132
  };
113
133
  }
134
+ getColumnData() {
135
+ const parentColumnData = this.parent.getColumnData?.();
136
+ if (!parentColumnData) {
137
+ throw new Error(`Parent element does not implement getColumnData()`);
138
+ }
139
+ const defaultValue = typeof this.defaultValueOrCallback === "function" ? this.defaultValueOrCallback() : this.defaultValueOrCallback;
140
+ return {
141
+ ...parentColumnData,
142
+ defaultValue
143
+ };
144
+ }
114
145
  }
115
146
 
116
147
  // elements/abstract.ts
117
148
  class ArcAbstract {
118
149
  validations;
119
150
  _description;
151
+ _storeData;
120
152
  constructor(validations = []) {
121
153
  this.validations = validations;
122
154
  }
@@ -138,6 +170,7 @@ class ArcAbstract {
138
170
  const Constructor = this.constructor;
139
171
  const newInstance = Object.assign(new Constructor, this);
140
172
  newInstance._description = this._description;
173
+ newInstance._storeData = this._storeData ? { ...this._storeData } : undefined;
141
174
  return newInstance;
142
175
  }
143
176
  validate(value) {
@@ -169,194 +202,54 @@ class ArcAbstract {
169
202
  getValidations() {
170
203
  return this.validations;
171
204
  }
172
- }
173
-
174
- // elements/abstract-primitive.ts
175
- class ArcPrimitive extends ArcAbstract {
176
- serialize(value) {
177
- return value;
178
- }
179
- parse(value) {
180
- return value;
181
- }
182
- deserialize(value) {
183
- return value;
184
- }
185
- }
186
-
187
- // elements/utils/type-validator-builder.ts
188
- function primitiveTypeComparatorStrategyFactory(type) {
189
- return (value) => typeof value === type;
190
- }
191
- function typeValidatorBuilder(typeName, comparatorStrategy) {
192
- const comparator = comparatorStrategy || primitiveTypeComparatorStrategyFactory(typeName);
193
- return {
194
- name: "type",
195
- validator: (value) => {
196
- const valueType = typeof value;
197
- if (!comparator(value))
198
- return { current: valueType, expected: typeName };
199
- return false;
200
- }
201
- };
202
- }
203
-
204
- // elements/string.ts
205
- var stringValidator = typeValidatorBuilder("string");
206
-
207
- class ArcString extends ArcPrimitive {
208
- constructor() {
209
- super([stringValidator]);
210
- }
211
- minLength(min) {
212
- return this.validation("minLength", (value) => {
213
- if (value.length < min)
214
- return {
215
- currentLength: value.length,
216
- minLength: min
217
- };
218
- });
219
- }
220
- maxLength(max) {
221
- return this.validation("maxLength", (value) => {
222
- if (value.length > max)
223
- return {
224
- currentLength: value.length,
225
- maxLength: max
226
- };
227
- });
228
- }
229
- length(number) {
230
- return this.validation("length", (value) => {
231
- if (value.length !== number) {
232
- return {
233
- currentLength: value.length,
234
- length: number
235
- };
236
- }
237
- });
238
- }
239
- includes(str) {
240
- return this.validation("includes", (value) => {
241
- if (!value.includes(str)) {
242
- return {
243
- currentValue: value,
244
- includes: str
245
- };
246
- }
247
- });
248
- }
249
- startsWith(str) {
250
- return this.validation("startsWith", (value) => {
251
- if (!value.startsWith(str)) {
252
- return {
253
- currentValue: value,
254
- startsWith: str
255
- };
256
- }
257
- });
258
- }
259
- endsWith(str) {
260
- return this.validation("endsWith", (value) => {
261
- if (!value.endsWith(str)) {
262
- return {
263
- currentValue: value,
264
- endsWith: str
265
- };
266
- }
267
- });
268
- }
269
- regex(regex) {
270
- return this.validation("regex", (value) => {
271
- if (!regex.test(value)) {
272
- return {
273
- currentValue: value,
274
- regex
275
- };
276
- }
277
- });
278
- }
279
- email() {
280
- const regex = /^(?:(?:[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+)*)|(?:"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f])*"))@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$|(?:\[(?:\d{1,3}\.){3}\d{1,3}\])$/;
281
- return this.validation("email", (value) => {
282
- if (!regex.test(value)) {
283
- return {
284
- currentEmail: value
285
- };
286
- }
287
- });
288
- }
289
- toJsonSchema() {
290
- const schema = { type: "string" };
291
- if (this._description) {
292
- schema.description = this._description;
293
- }
294
- return schema;
205
+ primaryKey() {
206
+ const clone = this.clone();
207
+ clone._storeData = { ...clone._storeData, isPrimaryKey: true };
208
+ return clone;
295
209
  }
296
- url() {
297
- const regex = /^(https?):\/\/(?![-0-9])(?!www\.[0-9])(?:[a-zA-Z0-9-]+(?::[a-zA-Z0-9-]+)?@)?(?:localhost|\b(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b|[a-zA-Z0-9-]+\.)?[a-zA-Z0-9-]+\.(?:[a-zA-Z]{2,}|[a-zA-Z]{2}\.[a-zA-Z]{2})(?::\d{1,5})?(?!\/\/)(?:\/[a-zA-Z0-9-._~:?#\[\]@!$&'()*+,;=]*)*(?!\/{2})(?:;[a-zA-Z0-9-._~:?#\[\]@!$&'()*+,;=]*)*(\?(?!=)[a-zA-Z0-9&=;]*)?(\#[a-zA-Z0-9-]*)?$|^(https?):\/\/(localhost|\b(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b)(?::\d{1,5})?(?!\/\/)(?:\/[a-zA-Z0-9-._~:?#\[\]@!$&'()*+,;=]*)*(?!\/{2})(?:;[a-zA-Z0-9-._~:?#\[\]@!$&'()*+,;=]*)*(\?(?!=)[a-zA-Z0-9&=;]*)?(\#[a-zA-Z0-9-]*)?$/;
298
- return this.validation("url", (value) => {
299
- if (!regex.test(value)) {
300
- return {
301
- currentUrl: value
302
- };
303
- }
304
- });
210
+ autoIncrement() {
211
+ const clone = this.clone();
212
+ clone._storeData = { ...clone._storeData, isAutoIncrement: true };
213
+ return clone;
305
214
  }
306
- ip() {
307
- const IPv4regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
308
- const IPv6regex = /^(?:([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|:(:[0-9a-fA-F]{1,4}){1,7}|([0-9a-fA-F]{1,4}:){1,6}:([0-9a-fA-F]{1,4}:){1,6}[0-9a-fA-F]{1,4})$/;
309
- return this.validation("ip", (value) => {
310
- if (!(IPv4regex.test(value) || IPv6regex.test(value))) {
311
- return {
312
- currentIP: value
313
- };
314
- }
315
- });
215
+ unique() {
216
+ const clone = this.clone();
217
+ clone._storeData = { ...clone._storeData, isUnique: true };
218
+ return clone;
316
219
  }
317
- validation(name, validator) {
318
- const instance = this.pipeValidation(name, validator);
319
- return instance;
220
+ index() {
221
+ const clone = this.clone();
222
+ clone._storeData = { ...clone._storeData, hasIndex: true };
223
+ return clone;
320
224
  }
321
- }
322
- function string() {
323
- return new ArcString;
324
- }
325
-
326
- // elements/id.ts
327
- class ArcCustomId extends ArcBranded {
328
- createFn;
329
- constructor(name, createFn) {
330
- super(string(), name);
331
- this.createFn = createFn;
225
+ foreignKey(table, column, options) {
226
+ const clone = this.clone();
227
+ clone._storeData = {
228
+ ...clone._storeData,
229
+ foreignKey: { table, column, ...options }
230
+ };
231
+ return clone;
332
232
  }
333
- get(...args) {
334
- return this.createFn(...args);
233
+ databaseType(overrides) {
234
+ const clone = this.clone();
235
+ clone._storeData = {
236
+ ...clone._storeData,
237
+ databaseType: { ...clone._storeData?.databaseType, ...overrides }
238
+ };
239
+ return clone;
335
240
  }
336
- }
337
-
338
- class ArcId extends ArcBranded {
339
- generateFn;
340
- constructor(name, generateFn) {
341
- super(string(), name);
342
- this.generateFn = generateFn;
241
+ columnOptions(options) {
242
+ const clone = this.clone();
243
+ clone._storeData = {
244
+ ...clone._storeData,
245
+ columnOptions: { ...clone._storeData?.columnOptions, ...options }
246
+ };
247
+ return clone;
343
248
  }
344
- generate() {
345
- if (this.generateFn) {
346
- return this.generateFn();
347
- }
348
- var timestamp = (new Date().getTime() / 1000 | 0).toString(16);
349
- return timestamp + "xxxxxxxxxxxxxxxx".replace(/[x]/g, function() {
350
- return (Math.random() * 16 | 0).toString(16);
351
- }).toLowerCase();
249
+ getStoreData() {
250
+ return this._storeData;
352
251
  }
353
252
  }
354
- function id(name, generateFn) {
355
- return new ArcId(name, generateFn);
356
- }
357
- function customId(name, createFn) {
358
- return new ArcCustomId(name, createFn);
359
- }
360
253
 
361
254
  // elements/any.ts
362
255
  class ArcAny extends ArcAbstract {
@@ -379,10 +272,37 @@ class ArcAny extends ArcAbstract {
379
272
  }
380
273
  return schema;
381
274
  }
275
+ getColumnData() {
276
+ const storeData = this.getStoreData();
277
+ return {
278
+ type: "object",
279
+ storeData: {
280
+ ...storeData,
281
+ isNullable: false
282
+ }
283
+ };
284
+ }
382
285
  }
383
286
  function any() {
384
287
  return new ArcAny;
385
288
  }
289
+ // elements/utils/type-validator-builder.ts
290
+ function primitiveTypeComparatorStrategyFactory(type) {
291
+ return (value) => typeof value === type;
292
+ }
293
+ function typeValidatorBuilder(typeName, comparatorStrategy) {
294
+ const comparator = comparatorStrategy || primitiveTypeComparatorStrategyFactory(typeName);
295
+ return {
296
+ name: "type",
297
+ validator: (value) => {
298
+ const valueType = typeof value;
299
+ if (!comparator(value))
300
+ return { current: valueType, expected: typeName };
301
+ return false;
302
+ }
303
+ };
304
+ }
305
+
386
306
  // elements/object.ts
387
307
  var objectValidator = typeValidatorBuilder("object");
388
308
 
@@ -498,8 +418,10 @@ class ArcObject extends ArcAbstract {
498
418
  const properties = {};
499
419
  const required = [];
500
420
  for (const [key, element] of Object.entries(this.rawShape)) {
501
- properties[key] = element.toJsonSchema?.() ?? {};
502
- if (!(element instanceof ArcOptional)) {
421
+ if (element instanceof ArcOptional) {
422
+ properties[key] = element.parent.toJsonSchema?.() ?? {};
423
+ } else {
424
+ properties[key] = element.toJsonSchema?.() ?? {};
503
425
  required.push(key);
504
426
  }
505
427
  }
@@ -514,6 +436,20 @@ class ArcObject extends ArcAbstract {
514
436
  }
515
437
  return schema;
516
438
  }
439
+ getColumnData() {
440
+ const storeData = this.getStoreData();
441
+ return {
442
+ type: "object",
443
+ storeData: {
444
+ ...storeData,
445
+ isNullable: false
446
+ }
447
+ };
448
+ }
449
+ merge(otherShape) {
450
+ const mergedShape = { ...this.rawShape, ...otherShape };
451
+ return new ArcObject(mergedShape);
452
+ }
517
453
  }
518
454
  function object(element) {
519
455
  return new ArcObject(element);
@@ -613,10 +549,33 @@ class ArcArray extends ArcAbstract {
613
549
  }
614
550
  return schema;
615
551
  }
552
+ getColumnData() {
553
+ const storeData = this.getStoreData();
554
+ return {
555
+ type: "array",
556
+ storeData: {
557
+ ...storeData,
558
+ isNullable: false
559
+ }
560
+ };
561
+ }
616
562
  }
617
563
  function array(element) {
618
564
  return new ArcArray(element);
619
565
  }
566
+ // elements/abstract-primitive.ts
567
+ class ArcPrimitive extends ArcAbstract {
568
+ serialize(value) {
569
+ return value;
570
+ }
571
+ parse(value) {
572
+ return value;
573
+ }
574
+ deserialize(value) {
575
+ return value;
576
+ }
577
+ }
578
+
620
579
  // elements/blob.ts
621
580
  var blobValidator = {
622
581
  name: "blob",
@@ -715,7 +674,17 @@ class ArcBlob extends ArcPrimitive {
715
674
  }
716
675
  return schema;
717
676
  }
718
- }
677
+ getColumnData() {
678
+ const storeData = this.getStoreData();
679
+ return {
680
+ type: "blob",
681
+ storeData: {
682
+ ...storeData,
683
+ isNullable: false
684
+ }
685
+ };
686
+ }
687
+ }
719
688
  function blob() {
720
689
  return new ArcBlob;
721
690
  }
@@ -740,6 +709,16 @@ class ArcBoolean extends ArcPrimitive {
740
709
  }
741
710
  return schema;
742
711
  }
712
+ getColumnData() {
713
+ const storeData = this.getStoreData();
714
+ return {
715
+ type: "boolean",
716
+ storeData: {
717
+ ...storeData,
718
+ isNullable: false
719
+ }
720
+ };
721
+ }
743
722
  }
744
723
  function boolean() {
745
724
  return new ArcBoolean;
@@ -789,6 +768,16 @@ class ArcDate extends ArcAbstract {
789
768
  }
790
769
  return schema;
791
770
  }
771
+ getColumnData() {
772
+ const storeData = this.getStoreData();
773
+ return {
774
+ type: "date",
775
+ storeData: {
776
+ ...storeData,
777
+ isNullable: false
778
+ }
779
+ };
780
+ }
792
781
  }
793
782
  function date() {
794
783
  return new ArcDate;
@@ -967,10 +956,207 @@ class ArcFile extends ArcPrimitive {
967
956
  }
968
957
  return schema;
969
958
  }
959
+ getColumnData() {
960
+ const storeData = this.getStoreData();
961
+ return {
962
+ type: "blob",
963
+ storeData: {
964
+ ...storeData,
965
+ isNullable: false
966
+ }
967
+ };
968
+ }
970
969
  }
971
970
  function file() {
972
971
  return new ArcFile;
973
972
  }
973
+ // elements/string.ts
974
+ var stringValidator = typeValidatorBuilder("string");
975
+
976
+ class ArcString extends ArcPrimitive {
977
+ constructor() {
978
+ super([stringValidator]);
979
+ }
980
+ minLength(min) {
981
+ return this.validation("minLength", (value) => {
982
+ if (value.length < min)
983
+ return {
984
+ currentLength: value.length,
985
+ minLength: min
986
+ };
987
+ });
988
+ }
989
+ maxLength(max) {
990
+ return this.validation("maxLength", (value) => {
991
+ if (value.length > max)
992
+ return {
993
+ currentLength: value.length,
994
+ maxLength: max
995
+ };
996
+ });
997
+ }
998
+ length(number) {
999
+ return this.validation("length", (value) => {
1000
+ if (value.length !== number) {
1001
+ return {
1002
+ currentLength: value.length,
1003
+ length: number
1004
+ };
1005
+ }
1006
+ });
1007
+ }
1008
+ includes(str) {
1009
+ return this.validation("includes", (value) => {
1010
+ if (!value.includes(str)) {
1011
+ return {
1012
+ currentValue: value,
1013
+ includes: str
1014
+ };
1015
+ }
1016
+ });
1017
+ }
1018
+ startsWith(str) {
1019
+ return this.validation("startsWith", (value) => {
1020
+ if (!value.startsWith(str)) {
1021
+ return {
1022
+ currentValue: value,
1023
+ startsWith: str
1024
+ };
1025
+ }
1026
+ });
1027
+ }
1028
+ endsWith(str) {
1029
+ return this.validation("endsWith", (value) => {
1030
+ if (!value.endsWith(str)) {
1031
+ return {
1032
+ currentValue: value,
1033
+ endsWith: str
1034
+ };
1035
+ }
1036
+ });
1037
+ }
1038
+ regex(regex) {
1039
+ return this.validation("regex", (value) => {
1040
+ if (!regex.test(value)) {
1041
+ return {
1042
+ currentValue: value,
1043
+ regex
1044
+ };
1045
+ }
1046
+ });
1047
+ }
1048
+ email() {
1049
+ const regex = /^(?:(?:[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~.-]+)*)|(?:"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f])*"))@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$|(?:\[(?:\d{1,3}\.){3}\d{1,3}\])$/;
1050
+ return this.validation("email", (value) => {
1051
+ if (!regex.test(value)) {
1052
+ return {
1053
+ currentEmail: value
1054
+ };
1055
+ }
1056
+ });
1057
+ }
1058
+ toJsonSchema() {
1059
+ const schema = { type: "string" };
1060
+ if (this._description) {
1061
+ schema.description = this._description;
1062
+ }
1063
+ return schema;
1064
+ }
1065
+ url() {
1066
+ const regex = /^(https?):\/\/(?![-0-9])(?!www\.[0-9])(?:[a-zA-Z0-9-]+(?::[a-zA-Z0-9-]+)?@)?(?:localhost|\b(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b|[a-zA-Z0-9-]+\.)?[a-zA-Z0-9-]+\.(?:[a-zA-Z]{2,}|[a-zA-Z]{2}\.[a-zA-Z]{2})(?::\d{1,5})?(?!\/\/)(?:\/[a-zA-Z0-9-._~:?#\[\]@!$&'()*+,;=]*)*(?!\/{2})(?:;[a-zA-Z0-9-._~:?#\[\]@!$&'()*+,;=]*)*(\?(?!=)[a-zA-Z0-9&=;]*)?(\#[a-zA-Z0-9-]*)?$|^(https?):\/\/(localhost|\b(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b)(?::\d{1,5})?(?!\/\/)(?:\/[a-zA-Z0-9-._~:?#\[\]@!$&'()*+,;=]*)*(?!\/{2})(?:;[a-zA-Z0-9-._~:?#\[\]@!$&'()*+,;=]*)*(\?(?!=)[a-zA-Z0-9&=;]*)?(\#[a-zA-Z0-9-]*)?$/;
1067
+ return this.validation("url", (value) => {
1068
+ if (!regex.test(value)) {
1069
+ return {
1070
+ currentUrl: value
1071
+ };
1072
+ }
1073
+ });
1074
+ }
1075
+ ip() {
1076
+ const IPv4regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
1077
+ const IPv6regex = /^(?:([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|:(:[0-9a-fA-F]{1,4}){1,7}|([0-9a-fA-F]{1,4}:){1,6}:([0-9a-fA-F]{1,4}:){1,6}[0-9a-fA-F]{1,4})$/;
1078
+ return this.validation("ip", (value) => {
1079
+ if (!(IPv4regex.test(value) || IPv6regex.test(value))) {
1080
+ return {
1081
+ currentIP: value
1082
+ };
1083
+ }
1084
+ });
1085
+ }
1086
+ validation(name, validator) {
1087
+ const instance = this.pipeValidation(name, validator);
1088
+ return instance;
1089
+ }
1090
+ getColumnData() {
1091
+ const storeData = this.getStoreData();
1092
+ const validationInfo = {};
1093
+ for (const validation of this.validations) {
1094
+ switch (validation.name) {
1095
+ case "minLength":
1096
+ break;
1097
+ case "maxLength":
1098
+ break;
1099
+ case "regex":
1100
+ break;
1101
+ case "email":
1102
+ validationInfo.pattern = "email";
1103
+ break;
1104
+ case "url":
1105
+ validationInfo.pattern = "url";
1106
+ break;
1107
+ case "ip":
1108
+ validationInfo.pattern = "ip";
1109
+ break;
1110
+ }
1111
+ }
1112
+ return {
1113
+ type: "string",
1114
+ storeData: {
1115
+ ...storeData,
1116
+ isNullable: false
1117
+ },
1118
+ validationInfo
1119
+ };
1120
+ }
1121
+ }
1122
+ function string() {
1123
+ return new ArcString;
1124
+ }
1125
+
1126
+ // elements/id.ts
1127
+ class ArcCustomId extends ArcBranded {
1128
+ createFn;
1129
+ constructor(name, createFn) {
1130
+ super(string(), name);
1131
+ this.createFn = createFn;
1132
+ }
1133
+ get(...args) {
1134
+ return this.createFn(...args);
1135
+ }
1136
+ }
1137
+
1138
+ class ArcId extends ArcBranded {
1139
+ generateFn;
1140
+ constructor(name, generateFn) {
1141
+ super(string(), name);
1142
+ this.generateFn = generateFn;
1143
+ }
1144
+ generate() {
1145
+ if (this.generateFn) {
1146
+ return this.generateFn();
1147
+ }
1148
+ var timestamp = (new Date().getTime() / 1000 | 0).toString(16);
1149
+ return timestamp + "xxxxxxxxxxxxxxxx".replace(/[x]/g, function() {
1150
+ return (Math.random() * 16 | 0).toString(16);
1151
+ }).toLowerCase();
1152
+ }
1153
+ }
1154
+ function id(name, generateFn) {
1155
+ return new ArcId(name, generateFn);
1156
+ }
1157
+ function customId(name, createFn) {
1158
+ return new ArcCustomId(name, createFn);
1159
+ }
974
1160
  // elements/number.ts
975
1161
  var numberValidator = typeValidatorBuilder("number");
976
1162
 
@@ -1001,6 +1187,26 @@ class ArcNumber extends ArcPrimitive {
1001
1187
  }
1002
1188
  return schema;
1003
1189
  }
1190
+ getColumnData() {
1191
+ const storeData = this.getStoreData();
1192
+ const validationInfo = {};
1193
+ for (const validation of this.validations) {
1194
+ switch (validation.name) {
1195
+ case "min":
1196
+ break;
1197
+ case "max":
1198
+ break;
1199
+ }
1200
+ }
1201
+ return {
1202
+ type: "number",
1203
+ storeData: {
1204
+ ...storeData,
1205
+ isNullable: false
1206
+ },
1207
+ validationInfo
1208
+ };
1209
+ }
1004
1210
  }
1005
1211
  function number() {
1006
1212
  return new ArcNumber;
@@ -1039,6 +1245,23 @@ class ArcOr {
1039
1245
  anyOf: this.elements.map((el) => el.toJsonSchema?.() ?? {})
1040
1246
  };
1041
1247
  }
1248
+ getColumnData() {
1249
+ return {
1250
+ type: "object",
1251
+ storeData: {
1252
+ isNullable: false
1253
+ },
1254
+ validationInfo: {
1255
+ unionTypes: this.elements.map((el) => {
1256
+ try {
1257
+ return el.getColumnData?.()?.type || "unknown";
1258
+ } catch {
1259
+ return "unknown";
1260
+ }
1261
+ })
1262
+ }
1263
+ };
1264
+ }
1042
1265
  pickElement(value) {
1043
1266
  for (const element of this.elements) {
1044
1267
  if (this.isTypeOf(element, value))
@@ -1118,6 +1341,16 @@ class ArcRecord extends ArcAbstract {
1118
1341
  }
1119
1342
  return schema;
1120
1343
  }
1344
+ getColumnData() {
1345
+ const storeData = this.getStoreData();
1346
+ return {
1347
+ type: "record",
1348
+ storeData: {
1349
+ ...storeData,
1350
+ isNullable: false
1351
+ }
1352
+ };
1353
+ }
1121
1354
  }
1122
1355
  function record(key, element) {
1123
1356
  return new ArcRecord(key, element);
@@ -1160,368 +1393,50 @@ class ArcStringEnum extends ArcAbstract {
1160
1393
  getEnumerators() {
1161
1394
  return this.values;
1162
1395
  }
1396
+ getColumnData() {
1397
+ const storeData = this.getStoreData();
1398
+ return {
1399
+ type: "stringEnum",
1400
+ storeData: {
1401
+ ...storeData,
1402
+ isNullable: false
1403
+ },
1404
+ validationInfo: {
1405
+ enumValues: this.values
1406
+ }
1407
+ };
1408
+ }
1163
1409
  }
1164
1410
  function stringEnum(...values) {
1165
1411
  return new ArcStringEnum(values);
1166
1412
  }
1167
- // utils/arcObjectToStoreSchema.ts
1168
- function getSQLiteType(element2) {
1169
- if (element2 instanceof ArcDate) {
1170
- return "TEXT";
1171
- }
1172
- if (element2 instanceof ArcArray || element2 instanceof ArcObject) {
1173
- return "TEXT";
1174
- }
1175
- if (element2 instanceof ArcBoolean) {
1176
- return "INTEGER";
1177
- }
1178
- if (element2 instanceof ArcNumber) {
1179
- return "NUMBER";
1180
- }
1181
- return "TEXT";
1182
- }
1183
- function arcObjectToStoreSchema(tableName, schema) {
1184
- const columns = schema.entries().map(([name, element2]) => ({
1185
- name,
1186
- type: getSQLiteType(element2),
1187
- isOptional: element2 instanceof ArcOptional
1188
- }));
1189
- columns.unshift({
1190
- name: "_id",
1191
- type: "TEXT",
1192
- isOptional: false
1193
- }, {
1194
- name: "deleted",
1195
- type: "INTEGER",
1196
- isOptional: false,
1197
- default: 0
1198
- }, {
1199
- name: "lastUpdate",
1200
- type: "TEXT",
1201
- isOptional: false
1202
- });
1203
- return {
1204
- tables: [
1205
- {
1206
- name: tableName,
1207
- columns,
1208
- primaryKey: "_id"
1209
- }
1210
- ]
1211
- };
1212
- }
1213
-
1214
- // context/query.ts
1215
- class ArcQuery {
1216
- lastResult;
1217
- listeners = new Set;
1218
- subscribe(listener) {
1219
- this.listeners.add(listener);
1413
+ // command/command.ts
1414
+ class ArcCommand extends ArcContextElement {
1415
+ name;
1416
+ _description;
1417
+ _params;
1418
+ _results;
1419
+ _elements;
1420
+ _handler;
1421
+ _isPublic = false;
1422
+ constructor(name) {
1423
+ super();
1424
+ this.name = name;
1220
1425
  }
1221
- unsubscribe(listener) {
1222
- this.listeners.delete(listener);
1426
+ use(elements) {
1427
+ const clone = this.clone();
1428
+ clone._elements = elements;
1429
+ return clone;
1223
1430
  }
1224
- nextResult(result) {
1225
- this.lastResult = result;
1226
- this.listeners.forEach((listener) => listener(result));
1431
+ description(description) {
1432
+ const clone = this.clone();
1433
+ clone._description = description;
1434
+ return clone;
1227
1435
  }
1228
- }
1229
-
1230
- // context/serializable-query.ts
1231
- class ArcSerializableQuery extends ArcQuery {
1232
- params;
1233
- static key;
1234
- constructor(params) {
1235
- super();
1236
- this.params = params;
1237
- }
1238
- serialize() {
1239
- return {
1240
- key: this.constructor.key,
1241
- params: this.params
1242
- };
1243
- }
1244
- }
1245
-
1246
- // collection/queries/abstract-collection-query.ts
1247
- class ArcCollectionQuery extends ArcSerializableQuery {
1248
- collection;
1249
- bindedChangeHandler = this.changeHandler.bind(this);
1250
- store;
1251
- constructor(collection, params) {
1252
- super(params);
1253
- this.collection = collection;
1254
- }
1255
- async run(dataStorage) {
1256
- const store = dataStorage.getStore(this.collection.name);
1257
- this.store = store;
1258
- const result = await this.fetch(store);
1259
- this.lastResult = result;
1260
- return result;
1261
- }
1262
- changeHandler(changes) {
1263
- let resultChanged = false;
1264
- for (const change of changes) {
1265
- const response = this.onChange(change);
1266
- if (response !== false) {
1267
- this.lastResult = response;
1268
- resultChanged = true;
1269
- }
1270
- }
1271
- if (resultChanged)
1272
- this.nextResult(this.lastResult);
1273
- }
1274
- }
1275
-
1276
- // collection/queries/find.ts
1277
- class QueryCollectionResult {
1278
- result;
1279
- constructor(result) {
1280
- this.result = result;
1281
- }
1282
- get(id3) {
1283
- return id3 ? this.result.find((r) => r._id === id3) : undefined;
1284
- }
1285
- map(callbackfn) {
1286
- return this.result.map(callbackfn);
1287
- }
1288
- toArray() {
1289
- return this.result;
1290
- }
1291
- }
1292
-
1293
- class ArcFindQuery extends ArcCollectionQuery {
1294
- params;
1295
- constructor(collection, params) {
1296
- super(collection, params);
1297
- this.params = params;
1298
- }
1299
- async fetch(store) {
1300
- const results = await store.find(this.params, this.bindedChangeHandler);
1301
- return this.createResult(results);
1302
- }
1303
- checkItem(item) {
1304
- if (!this.params.where)
1305
- return true;
1306
- return Object.entries(this.params.where).every(([key, condition]) => {
1307
- const value = item[key];
1308
- if (typeof condition !== "object" || condition === null) {
1309
- return value === condition;
1310
- }
1311
- return Object.entries(condition).every(([operator, operatorValue]) => {
1312
- switch (operator) {
1313
- case "$eq":
1314
- return value === operatorValue;
1315
- case "$ne":
1316
- return value !== operatorValue;
1317
- case "$gt":
1318
- return typeof value === "number" && typeof operatorValue === "number" && value > operatorValue;
1319
- case "$gte":
1320
- return typeof value === "number" && typeof operatorValue === "number" && value >= operatorValue;
1321
- case "$lt":
1322
- return typeof value === "number" && typeof operatorValue === "number" && value < operatorValue;
1323
- case "$lte":
1324
- return typeof value === "number" && typeof operatorValue === "number" && value <= operatorValue;
1325
- case "$in":
1326
- return Array.isArray(operatorValue) && operatorValue.includes(value);
1327
- case "$nin":
1328
- return Array.isArray(operatorValue) && !operatorValue.includes(value);
1329
- case "$exists":
1330
- return typeof operatorValue === "boolean" ? operatorValue ? value !== undefined : value === undefined : true;
1331
- default:
1332
- return true;
1333
- }
1334
- });
1335
- });
1336
- }
1337
- onChange(change) {
1338
- const lastResult = this.lastResult;
1339
- const lastResultAsArray = lastResult || [];
1340
- const index = lastResultAsArray.findIndex((e) => e._id === (change.type === "delete" ? change.id : change.id));
1341
- const isInLastResult = index !== -1;
1342
- const shouldBeInTheResult = change.type !== "delete" && this.checkItem(change.item);
1343
- if (isInLastResult && shouldBeInTheResult)
1344
- return this.createResult(lastResultAsArray.toSpliced(index, 1, change.item));
1345
- else if (isInLastResult && (change.type === "delete" || !shouldBeInTheResult))
1346
- return this.createResult(lastResultAsArray.toSpliced(index, 1));
1347
- else if (!isInLastResult && shouldBeInTheResult)
1348
- return this.createResult(lastResultAsArray.concat(change.item));
1349
- return false;
1350
- }
1351
- createResult(result) {
1352
- return new QueryCollectionResult(result);
1353
- }
1354
- }
1355
-
1356
- // collection/query-builders/find.ts
1357
- class ArcFindQueryBuilder {
1358
- collection;
1359
- queryContext;
1360
- options;
1361
- constructor(collection, queryContext, options) {
1362
- this.collection = collection;
1363
- this.queryContext = queryContext;
1364
- this.options = options;
1365
- }
1366
- toQuery() {
1367
- return this.queryContext.cacheQuery(ArcFindQuery, [
1368
- this.collection,
1369
- this.options
1370
- ]);
1371
- }
1372
- run() {
1373
- return this.queryContext.runQuery(this.toQuery());
1374
- }
1375
- }
1376
-
1377
- // collection/collection.ts
1378
- class ArcCollection extends ArcContextElementWithStore {
1379
- name;
1380
- id;
1381
- schema;
1382
- options;
1383
- _restrictions;
1384
- constructor(name, id3, schema, options) {
1385
- super();
1386
- this.name = name;
1387
- this.id = id3;
1388
- this.schema = schema;
1389
- this.options = options;
1390
- }
1391
- storeSchema() {
1392
- return arcObjectToStoreSchema(this.name, this.schema);
1393
- }
1394
- restrictions(authContext) {
1395
- if (this._restrictions) {
1396
- return this._restrictions(authContext);
1397
- }
1398
- return {
1399
- read: false,
1400
- write: false,
1401
- modify: false,
1402
- delete: false
1403
- };
1404
- }
1405
- auth(restrictionsFn) {
1406
- const collection = new ArcCollection(this.name, this.id, this.schema, this.options);
1407
- collection._restrictions = restrictionsFn;
1408
- return collection;
1409
- }
1410
- serialize(data) {
1411
- return {
1412
- _id: this.id.serialize(data._id),
1413
- ...this.schema.serialize(data)
1414
- };
1415
- }
1416
- deserialize(data) {
1417
- return {
1418
- _id: this.id.deserialize(data._id),
1419
- ...this.schema.deserialize(data)
1420
- };
1421
- }
1422
- queryBuilder = (context) => {
1423
- return {
1424
- find: (options) => new ArcFindQueryBuilder(this, context, options || {})
1425
- };
1426
- };
1427
- commandContext = (dataStorage, publishEvent) => {
1428
- const store = dataStorage.getStore(this.name);
1429
- return {
1430
- add: async (data) => {
1431
- if (this.id instanceof ArcCustomId)
1432
- throw new Error("Collection with custom id not support 'add' method");
1433
- const id3 = this.id.generate();
1434
- const parsed = this.schema.parse(data);
1435
- const body = {
1436
- _id: id3,
1437
- lastUpdate: new Date().toISOString(),
1438
- ...parsed
1439
- };
1440
- await store.set(body);
1441
- await publishEvent({
1442
- type: "set",
1443
- to: body
1444
- });
1445
- return { id: id3 };
1446
- },
1447
- remove: async (id3) => {
1448
- await store.remove(id3);
1449
- return { success: true };
1450
- },
1451
- set: async (id3, data) => {
1452
- const parsed = this.schema.parse(data);
1453
- const body = {
1454
- _id: id3,
1455
- ...parsed
1456
- };
1457
- await store.set(body);
1458
- await publishEvent({
1459
- type: "set",
1460
- to: body
1461
- });
1462
- return { success: true };
1463
- },
1464
- find: async (options) => {
1465
- return store.find(options);
1466
- },
1467
- findOne: async (where) => {
1468
- const result = await store.find({
1469
- where,
1470
- limit: 1
1471
- });
1472
- return result[0];
1473
- },
1474
- modify: async (id3, data) => {
1475
- const deserialized = this.schema.serializePartial(data);
1476
- const { from, to } = await store.modify(id3, deserialized);
1477
- await publishEvent({
1478
- type: "modify",
1479
- changes: deserialized,
1480
- from,
1481
- to
1482
- });
1483
- },
1484
- edit: async (id3, editCallback) => {
1485
- const { from, to } = await store.mutate(id3, editCallback);
1486
- await publishEvent({
1487
- type: "mutate",
1488
- from,
1489
- to
1490
- });
1491
- }
1492
- };
1493
- };
1494
- }
1495
- function collection(name, id3, schema, options = {}) {
1496
- return new ArcCollection(name, id3, schema, options);
1497
- }
1498
- // command/command.ts
1499
- class ArcCommand extends ArcContextElement {
1500
- name;
1501
- _description;
1502
- _params;
1503
- _results;
1504
- _elements;
1505
- _handler;
1506
- _isPublic = false;
1507
- constructor(name) {
1508
- super();
1509
- this.name = name;
1510
- }
1511
- use(elements) {
1512
- const clone = this.clone();
1513
- clone._elements = elements;
1514
- return clone;
1515
- }
1516
- description(description) {
1517
- const clone = this.clone();
1518
- clone._description = description;
1519
- return clone;
1520
- }
1521
- public() {
1522
- const clone = this.clone();
1523
- clone._isPublic = true;
1524
- return clone;
1436
+ public() {
1437
+ const clone = this.clone();
1438
+ clone._isPublic = true;
1439
+ return clone;
1525
1440
  }
1526
1441
  get isPublic() {
1527
1442
  return this._isPublic;
@@ -1590,15 +1505,19 @@ function command(name) {
1590
1505
  // context/context.ts
1591
1506
  class ArcContext {
1592
1507
  elements;
1508
+ elementsSet = new Set;
1593
1509
  constructor(elements) {
1594
1510
  this.elements = elements;
1511
+ this.elements.forEach((element2) => {
1512
+ this.elementsSet.add(element2);
1513
+ });
1595
1514
  }
1596
1515
  get(name) {
1597
1516
  return this.elements.find((element2) => element2.name === name);
1598
1517
  }
1599
1518
  getSyncListeners(eventType, authContext) {
1600
1519
  const listeners = [];
1601
- this.elements.forEach((element2) => {
1520
+ this.elementsSet.forEach((element2) => {
1602
1521
  if (element2.observer) {
1603
1522
  const handlers = element2.observer(authContext);
1604
1523
  const config = handlers[eventType];
@@ -1611,7 +1530,7 @@ class ArcContext {
1611
1530
  }
1612
1531
  getAsyncListeners(eventType, authContext) {
1613
1532
  const listeners = [];
1614
- this.elements.forEach((element2) => {
1533
+ this.elementsSet.forEach((element2) => {
1615
1534
  if (element2.observer) {
1616
1535
  const handlers = element2.observer(authContext);
1617
1536
  const config = handlers[eventType];
@@ -1682,6 +1601,28 @@ function contextMerge(...contexts) {
1682
1601
  }
1683
1602
  // context/event.ts
1684
1603
  var eventValidator = typeValidatorBuilder("object");
1604
+ var eventSchema = new ArcObject({
1605
+ id: string().primaryKey(),
1606
+ type: string(),
1607
+ payload: any(),
1608
+ createdAt: date()
1609
+ });
1610
+ var sharedEventDatabaseSchema = null;
1611
+ function getSharedEventDatabaseSchema() {
1612
+ if (!sharedEventDatabaseSchema) {
1613
+ sharedEventDatabaseSchema = {
1614
+ tables: [{
1615
+ name: "events",
1616
+ schema: eventSchema,
1617
+ options: {
1618
+ softDelete: false,
1619
+ versioning: true
1620
+ }
1621
+ }]
1622
+ };
1623
+ }
1624
+ return sharedEventDatabaseSchema;
1625
+ }
1685
1626
  var eventStoreSchema = {
1686
1627
  tables: [
1687
1628
  {
@@ -1715,6 +1656,7 @@ class ArcEvent extends ArcContextElementWithStore {
1715
1656
  payload;
1716
1657
  _restrictions;
1717
1658
  storeSchema = () => eventStoreSchema;
1659
+ databaseStoreSchema = () => getSharedEventDatabaseSchema();
1718
1660
  constructor(name, payload) {
1719
1661
  super();
1720
1662
  this.name = name;
@@ -1760,6 +1702,21 @@ class ArcEvent extends ArcContextElementWithStore {
1760
1702
  function event(name, payload) {
1761
1703
  return new ArcEvent(name, payload ? payload instanceof ArcObject ? payload : new ArcObject(payload) : undefined);
1762
1704
  }
1705
+ // context/query.ts
1706
+ class ArcQuery {
1707
+ lastResult;
1708
+ listeners = new Set;
1709
+ subscribe(listener) {
1710
+ this.listeners.add(listener);
1711
+ }
1712
+ unsubscribe(listener) {
1713
+ this.listeners.delete(listener);
1714
+ }
1715
+ nextResult(result) {
1716
+ this.lastResult = result;
1717
+ this.listeners.forEach((listener) => listener(result));
1718
+ }
1719
+ }
1763
1720
  // context/query-builder-context.ts
1764
1721
  class QueryBuilderContext {
1765
1722
  queryCache;
@@ -2228,107 +2185,729 @@ class MasterStoreState extends StoreState {
2228
2185
  item,
2229
2186
  id: change.id
2230
2187
  }
2231
- };
2188
+ };
2189
+ }
2190
+ throw new Error("Unknown change type");
2191
+ }
2192
+ async applyChange(change) {
2193
+ const transaction = await this.dataStorage.getReadWriteTransaction();
2194
+ const { event: event3, from, to } = await this.applyChangeAndReturnEvent(transaction, change);
2195
+ await transaction.commit();
2196
+ this.notifyListeners([event3]);
2197
+ return { from, to };
2198
+ }
2199
+ async applyChanges(changes) {
2200
+ const transaction = await this.dataStorage.getReadWriteTransaction();
2201
+ const events = [];
2202
+ for (const change of changes) {
2203
+ const { event: event3 } = await this.applyChangeAndReturnEvent(transaction, change);
2204
+ if (event3)
2205
+ events.push(event3);
2206
+ }
2207
+ await transaction.commit();
2208
+ if (events.length > 0) {
2209
+ this.notifyListeners(events);
2210
+ }
2211
+ }
2212
+ async find(options, listener) {
2213
+ if (listener) {
2214
+ this.listeners.set(listener, { callback: listener });
2215
+ }
2216
+ const transaction = await this.dataStorage.getReadTransaction();
2217
+ const results = await transaction.find(this.storeName, options);
2218
+ return results.map((item) => this.deserialize ? this.deserialize(item) : item);
2219
+ }
2220
+ }
2221
+
2222
+ // data-storage/data-storage-master.ts
2223
+ class MasterDataStorage extends DataStorage {
2224
+ dbAdapter;
2225
+ arcContext;
2226
+ stores = new Map;
2227
+ rtcAdapter;
2228
+ reinitTablesExecuted = false;
2229
+ constructor(dbAdapter, rtcAdapterFactory, arcContext) {
2230
+ super();
2231
+ this.dbAdapter = dbAdapter;
2232
+ this.arcContext = arcContext;
2233
+ this.rtcAdapter = rtcAdapterFactory(this);
2234
+ this.executeReinitTablesOnReady();
2235
+ }
2236
+ async executeReinitTablesOnReady() {
2237
+ try {
2238
+ const adapter = await this.dbAdapter;
2239
+ if (!this.reinitTablesExecuted) {
2240
+ await adapter.executeReinitTables(this);
2241
+ this.reinitTablesExecuted = true;
2242
+ }
2243
+ } catch (error) {
2244
+ console.error("Failed to execute reinitTable functions:", error);
2245
+ }
2246
+ }
2247
+ async getReadTransaction() {
2248
+ return (await this.dbAdapter).readTransaction();
2249
+ }
2250
+ async getReadWriteTransaction() {
2251
+ return (await this.dbAdapter).readWriteTransaction();
2252
+ }
2253
+ getStore(storeName) {
2254
+ if (!this.stores.has(storeName)) {
2255
+ this.stores.set(storeName, new MasterStoreState(storeName, this));
2256
+ }
2257
+ return this.stores.get(storeName);
2258
+ }
2259
+ async applyChanges(changes) {
2260
+ return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
2261
+ }
2262
+ applySerializedChanges(changes) {
2263
+ return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applySerializedChanges(changes2)));
2264
+ }
2265
+ async commitChanges(changes) {
2266
+ await Promise.all([
2267
+ this.applyChanges(changes),
2268
+ this.rtcAdapter.commitChanges(changes)
2269
+ ]);
2270
+ }
2271
+ fork() {
2272
+ return new ForkedDataStorage(this);
2273
+ }
2274
+ async sync(progressCallback) {
2275
+ await this.rtcAdapter.sync(progressCallback);
2276
+ }
2277
+ }
2278
+ // database/schema-extraction.ts
2279
+ function extractDatabaseAgnosticSchema(arcObject, tableName) {
2280
+ const columns = [];
2281
+ for (const [fieldName, fieldSchema] of arcObject.entries()) {
2282
+ const arcElement = fieldSchema;
2283
+ if (typeof arcElement.getColumnData !== "function") {
2284
+ throw new Error(`Element for field '${fieldName}' does not implement getColumnData() method. Element type: ${arcElement.constructor.name}`);
2285
+ }
2286
+ const columnInfo = arcElement.getColumnData();
2287
+ const fullColumnInfo = {
2288
+ name: fieldName,
2289
+ ...columnInfo
2290
+ };
2291
+ columns.push(fullColumnInfo);
2292
+ }
2293
+ return {
2294
+ tableName,
2295
+ columns
2296
+ };
2297
+ }
2298
+
2299
+ // db/postgresAdapter.ts
2300
+ class PostgreSQLReadTransaction {
2301
+ db;
2302
+ tables;
2303
+ adapter;
2304
+ constructor(db, tables, adapter) {
2305
+ this.db = db;
2306
+ this.tables = tables;
2307
+ this.adapter = adapter;
2308
+ }
2309
+ hasSoftDelete(tableName) {
2310
+ if (this.adapter) {
2311
+ return this.adapter.hasSoftDelete(tableName);
2312
+ }
2313
+ const table = this.tables.get(tableName);
2314
+ if (!table)
2315
+ return false;
2316
+ return table.columns.some((col) => col.name === "deleted");
2317
+ }
2318
+ deserializeValue(value, column) {
2319
+ if (value === null || value === undefined)
2320
+ return null;
2321
+ switch (column.type.toLowerCase()) {
2322
+ case "json":
2323
+ case "jsonb":
2324
+ if (typeof value === "string") {
2325
+ try {
2326
+ return JSON.parse(value);
2327
+ } catch {
2328
+ return value;
2329
+ }
2330
+ }
2331
+ return value;
2332
+ case "text":
2333
+ if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) {
2334
+ try {
2335
+ const parsed = JSON.parse(value);
2336
+ if (typeof parsed === "object" || Array.isArray(parsed)) {
2337
+ return parsed;
2338
+ }
2339
+ } catch {}
2340
+ }
2341
+ return value;
2342
+ case "datetime":
2343
+ case "timestamp":
2344
+ return new Date(value);
2345
+ default:
2346
+ return value;
2347
+ }
2348
+ }
2349
+ deserializeRow(row, table) {
2350
+ const result = {};
2351
+ for (const column of table.columns) {
2352
+ const value = row[column.name];
2353
+ result[column.name] = this.deserializeValue(value, column);
2354
+ }
2355
+ return result;
2356
+ }
2357
+ getId(store, id3) {
2358
+ return id3;
2359
+ }
2360
+ buildWhereClause(where, tableName) {
2361
+ const conditions = [];
2362
+ const params = [];
2363
+ let paramIndex = 1;
2364
+ if (tableName && this.hasSoftDelete(tableName)) {
2365
+ conditions.push('"deleted" = $1');
2366
+ params.push(false);
2367
+ paramIndex = 2;
2368
+ }
2369
+ if (!where) {
2370
+ return {
2371
+ sql: conditions.length > 0 ? conditions.join(" AND ") : "1=1",
2372
+ params
2373
+ };
2374
+ }
2375
+ Object.entries(where).forEach(([key, value]) => {
2376
+ if (typeof value === "object" && value !== null) {
2377
+ Object.entries(value).forEach(([operator, operand]) => {
2378
+ switch (operator) {
2379
+ case "$eq":
2380
+ case "$ne":
2381
+ case "$gt":
2382
+ case "$gte":
2383
+ case "$lt":
2384
+ case "$lte":
2385
+ conditions.push(`"${key}" ${this.getOperatorSymbol(operator)} $${paramIndex}`);
2386
+ params.push(operand);
2387
+ paramIndex++;
2388
+ break;
2389
+ case "$in":
2390
+ case "$nin":
2391
+ if (Array.isArray(operand)) {
2392
+ const placeholders = operand.map(() => `$${paramIndex++}`).join(", ");
2393
+ conditions.push(`"${key}" ${operator === "$in" ? "IN" : "NOT IN"} (${placeholders})`);
2394
+ params.push(...operand);
2395
+ }
2396
+ break;
2397
+ case "$exists":
2398
+ if (typeof operand === "boolean") {
2399
+ conditions.push(operand ? `"${key}" IS NOT NULL` : `"${key}" IS NULL`);
2400
+ }
2401
+ break;
2402
+ }
2403
+ });
2404
+ } else {
2405
+ conditions.push(`"${key}" = $${paramIndex}`);
2406
+ params.push(value);
2407
+ paramIndex++;
2408
+ }
2409
+ });
2410
+ return {
2411
+ sql: conditions.join(" AND "),
2412
+ params
2413
+ };
2414
+ }
2415
+ getOperatorSymbol(operator) {
2416
+ const operators = {
2417
+ $eq: "=",
2418
+ $ne: "!=",
2419
+ $gt: ">",
2420
+ $gte: ">=",
2421
+ $lt: "<",
2422
+ $lte: "<="
2423
+ };
2424
+ return operators[operator] || "=";
2425
+ }
2426
+ buildOrderByClause(orderBy) {
2427
+ if (!orderBy)
2428
+ return "";
2429
+ const orderClauses = Object.entries(orderBy).map(([key, direction]) => `"${key}" ${direction.toUpperCase()}`).join(", ");
2430
+ return orderClauses ? `ORDER BY ${orderClauses}` : "";
2431
+ }
2432
+ async find(store, options) {
2433
+ const { where, limit, offset, orderBy } = options || {};
2434
+ const whereClause = this.buildWhereClause(where, store);
2435
+ const orderByClause = this.buildOrderByClause(orderBy);
2436
+ const table = this.tables.get(store);
2437
+ if (!table) {
2438
+ throw new Error(`Store ${store} not found`);
2439
+ }
2440
+ let query2 = `
2441
+ SELECT *
2442
+ FROM "${store}"
2443
+ WHERE ${whereClause.sql}
2444
+ ${orderByClause}
2445
+ `;
2446
+ let params = whereClause.params;
2447
+ let paramIndex = params.length + 1;
2448
+ if (limit) {
2449
+ query2 += ` LIMIT $${paramIndex}`;
2450
+ params.push(limit);
2451
+ paramIndex++;
2452
+ }
2453
+ if (offset) {
2454
+ query2 += ` OFFSET $${paramIndex}`;
2455
+ params.push(offset);
2456
+ }
2457
+ const rows = await this.db.exec(query2, params);
2458
+ return rows.map((row) => this.deserializeRow(row, table));
2459
+ }
2460
+ }
2461
+
2462
+ class PostgreSQLReadWriteTransaction extends PostgreSQLReadTransaction {
2463
+ adapter;
2464
+ queries = [];
2465
+ constructor(db, tables, adapter) {
2466
+ super(db, tables);
2467
+ this.adapter = adapter;
2468
+ }
2469
+ async remove(store, id3) {
2470
+ const table = this.tables.get(store);
2471
+ if (!table) {
2472
+ throw new Error(`Store ${store} not found`);
2473
+ }
2474
+ const query2 = `UPDATE "${store}" SET "deleted" = $1, "lastUpdate" = $2 WHERE "${table.primaryKey}" = $3`;
2475
+ this.queries.push({
2476
+ sql: query2,
2477
+ params: [true, new Date().toISOString(), id3]
2478
+ });
2479
+ }
2480
+ async set(store, item) {
2481
+ const table = this.tables.get(store);
2482
+ if (!table) {
2483
+ throw new Error(`Store ${store} not found`);
2484
+ }
2485
+ const hasVersioning = this.adapter.hasVersioning(store);
2486
+ if (hasVersioning) {
2487
+ await this.setWithVersioning(store, item, table);
2488
+ } else {
2489
+ await this.setWithoutVersioning(store, item, table);
2490
+ }
2491
+ }
2492
+ async setWithoutVersioning(store, item, table) {
2493
+ const columnNames = table.columns.map((col) => col.name);
2494
+ const values = table.columns.map((column) => {
2495
+ let value = item[column.name];
2496
+ if (value === undefined && column.default !== undefined) {
2497
+ value = column.default;
2498
+ }
2499
+ return this.serializeValue(value, column);
2500
+ });
2501
+ const placeholders = columnNames.map((_, i) => `$${i + 1}`).join(", ");
2502
+ const updateColumns = columnNames.filter((col) => col !== table.primaryKey).map((col) => `"${col}" = EXCLUDED."${col}"`).join(", ");
2503
+ if (store === "events") {
2504
+ const simpleInsertSql = `
2505
+ INSERT INTO "${table.name}"
2506
+ (${columnNames.map((c) => `"${c}"`).join(", ")})
2507
+ VALUES (${placeholders})
2508
+ `;
2509
+ this.queries.push({
2510
+ sql: simpleInsertSql,
2511
+ params: values
2512
+ });
2513
+ } else {
2514
+ const sql = `
2515
+ INSERT INTO "${table.name}"
2516
+ (${columnNames.map((c) => `"${c}"`).join(", ")})
2517
+ VALUES (${placeholders})
2518
+ ON CONFLICT ("${table.primaryKey}")
2519
+ DO UPDATE SET ${updateColumns}
2520
+ `;
2521
+ this.queries.push({
2522
+ sql,
2523
+ params: values
2524
+ });
2525
+ }
2526
+ }
2527
+ async setWithVersioning(store, item, table) {
2528
+ const regularColumns = table.columns.filter((col) => col.name !== "__version");
2529
+ const columnNames = regularColumns.map((col) => col.name);
2530
+ const values = regularColumns.map((column) => {
2531
+ let value = item[column.name];
2532
+ if (value === undefined && column.default !== undefined) {
2533
+ value = column.default;
2534
+ }
2535
+ return this.serializeValue(value, column);
2536
+ });
2537
+ columnNames.push("__version");
2538
+ const placeholders = regularColumns.map((_, i) => `$${i + 1}`).join(", ");
2539
+ const updateColumns = regularColumns.filter((col) => col.name !== table.primaryKey).map((col) => `"${col.name}" = EXCLUDED."${col.name}"`).join(", ");
2540
+ const sql = `
2541
+ WITH next_version AS (
2542
+ INSERT INTO __arc_version_counters (table_name, last_version)
2543
+ VALUES ($${values.length + 1}, 1)
2544
+ ON CONFLICT(table_name)
2545
+ DO UPDATE SET last_version = __arc_version_counters.last_version + 1
2546
+ RETURNING last_version
2547
+ ),
2548
+ upsert AS (
2549
+ INSERT INTO "${table.name}"
2550
+ (${columnNames.map((c) => `"${c}"`).join(", ")})
2551
+ VALUES (${placeholders}, (SELECT last_version FROM next_version))
2552
+ ON CONFLICT ("${table.primaryKey}")
2553
+ DO UPDATE SET ${updateColumns}${updateColumns ? ", " : ""}"__version" = (SELECT last_version FROM next_version)
2554
+ RETURNING *
2555
+ )
2556
+ SELECT * FROM upsert
2557
+ `;
2558
+ this.queries.push({
2559
+ sql,
2560
+ params: [...values, store]
2561
+ });
2562
+ }
2563
+ async commit() {
2564
+ if (this.queries.length === 0) {
2565
+ return Promise.resolve();
2566
+ }
2567
+ try {
2568
+ await this.db.execBatch(this.queries);
2569
+ this.queries = [];
2570
+ } catch (error) {
2571
+ this.queries = [];
2572
+ throw error;
2573
+ }
2574
+ }
2575
+ serializeValue(value, column) {
2576
+ if (value === null || value === undefined)
2577
+ return null;
2578
+ switch (column.type.toLowerCase()) {
2579
+ case "timestamp":
2580
+ case "datetime":
2581
+ if (value instanceof Date) {
2582
+ return value.toISOString();
2583
+ }
2584
+ if (typeof value === "number") {
2585
+ const date3 = value > 10000000000 ? new Date(value) : new Date(value * 1000);
2586
+ return date3.toISOString();
2587
+ }
2588
+ if (typeof value === "string") {
2589
+ const date3 = new Date(value);
2590
+ if (!isNaN(date3.getTime())) {
2591
+ return date3.toISOString();
2592
+ }
2593
+ }
2594
+ return value;
2595
+ case "json":
2596
+ case "jsonb":
2597
+ return JSON.stringify(value);
2598
+ default:
2599
+ if (value instanceof Date) {
2600
+ return value.toISOString();
2601
+ }
2602
+ if (Array.isArray(value) || typeof value === "object") {
2603
+ return JSON.stringify(value);
2604
+ }
2605
+ return value;
2232
2606
  }
2233
- throw new Error("Unknown change type");
2234
2607
  }
2235
- async applyChange(change) {
2236
- const transaction = await this.dataStorage.getReadWriteTransaction();
2237
- const { event: event3, from, to } = await this.applyChangeAndReturnEvent(transaction, change);
2238
- await transaction.commit();
2239
- this.notifyListeners([event3]);
2240
- return { from, to };
2608
+ }
2609
+
2610
+ class PostgreSQLAdapter {
2611
+ db;
2612
+ context;
2613
+ tables = new Map;
2614
+ tableSchemas = new Map;
2615
+ pendingReinitTables = [];
2616
+ mapType(arcType, storeData) {
2617
+ if (storeData?.databaseType?.postgresql) {
2618
+ return storeData.databaseType.postgresql;
2619
+ }
2620
+ switch (arcType) {
2621
+ case "string":
2622
+ case "stringEnum":
2623
+ return "TEXT";
2624
+ case "id":
2625
+ case "customId":
2626
+ return storeData?.databaseType?.postgresql || "TEXT";
2627
+ case "number":
2628
+ return storeData?.isAutoIncrement ? "SERIAL" : "INTEGER";
2629
+ case "boolean":
2630
+ return "BOOLEAN";
2631
+ case "date":
2632
+ return "TIMESTAMP";
2633
+ case "object":
2634
+ case "array":
2635
+ case "record":
2636
+ return "JSONB";
2637
+ case "blob":
2638
+ return "BYTEA";
2639
+ default:
2640
+ return "TEXT";
2641
+ }
2241
2642
  }
2242
- async applyChanges(changes) {
2243
- const transaction = await this.dataStorage.getReadWriteTransaction();
2244
- const events = [];
2245
- for (const change of changes) {
2246
- const { event: event3 } = await this.applyChangeAndReturnEvent(transaction, change);
2247
- if (event3)
2248
- events.push(event3);
2643
+ buildConstraints(storeData) {
2644
+ const constraints = [];
2645
+ if (storeData?.isPrimaryKey) {
2646
+ constraints.push("PRIMARY KEY");
2249
2647
  }
2250
- await transaction.commit();
2251
- if (events.length > 0) {
2252
- this.notifyListeners(events);
2648
+ if (storeData?.isUnique) {
2649
+ constraints.push("UNIQUE");
2650
+ }
2651
+ if (storeData?.foreignKey) {
2652
+ const { table, column, onDelete, onUpdate } = storeData.foreignKey;
2653
+ let fkConstraint = `REFERENCES ${table}(${column})`;
2654
+ if (onDelete)
2655
+ fkConstraint += ` ON DELETE ${onDelete}`;
2656
+ if (onUpdate)
2657
+ fkConstraint += ` ON UPDATE ${onUpdate}`;
2658
+ constraints.push(fkConstraint);
2253
2659
  }
2660
+ return constraints;
2254
2661
  }
2255
- async find(options, listener) {
2256
- if (listener) {
2257
- this.listeners.set(listener, { callback: listener });
2662
+ generateColumnSQL(columnInfo) {
2663
+ const type = this.mapType(columnInfo.type, columnInfo.storeData);
2664
+ const constraints = [];
2665
+ if (!columnInfo.storeData?.isNullable) {
2666
+ constraints.push("NOT NULL");
2258
2667
  }
2259
- const transaction = await this.dataStorage.getReadTransaction();
2260
- const results = await transaction.find(this.storeName, options);
2261
- return results.map((item) => this.deserialize ? this.deserialize(item) : item);
2668
+ constraints.push(...this.buildConstraints(columnInfo.storeData));
2669
+ if (columnInfo.defaultValue !== undefined) {
2670
+ if (type === "UUID" && columnInfo.defaultValue === "auto") {
2671
+ constraints.push("DEFAULT gen_random_uuid()");
2672
+ } else if (typeof columnInfo.defaultValue === "string") {
2673
+ constraints.push(`DEFAULT '${columnInfo.defaultValue}'`);
2674
+ } else {
2675
+ constraints.push(`DEFAULT ${columnInfo.defaultValue}`);
2676
+ }
2677
+ }
2678
+ return `"${columnInfo.name}" ${type} ${constraints.join(" ")}`.trim();
2679
+ }
2680
+ generateCreateTableSQL(tableName, columns) {
2681
+ const columnDefinitions = columns.map((col) => this.generateColumnSQL(col));
2682
+ const indexes = columns.filter((col) => col.storeData?.hasIndex && !col.storeData?.isPrimaryKey).map((col) => `CREATE INDEX IF NOT EXISTS idx_${tableName}_${col.name} ON "${tableName}"("${col.name}");`);
2683
+ let sql = `CREATE TABLE IF NOT EXISTS "${tableName}" (
2684
+ ${columnDefinitions.join(`,
2685
+ `)}
2686
+ )`;
2687
+ if (indexes.length > 0) {
2688
+ sql += `;
2689
+ ` + indexes.join(`
2690
+ `);
2691
+ }
2692
+ return sql;
2262
2693
  }
2263
- }
2264
-
2265
- // data-storage/data-storage-master.ts
2266
- class MasterDataStorage extends DataStorage {
2267
- dbAdapter;
2268
- arcContext;
2269
- stores = new Map;
2270
- rtcAdapter;
2271
- constructor(dbAdapter, rtcAdapterFactory, arcContext) {
2272
- super();
2273
- this.dbAdapter = dbAdapter;
2274
- this.arcContext = arcContext;
2275
- this.rtcAdapter = rtcAdapterFactory(this);
2694
+ constructor(db, context3) {
2695
+ this.db = db;
2696
+ this.context = context3;
2697
+ this.context.elements.forEach((element3) => {
2698
+ if ("databaseStoreSchema" in element3 && typeof element3.databaseStoreSchema === "function") {
2699
+ const databaseSchema = element3.databaseStoreSchema();
2700
+ databaseSchema.tables.forEach((dbTable) => {
2701
+ const agnosticSchema = extractDatabaseAgnosticSchema(dbTable.schema, dbTable.name);
2702
+ const columns = agnosticSchema.columns.map((columnInfo) => ({
2703
+ name: columnInfo.name,
2704
+ type: this.mapType(columnInfo.type, columnInfo.storeData),
2705
+ constraints: this.buildConstraints(columnInfo.storeData),
2706
+ isNullable: columnInfo.storeData?.isNullable || false,
2707
+ defaultValue: columnInfo.defaultValue,
2708
+ isPrimaryKey: columnInfo.storeData?.isPrimaryKey || false,
2709
+ isAutoIncrement: columnInfo.storeData?.isAutoIncrement || false,
2710
+ isUnique: columnInfo.storeData?.isUnique || false,
2711
+ hasIndex: columnInfo.storeData?.hasIndex || false,
2712
+ foreignKey: columnInfo.storeData?.foreignKey
2713
+ }));
2714
+ this.tableSchemas.set(dbTable.name, columns);
2715
+ const legacyTable = {
2716
+ name: dbTable.name,
2717
+ primaryKey: columns.find((col) => col.isPrimaryKey)?.name || "_id",
2718
+ columns: columns.map((col) => ({
2719
+ name: col.name,
2720
+ type: col.type,
2721
+ isOptional: col.isNullable,
2722
+ default: col.defaultValue
2723
+ }))
2724
+ };
2725
+ this.tables.set(dbTable.name, legacyTable);
2726
+ });
2727
+ }
2728
+ });
2276
2729
  }
2277
- async getReadTransaction() {
2278
- return (await this.dbAdapter).readTransaction();
2730
+ async initialize() {
2731
+ await this.createVersionCounterTable();
2732
+ await this.createTableVersionsTable();
2733
+ const processedSchemas = new Set;
2734
+ const processedTables = new Set;
2735
+ for (const element3 of this.context.elements) {
2736
+ if ("databaseStoreSchema" in element3 && typeof element3.databaseStoreSchema === "function") {
2737
+ const databaseSchema = element3.databaseStoreSchema();
2738
+ if (processedSchemas.has(databaseSchema)) {
2739
+ continue;
2740
+ }
2741
+ processedSchemas.add(databaseSchema);
2742
+ for (const dbTable of databaseSchema.tables) {
2743
+ const tableKey = dbTable.version ? `${dbTable.name}_v${dbTable.version}` : dbTable.name;
2744
+ if (!processedTables.has(tableKey)) {
2745
+ const agnosticSchema = extractDatabaseAgnosticSchema(dbTable.schema, dbTable.name);
2746
+ let allColumns = [...agnosticSchema.columns];
2747
+ if (dbTable.options?.versioning) {
2748
+ allColumns.push({
2749
+ name: "__version",
2750
+ type: "number",
2751
+ storeData: { isNullable: false, hasIndex: true },
2752
+ defaultValue: 1
2753
+ });
2754
+ }
2755
+ if (dbTable.options?.softDelete) {
2756
+ allColumns.push({
2757
+ name: "deleted",
2758
+ type: "boolean",
2759
+ storeData: { isNullable: false, hasIndex: true },
2760
+ defaultValue: false
2761
+ });
2762
+ }
2763
+ const physicalTableName = this.getPhysicalTableName(dbTable.name, dbTable.version);
2764
+ if (dbTable.version && await this.checkVersionedTableExists(dbTable.name, dbTable.version)) {
2765
+ console.log(`Versioned table ${physicalTableName} already exists, skipping creation`);
2766
+ } else {
2767
+ await this.createTableIfNotExistsNew(physicalTableName, allColumns);
2768
+ if (dbTable.version) {
2769
+ await this.registerTableVersion(dbTable.name, dbTable.version, physicalTableName);
2770
+ if (databaseSchema.reinitTable) {
2771
+ this.pendingReinitTables.push({
2772
+ tableName: physicalTableName,
2773
+ reinitFn: databaseSchema.reinitTable
2774
+ });
2775
+ }
2776
+ }
2777
+ }
2778
+ const legacyTable = {
2779
+ name: physicalTableName,
2780
+ primaryKey: allColumns.find((col) => col.storeData?.isPrimaryKey)?.name || "_id",
2781
+ columns: allColumns.map((col) => ({
2782
+ name: col.name,
2783
+ type: this.mapType(col.type, col.storeData),
2784
+ isOptional: col.storeData?.isNullable || false,
2785
+ default: col.defaultValue
2786
+ }))
2787
+ };
2788
+ this.tables.set(dbTable.name, legacyTable);
2789
+ processedTables.add(tableKey);
2790
+ }
2791
+ }
2792
+ }
2793
+ }
2279
2794
  }
2280
- async getReadWriteTransaction() {
2281
- return (await this.dbAdapter).readWriteTransaction();
2795
+ async createTableIfNotExistsNew(tableName, columns) {
2796
+ const createTableSQL = this.generateCreateTableSQL(tableName, columns);
2797
+ await this.db.exec(createTableSQL);
2282
2798
  }
2283
- getStore(storeName) {
2284
- if (!this.stores.has(storeName)) {
2285
- this.stores.set(storeName, new MasterStoreState(storeName, this));
2286
- }
2287
- return this.stores.get(storeName);
2799
+ async createVersionCounterTable() {
2800
+ const sql = `
2801
+ CREATE TABLE IF NOT EXISTS __arc_version_counters (
2802
+ table_name TEXT PRIMARY KEY,
2803
+ last_version BIGINT NOT NULL DEFAULT 0
2804
+ )
2805
+ `;
2806
+ await this.db.exec(sql);
2288
2807
  }
2289
- async applyChanges(changes) {
2290
- return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
2808
+ async createTableVersionsTable() {
2809
+ const sql = `
2810
+ CREATE TABLE IF NOT EXISTS __arc_table_versions (
2811
+ table_name TEXT NOT NULL,
2812
+ version INTEGER NOT NULL,
2813
+ physical_table_name TEXT NOT NULL,
2814
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
2815
+ is_active BOOLEAN DEFAULT FALSE,
2816
+ PRIMARY KEY (table_name, version)
2817
+ )
2818
+ `;
2819
+ await this.db.exec(sql);
2291
2820
  }
2292
- applySerializedChanges(changes) {
2293
- return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applySerializedChanges(changes2)));
2821
+ getPhysicalTableName(logicalName, version) {
2822
+ return version ? `${logicalName}_v${version}` : logicalName;
2294
2823
  }
2295
- async commitChanges(changes) {
2296
- await Promise.all([
2297
- this.applyChanges(changes),
2298
- this.rtcAdapter.commitChanges(changes)
2299
- ]);
2824
+ async checkVersionedTableExists(logicalName, version) {
2825
+ const result = await this.db.exec("SELECT COUNT(*) as count FROM __arc_table_versions WHERE table_name = $1 AND version = $2", [logicalName, version]);
2826
+ return result[0]?.count > 0;
2300
2827
  }
2301
- fork() {
2302
- return new ForkedDataStorage(this);
2828
+ async registerTableVersion(logicalName, version, physicalName) {
2829
+ await this.db.exec("INSERT INTO __arc_table_versions (table_name, version, physical_table_name, is_active) VALUES ($1, $2, $3, $4)", [logicalName, version, physicalName, true]);
2303
2830
  }
2304
- async sync(progressCallback) {
2305
- await this.rtcAdapter.sync(progressCallback);
2831
+ hasVersioning(tableName) {
2832
+ const table = this.tables.get(tableName);
2833
+ if (!table)
2834
+ return false;
2835
+ return table.columns.some((col) => col.name === "__version");
2836
+ }
2837
+ hasSoftDelete(tableName) {
2838
+ const table = this.tables.get(tableName);
2839
+ if (!table)
2840
+ return false;
2841
+ return table.columns.some((col) => col.name === "deleted");
2842
+ }
2843
+ async executeReinitTables(dataStorage) {
2844
+ for (const { tableName, reinitFn } of this.pendingReinitTables) {
2845
+ try {
2846
+ await reinitFn(tableName, dataStorage);
2847
+ console.log(`Successfully reinitialized table: ${tableName}`);
2848
+ } catch (error) {
2849
+ console.error(`Failed to reinitialize table ${tableName}:`, error);
2850
+ throw error;
2851
+ }
2852
+ }
2853
+ this.pendingReinitTables = [];
2854
+ }
2855
+ readWriteTransaction(stores) {
2856
+ return new PostgreSQLReadWriteTransaction(this.db, this.tables, this);
2857
+ }
2858
+ readTransaction(stores) {
2859
+ return new PostgreSQLReadTransaction(this.db, this.tables, this);
2306
2860
  }
2307
2861
  }
2862
+ var createPostgreSQLAdapterFactory = (db) => {
2863
+ return async (context3) => {
2864
+ const adapter = new PostgreSQLAdapter(db, context3);
2865
+ await adapter.initialize();
2866
+ return adapter;
2867
+ };
2868
+ };
2308
2869
  // db/sqliteAdapter.ts
2309
2870
  class SQLiteReadTransaction {
2310
2871
  db;
2311
2872
  tables;
2312
- constructor(db, tables) {
2873
+ adapter;
2874
+ constructor(db, tables, adapter) {
2313
2875
  this.db = db;
2314
2876
  this.tables = tables;
2877
+ this.adapter = adapter;
2878
+ }
2879
+ hasSoftDelete(tableName) {
2880
+ if (this.adapter) {
2881
+ return this.adapter.hasSoftDelete(tableName);
2882
+ }
2883
+ const table = this.tables.get(tableName);
2884
+ if (!table)
2885
+ return false;
2886
+ return table.columns.some((col) => col.name === "deleted");
2315
2887
  }
2316
2888
  deserializeValue(value, column) {
2317
2889
  if (value === null || value === undefined)
2318
2890
  return null;
2319
2891
  switch (column.type.toLowerCase()) {
2320
2892
  case "json":
2321
- case "text": {
2322
- try {
2323
- const parsed = JSON.parse(value);
2324
- if (typeof parsed === "object" || Array.isArray(parsed)) {
2325
- return parsed;
2893
+ if (typeof value === "string") {
2894
+ try {
2895
+ return JSON.parse(value);
2896
+ } catch {
2897
+ return value;
2326
2898
  }
2327
- return value;
2328
- } catch {
2329
- return value;
2330
2899
  }
2331
- }
2900
+ return value;
2901
+ case "text":
2902
+ if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) {
2903
+ try {
2904
+ const parsed = JSON.parse(value);
2905
+ if (typeof parsed === "object" || Array.isArray(parsed)) {
2906
+ return parsed;
2907
+ }
2908
+ } catch {}
2909
+ }
2910
+ return value;
2332
2911
  case "datetime":
2333
2912
  case "timestamp":
2334
2913
  return new Date(value);
@@ -2347,12 +2926,18 @@ class SQLiteReadTransaction {
2347
2926
  getId(store, id3) {
2348
2927
  return id3;
2349
2928
  }
2350
- buildWhereClause(where) {
2929
+ buildWhereClause(where, tableName) {
2930
+ const conditions = [];
2931
+ const params = [];
2932
+ if (tableName && this.hasSoftDelete(tableName)) {
2933
+ conditions.push("deleted = 0");
2934
+ }
2351
2935
  if (!where) {
2352
- return { sql: "deleted != 1", params: [] };
2936
+ return {
2937
+ sql: conditions.length > 0 ? conditions.join(" AND ") : "1=1",
2938
+ params
2939
+ };
2353
2940
  }
2354
- const conditions = ["deleted != 1"];
2355
- const params = [];
2356
2941
  Object.entries(where).forEach(([key, value]) => {
2357
2942
  if (typeof value === "object" && value !== null) {
2358
2943
  Object.entries(value).forEach(([operator, operand]) => {
@@ -2409,7 +2994,7 @@ class SQLiteReadTransaction {
2409
2994
  }
2410
2995
  async find(store, options) {
2411
2996
  const { where, limit, offset, orderBy } = options || {};
2412
- const whereClause = this.buildWhereClause(where);
2997
+ const whereClause = this.buildWhereClause(where, store);
2413
2998
  const orderByClause = this.buildOrderByClause(orderBy);
2414
2999
  const table = this.tables.get(store);
2415
3000
  if (!table) {
@@ -2429,6 +3014,11 @@ class SQLiteReadTransaction {
2429
3014
  }
2430
3015
 
2431
3016
  class SQLiteReadWriteTransaction extends SQLiteReadTransaction {
3017
+ adapter;
3018
+ constructor(db, tables, adapter) {
3019
+ super(db, tables, adapter);
3020
+ this.adapter = adapter;
3021
+ }
2432
3022
  async remove(store, id3) {
2433
3023
  const table = this.tables.get(store);
2434
3024
  if (!table) {
@@ -2442,7 +3032,14 @@ class SQLiteReadWriteTransaction extends SQLiteReadTransaction {
2442
3032
  if (!table) {
2443
3033
  throw new Error(`Store ${store} not found`);
2444
3034
  }
2445
- const now = new Date().toISOString();
3035
+ const hasVersioning = this.adapter.hasVersioning(store);
3036
+ if (hasVersioning) {
3037
+ await this.setWithVersioning(store, item, table);
3038
+ } else {
3039
+ await this.setWithoutVersioning(store, item, table);
3040
+ }
3041
+ }
3042
+ async setWithoutVersioning(store, item, table) {
2446
3043
  const columnNames = table.columns.map((col) => col.name);
2447
3044
  const values = table.columns.map((column) => {
2448
3045
  let value = item[column.name];
@@ -2459,6 +3056,32 @@ class SQLiteReadWriteTransaction extends SQLiteReadTransaction {
2459
3056
  `;
2460
3057
  await this.db.exec(sql, values);
2461
3058
  }
3059
+ async setWithVersioning(store, item, table) {
3060
+ const regularColumns = table.columns.filter((col) => col.name !== "__version");
3061
+ const columnNames = regularColumns.map((col) => col.name);
3062
+ const values = regularColumns.map((column) => {
3063
+ let value = item[column.name];
3064
+ if (value === undefined && column.default !== undefined) {
3065
+ value = column.default;
3066
+ }
3067
+ return this.serializeValue(value, column);
3068
+ });
3069
+ columnNames.push("__version");
3070
+ const placeholders = regularColumns.map(() => "?").join(", ");
3071
+ const sql = `
3072
+ WITH next_version AS (
3073
+ INSERT INTO __arc_version_counters (table_name, last_version)
3074
+ VALUES (?, 1)
3075
+ ON CONFLICT(table_name)
3076
+ DO UPDATE SET last_version = last_version + 1
3077
+ RETURNING last_version
3078
+ )
3079
+ INSERT OR REPLACE INTO ${table.name}
3080
+ (${columnNames.map((c) => `"${c}"`).join(", ")})
3081
+ VALUES (${placeholders}, (SELECT last_version FROM next_version))
3082
+ `;
3083
+ await this.db.exec(sql, [...values, store]);
3084
+ }
2462
3085
  async commit() {
2463
3086
  return Promise.resolve();
2464
3087
  }
@@ -2466,6 +3089,22 @@ class SQLiteReadWriteTransaction extends SQLiteReadTransaction {
2466
3089
  if (value === null || value === undefined)
2467
3090
  return null;
2468
3091
  switch (column.type.toLowerCase()) {
3092
+ case "timestamp":
3093
+ case "datetime":
3094
+ if (value instanceof Date) {
3095
+ return value.toISOString();
3096
+ }
3097
+ if (typeof value === "number") {
3098
+ const date3 = value > 10000000000 ? new Date(value) : new Date(value * 1000);
3099
+ return date3.toISOString();
3100
+ }
3101
+ if (typeof value === "string") {
3102
+ const date3 = new Date(value);
3103
+ if (!isNaN(date3.getTime())) {
3104
+ return date3.toISOString();
3105
+ }
3106
+ }
3107
+ return value;
2469
3108
  case "json":
2470
3109
  return JSON.stringify(value);
2471
3110
  default:
@@ -2484,58 +3123,238 @@ class SQLiteAdapter {
2484
3123
  db;
2485
3124
  context;
2486
3125
  tables = new Map;
3126
+ tableSchemas = new Map;
3127
+ pendingReinitTables = [];
3128
+ mapType(arcType, storeData) {
3129
+ if (storeData?.databaseType?.sqlite) {
3130
+ return storeData.databaseType.sqlite;
3131
+ }
3132
+ switch (arcType) {
3133
+ case "string":
3134
+ case "id":
3135
+ case "customId":
3136
+ case "stringEnum":
3137
+ return "TEXT";
3138
+ case "number":
3139
+ return "INTEGER";
3140
+ case "boolean":
3141
+ return "INTEGER";
3142
+ case "date":
3143
+ return "TIMESTAMP";
3144
+ case "object":
3145
+ case "array":
3146
+ case "record":
3147
+ return "JSON";
3148
+ case "blob":
3149
+ return "BLOB";
3150
+ default:
3151
+ return "TEXT";
3152
+ }
3153
+ }
3154
+ buildConstraints(storeData) {
3155
+ const constraints = [];
3156
+ if (storeData?.isPrimaryKey) {
3157
+ constraints.push("PRIMARY KEY");
3158
+ }
3159
+ if (storeData?.isAutoIncrement) {
3160
+ constraints.push("AUTOINCREMENT");
3161
+ }
3162
+ if (storeData?.isUnique) {
3163
+ constraints.push("UNIQUE");
3164
+ }
3165
+ if (storeData?.foreignKey) {
3166
+ const { table, column, onDelete, onUpdate } = storeData.foreignKey;
3167
+ let fkConstraint = `REFERENCES ${table}(${column})`;
3168
+ if (onDelete)
3169
+ fkConstraint += ` ON DELETE ${onDelete}`;
3170
+ if (onUpdate)
3171
+ fkConstraint += ` ON UPDATE ${onUpdate}`;
3172
+ constraints.push(fkConstraint);
3173
+ }
3174
+ return constraints;
3175
+ }
3176
+ generateColumnSQL(columnInfo) {
3177
+ const type = this.mapType(columnInfo.type, columnInfo.storeData);
3178
+ const constraints = [];
3179
+ if (!columnInfo.storeData?.isNullable) {
3180
+ constraints.push("NOT NULL");
3181
+ }
3182
+ constraints.push(...this.buildConstraints(columnInfo.storeData));
3183
+ if (columnInfo.defaultValue !== undefined) {
3184
+ constraints.push(`DEFAULT ${JSON.stringify(columnInfo.defaultValue)}`);
3185
+ }
3186
+ return `"${columnInfo.name}" ${type} ${constraints.join(" ")}`.trim();
3187
+ }
3188
+ generateCreateTableSQL(tableName, columns) {
3189
+ const columnDefinitions = columns.map((col) => this.generateColumnSQL(col));
3190
+ const indexes = columns.filter((col) => col.storeData?.hasIndex && !col.storeData?.isPrimaryKey).map((col) => `CREATE INDEX IF NOT EXISTS idx_${tableName}_${col.name} ON ${tableName}(${col.name});`);
3191
+ let sql = `CREATE TABLE IF NOT EXISTS ${tableName} (
3192
+ ${columnDefinitions.join(`,
3193
+ `)}
3194
+ )`;
3195
+ if (indexes.length > 0) {
3196
+ sql += `;
3197
+ ` + indexes.join(`
3198
+ `);
3199
+ }
3200
+ return sql;
3201
+ }
2487
3202
  constructor(db, context3) {
2488
3203
  this.db = db;
2489
3204
  this.context = context3;
2490
3205
  this.context.elements.forEach((element3) => {
2491
- if ("storeSchema" in element3 && typeof element3.storeSchema === "function") {
2492
- element3.storeSchema().tables.forEach((table) => {
2493
- this.tables.set(table.name, table);
3206
+ if ("databaseStoreSchema" in element3 && typeof element3.databaseStoreSchema === "function") {
3207
+ const databaseSchema = element3.databaseStoreSchema();
3208
+ databaseSchema.tables.forEach((dbTable) => {
3209
+ const agnosticSchema = extractDatabaseAgnosticSchema(dbTable.schema, dbTable.name);
3210
+ const columns = agnosticSchema.columns.map((columnInfo) => ({
3211
+ name: columnInfo.name,
3212
+ type: this.mapType(columnInfo.type, columnInfo.storeData),
3213
+ constraints: this.buildConstraints(columnInfo.storeData),
3214
+ isNullable: columnInfo.storeData?.isNullable || false,
3215
+ defaultValue: columnInfo.defaultValue,
3216
+ isPrimaryKey: columnInfo.storeData?.isPrimaryKey || false,
3217
+ isAutoIncrement: columnInfo.storeData?.isAutoIncrement || false,
3218
+ isUnique: columnInfo.storeData?.isUnique || false,
3219
+ hasIndex: columnInfo.storeData?.hasIndex || false,
3220
+ foreignKey: columnInfo.storeData?.foreignKey
3221
+ }));
3222
+ this.tableSchemas.set(dbTable.name, columns);
3223
+ const physicalTableName = this.getPhysicalTableName(dbTable.name, dbTable.version);
3224
+ const legacyTable = {
3225
+ name: physicalTableName,
3226
+ primaryKey: columns.find((col) => col.isPrimaryKey)?.name || "_id",
3227
+ columns: columns.map((col) => ({
3228
+ name: col.name,
3229
+ type: col.type,
3230
+ isOptional: col.isNullable,
3231
+ default: col.defaultValue
3232
+ }))
3233
+ };
3234
+ this.tables.set(dbTable.name, legacyTable);
2494
3235
  });
2495
3236
  }
2496
3237
  });
2497
3238
  }
2498
3239
  async initialize() {
2499
- const stores = new Set;
3240
+ await this.createVersionCounterTable();
3241
+ await this.createTableVersionsTable();
3242
+ const processedSchemas = new Set;
3243
+ const processedTables = new Set;
2500
3244
  for (const element3 of this.context.elements) {
2501
- if ("storeSchema" in element3 && typeof element3.storeSchema === "function") {
2502
- const schema = element3.storeSchema();
2503
- stores.add(schema);
2504
- }
2505
- }
2506
- for (const schema of stores) {
2507
- for (const table of schema.tables) {
2508
- await this.createTableIfNotExists(table);
3245
+ if ("databaseStoreSchema" in element3 && typeof element3.databaseStoreSchema === "function") {
3246
+ const databaseSchema = element3.databaseStoreSchema();
3247
+ if (processedSchemas.has(databaseSchema)) {
3248
+ continue;
3249
+ }
3250
+ processedSchemas.add(databaseSchema);
3251
+ for (const dbTable of databaseSchema.tables) {
3252
+ const tableKey = dbTable.version ? `${dbTable.name}_v${dbTable.version}` : dbTable.name;
3253
+ if (!processedTables.has(tableKey)) {
3254
+ const agnosticSchema = extractDatabaseAgnosticSchema(dbTable.schema, dbTable.name);
3255
+ let allColumns = [...agnosticSchema.columns];
3256
+ if (dbTable.options?.versioning) {
3257
+ allColumns.push({
3258
+ name: "__version",
3259
+ type: "number",
3260
+ storeData: { isNullable: false, hasIndex: true },
3261
+ defaultValue: 1
3262
+ });
3263
+ }
3264
+ if (dbTable.options?.softDelete) {
3265
+ allColumns.push({
3266
+ name: "deleted",
3267
+ type: "boolean",
3268
+ storeData: { isNullable: false, hasIndex: true },
3269
+ defaultValue: false
3270
+ });
3271
+ }
3272
+ const physicalTableName = this.getPhysicalTableName(dbTable.name, dbTable.version);
3273
+ if (dbTable.version && await this.checkVersionedTableExists(dbTable.name, dbTable.version)) {
3274
+ console.log(`Versioned table ${physicalTableName} already exists, skipping creation`);
3275
+ } else {
3276
+ await this.createTableIfNotExistsNew(physicalTableName, allColumns);
3277
+ if (dbTable.version) {
3278
+ await this.registerTableVersion(dbTable.name, dbTable.version, physicalTableName);
3279
+ if (databaseSchema.reinitTable) {
3280
+ this.pendingReinitTables.push({
3281
+ tableName: physicalTableName,
3282
+ reinitFn: databaseSchema.reinitTable
3283
+ });
3284
+ }
3285
+ }
3286
+ }
3287
+ processedTables.add(tableKey);
3288
+ }
3289
+ }
2509
3290
  }
2510
3291
  }
2511
3292
  }
2512
- async createTableIfNotExists(table) {
2513
- const columns = table.columns.map((column) => {
2514
- const constraints = [];
2515
- if (!column.isOptional) {
2516
- constraints.push("NOT NULL");
2517
- }
2518
- if (column.default !== undefined) {
2519
- constraints.push(`DEFAULT ${JSON.stringify(column.default)}`);
2520
- }
2521
- if (column.name === table.primaryKey) {
2522
- constraints.push("PRIMARY KEY");
2523
- }
2524
- return `"${column.name}" ${column.type} ${constraints.join(" ")}`.trim();
2525
- });
2526
- const createTableSQL = `
2527
- CREATE TABLE IF NOT EXISTS ${table.name} (
2528
- ${columns.join(`,
2529
- `)}
3293
+ async createTableIfNotExistsNew(tableName, columns) {
3294
+ const createTableSQL = this.generateCreateTableSQL(tableName, columns);
3295
+ await this.db.exec(createTableSQL);
3296
+ }
3297
+ async createVersionCounterTable() {
3298
+ const sql = `
3299
+ CREATE TABLE IF NOT EXISTS __arc_version_counters (
3300
+ table_name TEXT PRIMARY KEY,
3301
+ last_version INTEGER NOT NULL DEFAULT 0
2530
3302
  )
2531
3303
  `;
2532
- await this.db.exec(createTableSQL);
3304
+ await this.db.exec(sql);
3305
+ }
3306
+ async createTableVersionsTable() {
3307
+ const sql = `
3308
+ CREATE TABLE IF NOT EXISTS __arc_table_versions (
3309
+ table_name TEXT NOT NULL,
3310
+ version INTEGER NOT NULL,
3311
+ physical_table_name TEXT NOT NULL,
3312
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
3313
+ is_active INTEGER DEFAULT 0,
3314
+ PRIMARY KEY (table_name, version)
3315
+ )
3316
+ `;
3317
+ await this.db.exec(sql);
3318
+ }
3319
+ getPhysicalTableName(logicalName, version) {
3320
+ return version ? `${logicalName}_v${version}` : logicalName;
3321
+ }
3322
+ async checkVersionedTableExists(logicalName, version) {
3323
+ const result = await this.db.exec("SELECT COUNT(*) as count FROM __arc_table_versions WHERE table_name = ? AND version = ?", [logicalName, version]);
3324
+ return result[0]?.count > 0;
3325
+ }
3326
+ async registerTableVersion(logicalName, version, physicalName) {
3327
+ await this.db.exec("INSERT INTO __arc_table_versions (table_name, version, physical_table_name, is_active) VALUES (?, ?, ?, ?)", [logicalName, version, physicalName, 1]);
3328
+ }
3329
+ hasVersioning(tableName) {
3330
+ const table = this.tables.get(tableName);
3331
+ if (!table)
3332
+ return false;
3333
+ return table.columns.some((col) => col.name === "__version");
3334
+ }
3335
+ hasSoftDelete(tableName) {
3336
+ const table = this.tables.get(tableName);
3337
+ if (!table)
3338
+ return false;
3339
+ return table.columns.some((col) => col.name === "deleted");
3340
+ }
3341
+ async executeReinitTables(dataStorage) {
3342
+ for (const { tableName, reinitFn } of this.pendingReinitTables) {
3343
+ try {
3344
+ await reinitFn(tableName, dataStorage);
3345
+ console.log(`Successfully reinitialized table: ${tableName}`);
3346
+ } catch (error) {
3347
+ console.error(`Failed to reinitialize table ${tableName}:`, error);
3348
+ throw error;
3349
+ }
3350
+ }
3351
+ this.pendingReinitTables = [];
2533
3352
  }
2534
3353
  readWriteTransaction(stores) {
2535
- return new SQLiteReadWriteTransaction(this.db, this.tables);
3354
+ return new SQLiteReadWriteTransaction(this.db, this.tables, this);
2536
3355
  }
2537
3356
  readTransaction(stores) {
2538
- return new SQLiteReadTransaction(this.db, this.tables);
3357
+ return new SQLiteReadTransaction(this.db, this.tables, this);
2539
3358
  }
2540
3359
  }
2541
3360
  var createSQLiteAdapterFactory = (db) => {
@@ -2599,17 +3418,17 @@ class ArcListener extends ArcContextElement {
2599
3418
  return (e) => {
2600
3419
  const element4 = this._elements.find((element5) => element5.name === e.name);
2601
3420
  if (!element4) {
2602
- throw new Error(`Element "${String(name)}" not found in listener "${this.name}"`);
3421
+ throw new Error(`Element "${String(e.name)}" not found in listener "${this.name}"`);
2603
3422
  }
2604
3423
  if (!element4.commandContext) {
2605
- throw new Error(`Element "${String(name)}" does not have a command context`);
3424
+ throw new Error(`Element "${String(e.name)}" does not have a command context`);
2606
3425
  }
2607
3426
  return element4.commandContext(dataStorage, publishEvent, authContext);
2608
3427
  };
2609
3428
  }
2610
3429
  const element3 = this._elements.find((element4) => element4.name === name);
2611
3430
  if (!element3) {
2612
- throw new Error(`Element "${String(name)}" not found in listener "${this.name}"`);
3431
+ throw new Error(`Element "${element3}" not found in listener "${this.name}"`);
2613
3432
  }
2614
3433
  if (!element3.commandContext) {
2615
3434
  throw new Error(`Element "${String(name)}" does not have a command context`);
@@ -2673,14 +3492,14 @@ class EventPublisher {
2673
3492
  async runAsyncListeners() {
2674
3493
  const allAsyncTasks = this.asyncEvents.flatMap(({ event: event3, listeners, authContext }) => listeners.map(async (listener3) => {
2675
3494
  const listenerFork = this.dataStorage.fork();
3495
+ const childPublisher = new EventPublisher(this.context, this.dataStorage, authContext);
2676
3496
  const asyncPublishEvent = async (childEvent) => {
2677
- const childPublisher = new EventPublisher(this.context, listenerFork, authContext);
2678
3497
  await childPublisher.publishEvent(childEvent, listenerFork);
2679
- await childPublisher.runAsyncListeners();
2680
3498
  };
2681
3499
  try {
2682
3500
  await listener3(event3, listenerFork, asyncPublishEvent);
2683
3501
  await listenerFork.merge();
3502
+ childPublisher.runAsyncListeners();
2684
3503
  } catch (error) {
2685
3504
  console.error("Async listener failed:", error);
2686
3505
  }
@@ -3154,6 +3973,22 @@ class RTCClient {
3154
3973
  var rtcClientFactory = (token) => (storage) => {
3155
3974
  return new RTCClient(storage, token);
3156
3975
  };
3976
+ // context/serializable-query.ts
3977
+ class ArcSerializableQuery extends ArcQuery {
3978
+ params;
3979
+ static key;
3980
+ constructor(params) {
3981
+ super();
3982
+ this.params = params;
3983
+ }
3984
+ serialize() {
3985
+ return {
3986
+ key: this.constructor.key,
3987
+ params: this.params
3988
+ };
3989
+ }
3990
+ }
3991
+
3157
3992
  // view/queries/abstract-view-query.ts
3158
3993
  class ArcViewQuery extends ArcSerializableQuery {
3159
3994
  view;
@@ -3457,6 +4292,7 @@ class ArcView extends ArcContextElementWithStore {
3457
4292
  _elements;
3458
4293
  _handler;
3459
4294
  _isAsync = false;
4295
+ _version;
3460
4296
  restrictions;
3461
4297
  constructor(name, id3, schema) {
3462
4298
  super();
@@ -3464,8 +4300,84 @@ class ArcView extends ArcContextElementWithStore {
3464
4300
  this.id = id3;
3465
4301
  this.schema = schema;
3466
4302
  }
3467
- storeSchema = () => {
3468
- return arcObjectToStoreSchema(this.name, this.schema);
4303
+ databaseStoreSchema = () => {
4304
+ const systemFields = {
4305
+ _id: string().primaryKey()
4306
+ };
4307
+ const completeSchema = this.schema.merge(systemFields);
4308
+ return {
4309
+ tables: [
4310
+ {
4311
+ name: this.name,
4312
+ schema: completeSchema,
4313
+ version: this._version,
4314
+ options: {
4315
+ softDelete: true,
4316
+ versioning: true
4317
+ }
4318
+ }
4319
+ ],
4320
+ reinitTable: async (tableName, dataStorage) => {
4321
+ console.log(`Reinitializing view table: ${tableName} for view: ${this.name}`);
4322
+ if (this._handler && this._elements) {
4323
+ try {
4324
+ const eventStore = dataStorage.getStore("events");
4325
+ const viewStore = dataStorage.getStore(this.name);
4326
+ const events = await eventStore.find({
4327
+ where: {
4328
+ type: {
4329
+ $in: this._elements.filter((element3) => element3 instanceof ArcEvent).map((element3) => element3.name)
4330
+ }
4331
+ }
4332
+ });
4333
+ console.log(`Found ${events.length} events to replay for view ${this.name}`);
4334
+ const reinitContext = {
4335
+ set: async (id3, data) => {
4336
+ const parsed = this.schema.parse(data);
4337
+ const body = {
4338
+ _id: id3,
4339
+ lastUpdate: new Date().toISOString(),
4340
+ ...parsed
4341
+ };
4342
+ await viewStore.set(body);
4343
+ },
4344
+ modify: async (id3, data) => {
4345
+ await viewStore.modify(id3, {
4346
+ ...data,
4347
+ lastUpdate: new Date().toISOString()
4348
+ });
4349
+ },
4350
+ remove: async (id3) => {
4351
+ await viewStore.remove(id3);
4352
+ },
4353
+ find: async (options) => {
4354
+ return viewStore.find(options);
4355
+ },
4356
+ findOne: async (where) => {
4357
+ const result = await viewStore.find({ where, limit: 1 });
4358
+ return result[0];
4359
+ },
4360
+ $auth: {}
4361
+ };
4362
+ for (const event3 of events) {
4363
+ if (this._handler && this._handler[event3.type]) {
4364
+ try {
4365
+ await this._handler[event3.type](reinitContext, event3);
4366
+ } catch (error) {
4367
+ console.error(`Error processing event ${event3.type} for view ${this.name}:`, error);
4368
+ }
4369
+ }
4370
+ }
4371
+ console.log(`Successfully reinitialized view ${this.name} with ${events.length} events`);
4372
+ } catch (error) {
4373
+ console.error(`Failed to reinitialize view ${this.name}:`, error);
4374
+ throw error;
4375
+ }
4376
+ } else {
4377
+ console.log(`No handlers defined for view ${this.name}, skipping event replay`);
4378
+ }
4379
+ }
4380
+ };
3469
4381
  };
3470
4382
  auth(restrictionsFn) {
3471
4383
  const clone = this.clone();
@@ -3487,6 +4399,11 @@ class ArcView extends ArcContextElementWithStore {
3487
4399
  clone._isAsync = true;
3488
4400
  return clone;
3489
4401
  }
4402
+ version(version) {
4403
+ const clone = this.clone();
4404
+ clone._version = version;
4405
+ return clone;
4406
+ }
3490
4407
  handle(handler) {
3491
4408
  const clone = this.clone();
3492
4409
  clone._handler = handler;
@@ -3607,6 +4524,7 @@ class ArcView extends ArcContextElementWithStore {
3607
4524
  clone._elements = this._elements;
3608
4525
  clone._handler = this._handler;
3609
4526
  clone._isAsync = this._isAsync;
4527
+ clone._version = this._version;
3610
4528
  clone.restrictions = this.restrictions;
3611
4529
  return clone;
3612
4530
  }
@@ -3634,14 +4552,13 @@ export {
3634
4552
  date,
3635
4553
  customId,
3636
4554
  createSQLiteAdapterFactory,
4555
+ createPostgreSQLAdapterFactory,
3637
4556
  contextMerge,
3638
4557
  context,
3639
4558
  command,
3640
- collection,
3641
4559
  boolean,
3642
4560
  blob,
3643
4561
  array,
3644
- arcObjectToStoreSchema,
3645
4562
  any,
3646
4563
  StoreState,
3647
4564
  SQLiteAdapter,
@@ -3649,6 +4566,7 @@ export {
3649
4566
  QueryViewResult,
3650
4567
  QueryCache,
3651
4568
  QueryBuilderContext,
4569
+ PostgreSQLAdapter,
3652
4570
  ModelBase,
3653
4571
  Model,
3654
4572
  MasterStoreState,
@@ -3681,8 +4599,6 @@ export {
3681
4599
  ArcContextElement,
3682
4600
  ArcContext,
3683
4601
  ArcCommand,
3684
- ArcCollectionQuery,
3685
- ArcCollection,
3686
4602
  ArcBranded,
3687
4603
  ArcBoolean,
3688
4604
  ArcBlob,