@arcote.tech/arc 0.1.8 → 0.1.9

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 +1571 -657
  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
 
@@ -514,6 +434,20 @@ class ArcObject extends ArcAbstract {
514
434
  }
515
435
  return schema;
516
436
  }
437
+ getColumnData() {
438
+ const storeData = this.getStoreData();
439
+ return {
440
+ type: "object",
441
+ storeData: {
442
+ ...storeData,
443
+ isNullable: false
444
+ }
445
+ };
446
+ }
447
+ merge(otherShape) {
448
+ const mergedShape = { ...this.rawShape, ...otherShape };
449
+ return new ArcObject(mergedShape);
450
+ }
517
451
  }
518
452
  function object(element) {
519
453
  return new ArcObject(element);
@@ -613,10 +547,33 @@ class ArcArray extends ArcAbstract {
613
547
  }
614
548
  return schema;
615
549
  }
550
+ getColumnData() {
551
+ const storeData = this.getStoreData();
552
+ return {
553
+ type: "array",
554
+ storeData: {
555
+ ...storeData,
556
+ isNullable: false
557
+ }
558
+ };
559
+ }
616
560
  }
617
561
  function array(element) {
618
562
  return new ArcArray(element);
619
563
  }
564
+ // elements/abstract-primitive.ts
565
+ class ArcPrimitive extends ArcAbstract {
566
+ serialize(value) {
567
+ return value;
568
+ }
569
+ parse(value) {
570
+ return value;
571
+ }
572
+ deserialize(value) {
573
+ return value;
574
+ }
575
+ }
576
+
620
577
  // elements/blob.ts
621
578
  var blobValidator = {
622
579
  name: "blob",
@@ -715,8 +672,18 @@ class ArcBlob extends ArcPrimitive {
715
672
  }
716
673
  return schema;
717
674
  }
718
- }
719
- function blob() {
675
+ getColumnData() {
676
+ const storeData = this.getStoreData();
677
+ return {
678
+ type: "blob",
679
+ storeData: {
680
+ ...storeData,
681
+ isNullable: false
682
+ }
683
+ };
684
+ }
685
+ }
686
+ function blob() {
720
687
  return new ArcBlob;
721
688
  }
722
689
  // elements/boolean.ts
@@ -740,6 +707,16 @@ class ArcBoolean extends ArcPrimitive {
740
707
  }
741
708
  return schema;
742
709
  }
710
+ getColumnData() {
711
+ const storeData = this.getStoreData();
712
+ return {
713
+ type: "boolean",
714
+ storeData: {
715
+ ...storeData,
716
+ isNullable: false
717
+ }
718
+ };
719
+ }
743
720
  }
744
721
  function boolean() {
745
722
  return new ArcBoolean;
@@ -789,6 +766,16 @@ class ArcDate extends ArcAbstract {
789
766
  }
790
767
  return schema;
791
768
  }
769
+ getColumnData() {
770
+ const storeData = this.getStoreData();
771
+ return {
772
+ type: "date",
773
+ storeData: {
774
+ ...storeData,
775
+ isNullable: false
776
+ }
777
+ };
778
+ }
792
779
  }
793
780
  function date() {
794
781
  return new ArcDate;
@@ -967,10 +954,207 @@ class ArcFile extends ArcPrimitive {
967
954
  }
968
955
  return schema;
969
956
  }
957
+ getColumnData() {
958
+ const storeData = this.getStoreData();
959
+ return {
960
+ type: "blob",
961
+ storeData: {
962
+ ...storeData,
963
+ isNullable: false
964
+ }
965
+ };
966
+ }
970
967
  }
971
968
  function file() {
972
969
  return new ArcFile;
973
970
  }
971
+ // elements/string.ts
972
+ var stringValidator = typeValidatorBuilder("string");
973
+
974
+ class ArcString extends ArcPrimitive {
975
+ constructor() {
976
+ super([stringValidator]);
977
+ }
978
+ minLength(min) {
979
+ return this.validation("minLength", (value) => {
980
+ if (value.length < min)
981
+ return {
982
+ currentLength: value.length,
983
+ minLength: min
984
+ };
985
+ });
986
+ }
987
+ maxLength(max) {
988
+ return this.validation("maxLength", (value) => {
989
+ if (value.length > max)
990
+ return {
991
+ currentLength: value.length,
992
+ maxLength: max
993
+ };
994
+ });
995
+ }
996
+ length(number) {
997
+ return this.validation("length", (value) => {
998
+ if (value.length !== number) {
999
+ return {
1000
+ currentLength: value.length,
1001
+ length: number
1002
+ };
1003
+ }
1004
+ });
1005
+ }
1006
+ includes(str) {
1007
+ return this.validation("includes", (value) => {
1008
+ if (!value.includes(str)) {
1009
+ return {
1010
+ currentValue: value,
1011
+ includes: str
1012
+ };
1013
+ }
1014
+ });
1015
+ }
1016
+ startsWith(str) {
1017
+ return this.validation("startsWith", (value) => {
1018
+ if (!value.startsWith(str)) {
1019
+ return {
1020
+ currentValue: value,
1021
+ startsWith: str
1022
+ };
1023
+ }
1024
+ });
1025
+ }
1026
+ endsWith(str) {
1027
+ return this.validation("endsWith", (value) => {
1028
+ if (!value.endsWith(str)) {
1029
+ return {
1030
+ currentValue: value,
1031
+ endsWith: str
1032
+ };
1033
+ }
1034
+ });
1035
+ }
1036
+ regex(regex) {
1037
+ return this.validation("regex", (value) => {
1038
+ if (!regex.test(value)) {
1039
+ return {
1040
+ currentValue: value,
1041
+ regex
1042
+ };
1043
+ }
1044
+ });
1045
+ }
1046
+ email() {
1047
+ 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}\])$/;
1048
+ return this.validation("email", (value) => {
1049
+ if (!regex.test(value)) {
1050
+ return {
1051
+ currentEmail: value
1052
+ };
1053
+ }
1054
+ });
1055
+ }
1056
+ toJsonSchema() {
1057
+ const schema = { type: "string" };
1058
+ if (this._description) {
1059
+ schema.description = this._description;
1060
+ }
1061
+ return schema;
1062
+ }
1063
+ url() {
1064
+ 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-]*)?$/;
1065
+ return this.validation("url", (value) => {
1066
+ if (!regex.test(value)) {
1067
+ return {
1068
+ currentUrl: value
1069
+ };
1070
+ }
1071
+ });
1072
+ }
1073
+ ip() {
1074
+ 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]?)$/;
1075
+ 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})$/;
1076
+ return this.validation("ip", (value) => {
1077
+ if (!(IPv4regex.test(value) || IPv6regex.test(value))) {
1078
+ return {
1079
+ currentIP: value
1080
+ };
1081
+ }
1082
+ });
1083
+ }
1084
+ validation(name, validator) {
1085
+ const instance = this.pipeValidation(name, validator);
1086
+ return instance;
1087
+ }
1088
+ getColumnData() {
1089
+ const storeData = this.getStoreData();
1090
+ const validationInfo = {};
1091
+ for (const validation of this.validations) {
1092
+ switch (validation.name) {
1093
+ case "minLength":
1094
+ break;
1095
+ case "maxLength":
1096
+ break;
1097
+ case "regex":
1098
+ break;
1099
+ case "email":
1100
+ validationInfo.pattern = "email";
1101
+ break;
1102
+ case "url":
1103
+ validationInfo.pattern = "url";
1104
+ break;
1105
+ case "ip":
1106
+ validationInfo.pattern = "ip";
1107
+ break;
1108
+ }
1109
+ }
1110
+ return {
1111
+ type: "string",
1112
+ storeData: {
1113
+ ...storeData,
1114
+ isNullable: false
1115
+ },
1116
+ validationInfo
1117
+ };
1118
+ }
1119
+ }
1120
+ function string() {
1121
+ return new ArcString;
1122
+ }
1123
+
1124
+ // elements/id.ts
1125
+ class ArcCustomId extends ArcBranded {
1126
+ createFn;
1127
+ constructor(name, createFn) {
1128
+ super(string(), name);
1129
+ this.createFn = createFn;
1130
+ }
1131
+ get(...args) {
1132
+ return this.createFn(...args);
1133
+ }
1134
+ }
1135
+
1136
+ class ArcId extends ArcBranded {
1137
+ generateFn;
1138
+ constructor(name, generateFn) {
1139
+ super(string(), name);
1140
+ this.generateFn = generateFn;
1141
+ }
1142
+ generate() {
1143
+ if (this.generateFn) {
1144
+ return this.generateFn();
1145
+ }
1146
+ var timestamp = (new Date().getTime() / 1000 | 0).toString(16);
1147
+ return timestamp + "xxxxxxxxxxxxxxxx".replace(/[x]/g, function() {
1148
+ return (Math.random() * 16 | 0).toString(16);
1149
+ }).toLowerCase();
1150
+ }
1151
+ }
1152
+ function id(name, generateFn) {
1153
+ return new ArcId(name, generateFn);
1154
+ }
1155
+ function customId(name, createFn) {
1156
+ return new ArcCustomId(name, createFn);
1157
+ }
974
1158
  // elements/number.ts
975
1159
  var numberValidator = typeValidatorBuilder("number");
976
1160
 
@@ -1001,6 +1185,26 @@ class ArcNumber extends ArcPrimitive {
1001
1185
  }
1002
1186
  return schema;
1003
1187
  }
1188
+ getColumnData() {
1189
+ const storeData = this.getStoreData();
1190
+ const validationInfo = {};
1191
+ for (const validation of this.validations) {
1192
+ switch (validation.name) {
1193
+ case "min":
1194
+ break;
1195
+ case "max":
1196
+ break;
1197
+ }
1198
+ }
1199
+ return {
1200
+ type: "number",
1201
+ storeData: {
1202
+ ...storeData,
1203
+ isNullable: false
1204
+ },
1205
+ validationInfo
1206
+ };
1207
+ }
1004
1208
  }
1005
1209
  function number() {
1006
1210
  return new ArcNumber;
@@ -1039,6 +1243,23 @@ class ArcOr {
1039
1243
  anyOf: this.elements.map((el) => el.toJsonSchema?.() ?? {})
1040
1244
  };
1041
1245
  }
1246
+ getColumnData() {
1247
+ return {
1248
+ type: "object",
1249
+ storeData: {
1250
+ isNullable: false
1251
+ },
1252
+ validationInfo: {
1253
+ unionTypes: this.elements.map((el) => {
1254
+ try {
1255
+ return el.getColumnData?.()?.type || "unknown";
1256
+ } catch {
1257
+ return "unknown";
1258
+ }
1259
+ })
1260
+ }
1261
+ };
1262
+ }
1042
1263
  pickElement(value) {
1043
1264
  for (const element of this.elements) {
1044
1265
  if (this.isTypeOf(element, value))
@@ -1118,6 +1339,16 @@ class ArcRecord extends ArcAbstract {
1118
1339
  }
1119
1340
  return schema;
1120
1341
  }
1342
+ getColumnData() {
1343
+ const storeData = this.getStoreData();
1344
+ return {
1345
+ type: "record",
1346
+ storeData: {
1347
+ ...storeData,
1348
+ isNullable: false
1349
+ }
1350
+ };
1351
+ }
1121
1352
  }
1122
1353
  function record(key, element) {
1123
1354
  return new ArcRecord(key, element);
@@ -1160,368 +1391,50 @@ class ArcStringEnum extends ArcAbstract {
1160
1391
  getEnumerators() {
1161
1392
  return this.values;
1162
1393
  }
1394
+ getColumnData() {
1395
+ const storeData = this.getStoreData();
1396
+ return {
1397
+ type: "stringEnum",
1398
+ storeData: {
1399
+ ...storeData,
1400
+ isNullable: false
1401
+ },
1402
+ validationInfo: {
1403
+ enumValues: this.values
1404
+ }
1405
+ };
1406
+ }
1163
1407
  }
1164
1408
  function stringEnum(...values) {
1165
1409
  return new ArcStringEnum(values);
1166
1410
  }
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);
1411
+ // command/command.ts
1412
+ class ArcCommand extends ArcContextElement {
1413
+ name;
1414
+ _description;
1415
+ _params;
1416
+ _results;
1417
+ _elements;
1418
+ _handler;
1419
+ _isPublic = false;
1420
+ constructor(name) {
1421
+ super();
1422
+ this.name = name;
1220
1423
  }
1221
- unsubscribe(listener) {
1222
- this.listeners.delete(listener);
1424
+ use(elements) {
1425
+ const clone = this.clone();
1426
+ clone._elements = elements;
1427
+ return clone;
1223
1428
  }
1224
- nextResult(result) {
1225
- this.lastResult = result;
1226
- this.listeners.forEach((listener) => listener(result));
1429
+ description(description) {
1430
+ const clone = this.clone();
1431
+ clone._description = description;
1432
+ return clone;
1227
1433
  }
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;
1434
+ public() {
1435
+ const clone = this.clone();
1436
+ clone._isPublic = true;
1437
+ return clone;
1525
1438
  }
1526
1439
  get isPublic() {
1527
1440
  return this._isPublic;
@@ -1590,15 +1503,19 @@ function command(name) {
1590
1503
  // context/context.ts
1591
1504
  class ArcContext {
1592
1505
  elements;
1506
+ elementsSet = new Set;
1593
1507
  constructor(elements) {
1594
1508
  this.elements = elements;
1509
+ this.elements.forEach((element2) => {
1510
+ this.elementsSet.add(element2);
1511
+ });
1595
1512
  }
1596
1513
  get(name) {
1597
1514
  return this.elements.find((element2) => element2.name === name);
1598
1515
  }
1599
1516
  getSyncListeners(eventType, authContext) {
1600
1517
  const listeners = [];
1601
- this.elements.forEach((element2) => {
1518
+ this.elementsSet.forEach((element2) => {
1602
1519
  if (element2.observer) {
1603
1520
  const handlers = element2.observer(authContext);
1604
1521
  const config = handlers[eventType];
@@ -1611,7 +1528,7 @@ class ArcContext {
1611
1528
  }
1612
1529
  getAsyncListeners(eventType, authContext) {
1613
1530
  const listeners = [];
1614
- this.elements.forEach((element2) => {
1531
+ this.elementsSet.forEach((element2) => {
1615
1532
  if (element2.observer) {
1616
1533
  const handlers = element2.observer(authContext);
1617
1534
  const config = handlers[eventType];
@@ -1682,6 +1599,28 @@ function contextMerge(...contexts) {
1682
1599
  }
1683
1600
  // context/event.ts
1684
1601
  var eventValidator = typeValidatorBuilder("object");
1602
+ var eventSchema = new ArcObject({
1603
+ id: string().primaryKey(),
1604
+ type: string(),
1605
+ payload: any(),
1606
+ createdAt: date()
1607
+ });
1608
+ var sharedEventDatabaseSchema = null;
1609
+ function getSharedEventDatabaseSchema() {
1610
+ if (!sharedEventDatabaseSchema) {
1611
+ sharedEventDatabaseSchema = {
1612
+ tables: [{
1613
+ name: "events",
1614
+ schema: eventSchema,
1615
+ options: {
1616
+ softDelete: false,
1617
+ versioning: true
1618
+ }
1619
+ }]
1620
+ };
1621
+ }
1622
+ return sharedEventDatabaseSchema;
1623
+ }
1685
1624
  var eventStoreSchema = {
1686
1625
  tables: [
1687
1626
  {
@@ -1715,6 +1654,7 @@ class ArcEvent extends ArcContextElementWithStore {
1715
1654
  payload;
1716
1655
  _restrictions;
1717
1656
  storeSchema = () => eventStoreSchema;
1657
+ databaseStoreSchema = () => getSharedEventDatabaseSchema();
1718
1658
  constructor(name, payload) {
1719
1659
  super();
1720
1660
  this.name = name;
@@ -1760,6 +1700,21 @@ class ArcEvent extends ArcContextElementWithStore {
1760
1700
  function event(name, payload) {
1761
1701
  return new ArcEvent(name, payload ? payload instanceof ArcObject ? payload : new ArcObject(payload) : undefined);
1762
1702
  }
1703
+ // context/query.ts
1704
+ class ArcQuery {
1705
+ lastResult;
1706
+ listeners = new Set;
1707
+ subscribe(listener) {
1708
+ this.listeners.add(listener);
1709
+ }
1710
+ unsubscribe(listener) {
1711
+ this.listeners.delete(listener);
1712
+ }
1713
+ nextResult(result) {
1714
+ this.lastResult = result;
1715
+ this.listeners.forEach((listener) => listener(result));
1716
+ }
1717
+ }
1763
1718
  // context/query-builder-context.ts
1764
1719
  class QueryBuilderContext {
1765
1720
  queryCache;
@@ -2228,107 +2183,729 @@ class MasterStoreState extends StoreState {
2228
2183
  item,
2229
2184
  id: change.id
2230
2185
  }
2231
- };
2186
+ };
2187
+ }
2188
+ throw new Error("Unknown change type");
2189
+ }
2190
+ async applyChange(change) {
2191
+ const transaction = await this.dataStorage.getReadWriteTransaction();
2192
+ const { event: event3, from, to } = await this.applyChangeAndReturnEvent(transaction, change);
2193
+ await transaction.commit();
2194
+ this.notifyListeners([event3]);
2195
+ return { from, to };
2196
+ }
2197
+ async applyChanges(changes) {
2198
+ const transaction = await this.dataStorage.getReadWriteTransaction();
2199
+ const events = [];
2200
+ for (const change of changes) {
2201
+ const { event: event3 } = await this.applyChangeAndReturnEvent(transaction, change);
2202
+ if (event3)
2203
+ events.push(event3);
2204
+ }
2205
+ await transaction.commit();
2206
+ if (events.length > 0) {
2207
+ this.notifyListeners(events);
2208
+ }
2209
+ }
2210
+ async find(options, listener) {
2211
+ if (listener) {
2212
+ this.listeners.set(listener, { callback: listener });
2213
+ }
2214
+ const transaction = await this.dataStorage.getReadTransaction();
2215
+ const results = await transaction.find(this.storeName, options);
2216
+ return results.map((item) => this.deserialize ? this.deserialize(item) : item);
2217
+ }
2218
+ }
2219
+
2220
+ // data-storage/data-storage-master.ts
2221
+ class MasterDataStorage extends DataStorage {
2222
+ dbAdapter;
2223
+ arcContext;
2224
+ stores = new Map;
2225
+ rtcAdapter;
2226
+ reinitTablesExecuted = false;
2227
+ constructor(dbAdapter, rtcAdapterFactory, arcContext) {
2228
+ super();
2229
+ this.dbAdapter = dbAdapter;
2230
+ this.arcContext = arcContext;
2231
+ this.rtcAdapter = rtcAdapterFactory(this);
2232
+ this.executeReinitTablesOnReady();
2233
+ }
2234
+ async executeReinitTablesOnReady() {
2235
+ try {
2236
+ const adapter = await this.dbAdapter;
2237
+ if (!this.reinitTablesExecuted) {
2238
+ await adapter.executeReinitTables(this);
2239
+ this.reinitTablesExecuted = true;
2240
+ }
2241
+ } catch (error) {
2242
+ console.error("Failed to execute reinitTable functions:", error);
2243
+ }
2244
+ }
2245
+ async getReadTransaction() {
2246
+ return (await this.dbAdapter).readTransaction();
2247
+ }
2248
+ async getReadWriteTransaction() {
2249
+ return (await this.dbAdapter).readWriteTransaction();
2250
+ }
2251
+ getStore(storeName) {
2252
+ if (!this.stores.has(storeName)) {
2253
+ this.stores.set(storeName, new MasterStoreState(storeName, this));
2254
+ }
2255
+ return this.stores.get(storeName);
2256
+ }
2257
+ async applyChanges(changes) {
2258
+ return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
2259
+ }
2260
+ applySerializedChanges(changes) {
2261
+ return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applySerializedChanges(changes2)));
2262
+ }
2263
+ async commitChanges(changes) {
2264
+ await Promise.all([
2265
+ this.applyChanges(changes),
2266
+ this.rtcAdapter.commitChanges(changes)
2267
+ ]);
2268
+ }
2269
+ fork() {
2270
+ return new ForkedDataStorage(this);
2271
+ }
2272
+ async sync(progressCallback) {
2273
+ await this.rtcAdapter.sync(progressCallback);
2274
+ }
2275
+ }
2276
+ // database/schema-extraction.ts
2277
+ function extractDatabaseAgnosticSchema(arcObject, tableName) {
2278
+ const columns = [];
2279
+ for (const [fieldName, fieldSchema] of arcObject.entries()) {
2280
+ const arcElement = fieldSchema;
2281
+ if (typeof arcElement.getColumnData !== "function") {
2282
+ throw new Error(`Element for field '${fieldName}' does not implement getColumnData() method. Element type: ${arcElement.constructor.name}`);
2283
+ }
2284
+ const columnInfo = arcElement.getColumnData();
2285
+ const fullColumnInfo = {
2286
+ name: fieldName,
2287
+ ...columnInfo
2288
+ };
2289
+ columns.push(fullColumnInfo);
2290
+ }
2291
+ return {
2292
+ tableName,
2293
+ columns
2294
+ };
2295
+ }
2296
+
2297
+ // db/postgresAdapter.ts
2298
+ class PostgreSQLReadTransaction {
2299
+ db;
2300
+ tables;
2301
+ adapter;
2302
+ constructor(db, tables, adapter) {
2303
+ this.db = db;
2304
+ this.tables = tables;
2305
+ this.adapter = adapter;
2306
+ }
2307
+ hasSoftDelete(tableName) {
2308
+ if (this.adapter) {
2309
+ return this.adapter.hasSoftDelete(tableName);
2310
+ }
2311
+ const table = this.tables.get(tableName);
2312
+ if (!table)
2313
+ return false;
2314
+ return table.columns.some((col) => col.name === "deleted");
2315
+ }
2316
+ deserializeValue(value, column) {
2317
+ if (value === null || value === undefined)
2318
+ return null;
2319
+ switch (column.type.toLowerCase()) {
2320
+ case "json":
2321
+ case "jsonb":
2322
+ if (typeof value === "string") {
2323
+ try {
2324
+ return JSON.parse(value);
2325
+ } catch {
2326
+ return value;
2327
+ }
2328
+ }
2329
+ return value;
2330
+ case "text":
2331
+ if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) {
2332
+ try {
2333
+ const parsed = JSON.parse(value);
2334
+ if (typeof parsed === "object" || Array.isArray(parsed)) {
2335
+ return parsed;
2336
+ }
2337
+ } catch {}
2338
+ }
2339
+ return value;
2340
+ case "datetime":
2341
+ case "timestamp":
2342
+ return new Date(value);
2343
+ default:
2344
+ return value;
2345
+ }
2346
+ }
2347
+ deserializeRow(row, table) {
2348
+ const result = {};
2349
+ for (const column of table.columns) {
2350
+ const value = row[column.name];
2351
+ result[column.name] = this.deserializeValue(value, column);
2352
+ }
2353
+ return result;
2354
+ }
2355
+ getId(store, id3) {
2356
+ return id3;
2357
+ }
2358
+ buildWhereClause(where, tableName) {
2359
+ const conditions = [];
2360
+ const params = [];
2361
+ let paramIndex = 1;
2362
+ if (tableName && this.hasSoftDelete(tableName)) {
2363
+ conditions.push('"deleted" = $1');
2364
+ params.push(false);
2365
+ paramIndex = 2;
2366
+ }
2367
+ if (!where) {
2368
+ return {
2369
+ sql: conditions.length > 0 ? conditions.join(" AND ") : "1=1",
2370
+ params
2371
+ };
2372
+ }
2373
+ Object.entries(where).forEach(([key, value]) => {
2374
+ if (typeof value === "object" && value !== null) {
2375
+ Object.entries(value).forEach(([operator, operand]) => {
2376
+ switch (operator) {
2377
+ case "$eq":
2378
+ case "$ne":
2379
+ case "$gt":
2380
+ case "$gte":
2381
+ case "$lt":
2382
+ case "$lte":
2383
+ conditions.push(`"${key}" ${this.getOperatorSymbol(operator)} $${paramIndex}`);
2384
+ params.push(operand);
2385
+ paramIndex++;
2386
+ break;
2387
+ case "$in":
2388
+ case "$nin":
2389
+ if (Array.isArray(operand)) {
2390
+ const placeholders = operand.map(() => `$${paramIndex++}`).join(", ");
2391
+ conditions.push(`"${key}" ${operator === "$in" ? "IN" : "NOT IN"} (${placeholders})`);
2392
+ params.push(...operand);
2393
+ }
2394
+ break;
2395
+ case "$exists":
2396
+ if (typeof operand === "boolean") {
2397
+ conditions.push(operand ? `"${key}" IS NOT NULL` : `"${key}" IS NULL`);
2398
+ }
2399
+ break;
2400
+ }
2401
+ });
2402
+ } else {
2403
+ conditions.push(`"${key}" = $${paramIndex}`);
2404
+ params.push(value);
2405
+ paramIndex++;
2406
+ }
2407
+ });
2408
+ return {
2409
+ sql: conditions.join(" AND "),
2410
+ params
2411
+ };
2412
+ }
2413
+ getOperatorSymbol(operator) {
2414
+ const operators = {
2415
+ $eq: "=",
2416
+ $ne: "!=",
2417
+ $gt: ">",
2418
+ $gte: ">=",
2419
+ $lt: "<",
2420
+ $lte: "<="
2421
+ };
2422
+ return operators[operator] || "=";
2423
+ }
2424
+ buildOrderByClause(orderBy) {
2425
+ if (!orderBy)
2426
+ return "";
2427
+ const orderClauses = Object.entries(orderBy).map(([key, direction]) => `"${key}" ${direction.toUpperCase()}`).join(", ");
2428
+ return orderClauses ? `ORDER BY ${orderClauses}` : "";
2429
+ }
2430
+ async find(store, options) {
2431
+ const { where, limit, offset, orderBy } = options || {};
2432
+ const whereClause = this.buildWhereClause(where, store);
2433
+ const orderByClause = this.buildOrderByClause(orderBy);
2434
+ const table = this.tables.get(store);
2435
+ if (!table) {
2436
+ throw new Error(`Store ${store} not found`);
2437
+ }
2438
+ let query2 = `
2439
+ SELECT *
2440
+ FROM "${store}"
2441
+ WHERE ${whereClause.sql}
2442
+ ${orderByClause}
2443
+ `;
2444
+ let params = whereClause.params;
2445
+ let paramIndex = params.length + 1;
2446
+ if (limit) {
2447
+ query2 += ` LIMIT $${paramIndex}`;
2448
+ params.push(limit);
2449
+ paramIndex++;
2450
+ }
2451
+ if (offset) {
2452
+ query2 += ` OFFSET $${paramIndex}`;
2453
+ params.push(offset);
2454
+ }
2455
+ const rows = await this.db.exec(query2, params);
2456
+ return rows.map((row) => this.deserializeRow(row, table));
2457
+ }
2458
+ }
2459
+
2460
+ class PostgreSQLReadWriteTransaction extends PostgreSQLReadTransaction {
2461
+ adapter;
2462
+ queries = [];
2463
+ constructor(db, tables, adapter) {
2464
+ super(db, tables);
2465
+ this.adapter = adapter;
2466
+ }
2467
+ async remove(store, id3) {
2468
+ const table = this.tables.get(store);
2469
+ if (!table) {
2470
+ throw new Error(`Store ${store} not found`);
2471
+ }
2472
+ const query2 = `UPDATE "${store}" SET "deleted" = $1, "lastUpdate" = $2 WHERE "${table.primaryKey}" = $3`;
2473
+ this.queries.push({
2474
+ sql: query2,
2475
+ params: [true, new Date().toISOString(), id3]
2476
+ });
2477
+ }
2478
+ async set(store, item) {
2479
+ const table = this.tables.get(store);
2480
+ if (!table) {
2481
+ throw new Error(`Store ${store} not found`);
2482
+ }
2483
+ const hasVersioning = this.adapter.hasVersioning(store);
2484
+ if (hasVersioning) {
2485
+ await this.setWithVersioning(store, item, table);
2486
+ } else {
2487
+ await this.setWithoutVersioning(store, item, table);
2488
+ }
2489
+ }
2490
+ async setWithoutVersioning(store, item, table) {
2491
+ const columnNames = table.columns.map((col) => col.name);
2492
+ const values = table.columns.map((column) => {
2493
+ let value = item[column.name];
2494
+ if (value === undefined && column.default !== undefined) {
2495
+ value = column.default;
2496
+ }
2497
+ return this.serializeValue(value, column);
2498
+ });
2499
+ const placeholders = columnNames.map((_, i) => `$${i + 1}`).join(", ");
2500
+ const updateColumns = columnNames.filter((col) => col !== table.primaryKey).map((col) => `"${col}" = EXCLUDED."${col}"`).join(", ");
2501
+ if (store === "events") {
2502
+ const simpleInsertSql = `
2503
+ INSERT INTO "${table.name}"
2504
+ (${columnNames.map((c) => `"${c}"`).join(", ")})
2505
+ VALUES (${placeholders})
2506
+ `;
2507
+ this.queries.push({
2508
+ sql: simpleInsertSql,
2509
+ params: values
2510
+ });
2511
+ } else {
2512
+ const sql = `
2513
+ INSERT INTO "${table.name}"
2514
+ (${columnNames.map((c) => `"${c}"`).join(", ")})
2515
+ VALUES (${placeholders})
2516
+ ON CONFLICT ("${table.primaryKey}")
2517
+ DO UPDATE SET ${updateColumns}
2518
+ `;
2519
+ this.queries.push({
2520
+ sql,
2521
+ params: values
2522
+ });
2523
+ }
2524
+ }
2525
+ async setWithVersioning(store, item, table) {
2526
+ const regularColumns = table.columns.filter((col) => col.name !== "__version");
2527
+ const columnNames = regularColumns.map((col) => col.name);
2528
+ const values = regularColumns.map((column) => {
2529
+ let value = item[column.name];
2530
+ if (value === undefined && column.default !== undefined) {
2531
+ value = column.default;
2532
+ }
2533
+ return this.serializeValue(value, column);
2534
+ });
2535
+ columnNames.push("__version");
2536
+ const placeholders = regularColumns.map((_, i) => `$${i + 1}`).join(", ");
2537
+ const updateColumns = regularColumns.filter((col) => col.name !== table.primaryKey).map((col) => `"${col.name}" = EXCLUDED."${col.name}"`).join(", ");
2538
+ const sql = `
2539
+ WITH next_version AS (
2540
+ INSERT INTO __arc_version_counters (table_name, last_version)
2541
+ VALUES ($${values.length + 1}, 1)
2542
+ ON CONFLICT(table_name)
2543
+ DO UPDATE SET last_version = __arc_version_counters.last_version + 1
2544
+ RETURNING last_version
2545
+ ),
2546
+ upsert AS (
2547
+ INSERT INTO "${table.name}"
2548
+ (${columnNames.map((c) => `"${c}"`).join(", ")})
2549
+ VALUES (${placeholders}, (SELECT last_version FROM next_version))
2550
+ ON CONFLICT ("${table.primaryKey}")
2551
+ DO UPDATE SET ${updateColumns}${updateColumns ? ", " : ""}"__version" = (SELECT last_version FROM next_version)
2552
+ RETURNING *
2553
+ )
2554
+ SELECT * FROM upsert
2555
+ `;
2556
+ this.queries.push({
2557
+ sql,
2558
+ params: [...values, store]
2559
+ });
2560
+ }
2561
+ async commit() {
2562
+ if (this.queries.length === 0) {
2563
+ return Promise.resolve();
2564
+ }
2565
+ try {
2566
+ await this.db.execBatch(this.queries);
2567
+ this.queries = [];
2568
+ } catch (error) {
2569
+ this.queries = [];
2570
+ throw error;
2571
+ }
2572
+ }
2573
+ serializeValue(value, column) {
2574
+ if (value === null || value === undefined)
2575
+ return null;
2576
+ switch (column.type.toLowerCase()) {
2577
+ case "timestamp":
2578
+ case "datetime":
2579
+ if (value instanceof Date) {
2580
+ return value.toISOString();
2581
+ }
2582
+ if (typeof value === "number") {
2583
+ const date3 = value > 10000000000 ? new Date(value) : new Date(value * 1000);
2584
+ return date3.toISOString();
2585
+ }
2586
+ if (typeof value === "string") {
2587
+ const date3 = new Date(value);
2588
+ if (!isNaN(date3.getTime())) {
2589
+ return date3.toISOString();
2590
+ }
2591
+ }
2592
+ return value;
2593
+ case "json":
2594
+ case "jsonb":
2595
+ return JSON.stringify(value);
2596
+ default:
2597
+ if (value instanceof Date) {
2598
+ return value.toISOString();
2599
+ }
2600
+ if (Array.isArray(value) || typeof value === "object") {
2601
+ return JSON.stringify(value);
2602
+ }
2603
+ return value;
2232
2604
  }
2233
- throw new Error("Unknown change type");
2234
2605
  }
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 };
2606
+ }
2607
+
2608
+ class PostgreSQLAdapter {
2609
+ db;
2610
+ context;
2611
+ tables = new Map;
2612
+ tableSchemas = new Map;
2613
+ pendingReinitTables = [];
2614
+ mapType(arcType, storeData) {
2615
+ if (storeData?.databaseType?.postgresql) {
2616
+ return storeData.databaseType.postgresql;
2617
+ }
2618
+ switch (arcType) {
2619
+ case "string":
2620
+ case "stringEnum":
2621
+ return "TEXT";
2622
+ case "id":
2623
+ case "customId":
2624
+ return storeData?.databaseType?.postgresql || "TEXT";
2625
+ case "number":
2626
+ return storeData?.isAutoIncrement ? "SERIAL" : "INTEGER";
2627
+ case "boolean":
2628
+ return "BOOLEAN";
2629
+ case "date":
2630
+ return "TIMESTAMP";
2631
+ case "object":
2632
+ case "array":
2633
+ case "record":
2634
+ return "JSONB";
2635
+ case "blob":
2636
+ return "BYTEA";
2637
+ default:
2638
+ return "TEXT";
2639
+ }
2241
2640
  }
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);
2641
+ buildConstraints(storeData) {
2642
+ const constraints = [];
2643
+ if (storeData?.isPrimaryKey) {
2644
+ constraints.push("PRIMARY KEY");
2249
2645
  }
2250
- await transaction.commit();
2251
- if (events.length > 0) {
2252
- this.notifyListeners(events);
2646
+ if (storeData?.isUnique) {
2647
+ constraints.push("UNIQUE");
2648
+ }
2649
+ if (storeData?.foreignKey) {
2650
+ const { table, column, onDelete, onUpdate } = storeData.foreignKey;
2651
+ let fkConstraint = `REFERENCES ${table}(${column})`;
2652
+ if (onDelete)
2653
+ fkConstraint += ` ON DELETE ${onDelete}`;
2654
+ if (onUpdate)
2655
+ fkConstraint += ` ON UPDATE ${onUpdate}`;
2656
+ constraints.push(fkConstraint);
2253
2657
  }
2658
+ return constraints;
2254
2659
  }
2255
- async find(options, listener) {
2256
- if (listener) {
2257
- this.listeners.set(listener, { callback: listener });
2660
+ generateColumnSQL(columnInfo) {
2661
+ const type = this.mapType(columnInfo.type, columnInfo.storeData);
2662
+ const constraints = [];
2663
+ if (!columnInfo.storeData?.isNullable) {
2664
+ constraints.push("NOT NULL");
2258
2665
  }
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);
2666
+ constraints.push(...this.buildConstraints(columnInfo.storeData));
2667
+ if (columnInfo.defaultValue !== undefined) {
2668
+ if (type === "UUID" && columnInfo.defaultValue === "auto") {
2669
+ constraints.push("DEFAULT gen_random_uuid()");
2670
+ } else if (typeof columnInfo.defaultValue === "string") {
2671
+ constraints.push(`DEFAULT '${columnInfo.defaultValue}'`);
2672
+ } else {
2673
+ constraints.push(`DEFAULT ${columnInfo.defaultValue}`);
2674
+ }
2675
+ }
2676
+ return `"${columnInfo.name}" ${type} ${constraints.join(" ")}`.trim();
2677
+ }
2678
+ generateCreateTableSQL(tableName, columns) {
2679
+ const columnDefinitions = columns.map((col) => this.generateColumnSQL(col));
2680
+ 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}");`);
2681
+ let sql = `CREATE TABLE IF NOT EXISTS "${tableName}" (
2682
+ ${columnDefinitions.join(`,
2683
+ `)}
2684
+ )`;
2685
+ if (indexes.length > 0) {
2686
+ sql += `;
2687
+ ` + indexes.join(`
2688
+ `);
2689
+ }
2690
+ return sql;
2262
2691
  }
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);
2692
+ constructor(db, context3) {
2693
+ this.db = db;
2694
+ this.context = context3;
2695
+ this.context.elements.forEach((element3) => {
2696
+ if ("databaseStoreSchema" in element3 && typeof element3.databaseStoreSchema === "function") {
2697
+ const databaseSchema = element3.databaseStoreSchema();
2698
+ databaseSchema.tables.forEach((dbTable) => {
2699
+ const agnosticSchema = extractDatabaseAgnosticSchema(dbTable.schema, dbTable.name);
2700
+ const columns = agnosticSchema.columns.map((columnInfo) => ({
2701
+ name: columnInfo.name,
2702
+ type: this.mapType(columnInfo.type, columnInfo.storeData),
2703
+ constraints: this.buildConstraints(columnInfo.storeData),
2704
+ isNullable: columnInfo.storeData?.isNullable || false,
2705
+ defaultValue: columnInfo.defaultValue,
2706
+ isPrimaryKey: columnInfo.storeData?.isPrimaryKey || false,
2707
+ isAutoIncrement: columnInfo.storeData?.isAutoIncrement || false,
2708
+ isUnique: columnInfo.storeData?.isUnique || false,
2709
+ hasIndex: columnInfo.storeData?.hasIndex || false,
2710
+ foreignKey: columnInfo.storeData?.foreignKey
2711
+ }));
2712
+ this.tableSchemas.set(dbTable.name, columns);
2713
+ const legacyTable = {
2714
+ name: dbTable.name,
2715
+ primaryKey: columns.find((col) => col.isPrimaryKey)?.name || "_id",
2716
+ columns: columns.map((col) => ({
2717
+ name: col.name,
2718
+ type: col.type,
2719
+ isOptional: col.isNullable,
2720
+ default: col.defaultValue
2721
+ }))
2722
+ };
2723
+ this.tables.set(dbTable.name, legacyTable);
2724
+ });
2725
+ }
2726
+ });
2276
2727
  }
2277
- async getReadTransaction() {
2278
- return (await this.dbAdapter).readTransaction();
2728
+ async initialize() {
2729
+ await this.createVersionCounterTable();
2730
+ await this.createTableVersionsTable();
2731
+ const processedSchemas = new Set;
2732
+ const processedTables = new Set;
2733
+ for (const element3 of this.context.elements) {
2734
+ if ("databaseStoreSchema" in element3 && typeof element3.databaseStoreSchema === "function") {
2735
+ const databaseSchema = element3.databaseStoreSchema();
2736
+ if (processedSchemas.has(databaseSchema)) {
2737
+ continue;
2738
+ }
2739
+ processedSchemas.add(databaseSchema);
2740
+ for (const dbTable of databaseSchema.tables) {
2741
+ const tableKey = dbTable.version ? `${dbTable.name}_v${dbTable.version}` : dbTable.name;
2742
+ if (!processedTables.has(tableKey)) {
2743
+ const agnosticSchema = extractDatabaseAgnosticSchema(dbTable.schema, dbTable.name);
2744
+ let allColumns = [...agnosticSchema.columns];
2745
+ if (dbTable.options?.versioning) {
2746
+ allColumns.push({
2747
+ name: "__version",
2748
+ type: "number",
2749
+ storeData: { isNullable: false, hasIndex: true },
2750
+ defaultValue: 1
2751
+ });
2752
+ }
2753
+ if (dbTable.options?.softDelete) {
2754
+ allColumns.push({
2755
+ name: "deleted",
2756
+ type: "boolean",
2757
+ storeData: { isNullable: false, hasIndex: true },
2758
+ defaultValue: false
2759
+ });
2760
+ }
2761
+ const physicalTableName = this.getPhysicalTableName(dbTable.name, dbTable.version);
2762
+ if (dbTable.version && await this.checkVersionedTableExists(dbTable.name, dbTable.version)) {
2763
+ console.log(`Versioned table ${physicalTableName} already exists, skipping creation`);
2764
+ } else {
2765
+ await this.createTableIfNotExistsNew(physicalTableName, allColumns);
2766
+ if (dbTable.version) {
2767
+ await this.registerTableVersion(dbTable.name, dbTable.version, physicalTableName);
2768
+ if (databaseSchema.reinitTable) {
2769
+ this.pendingReinitTables.push({
2770
+ tableName: physicalTableName,
2771
+ reinitFn: databaseSchema.reinitTable
2772
+ });
2773
+ }
2774
+ }
2775
+ }
2776
+ const legacyTable = {
2777
+ name: physicalTableName,
2778
+ primaryKey: allColumns.find((col) => col.storeData?.isPrimaryKey)?.name || "_id",
2779
+ columns: allColumns.map((col) => ({
2780
+ name: col.name,
2781
+ type: this.mapType(col.type, col.storeData),
2782
+ isOptional: col.storeData?.isNullable || false,
2783
+ default: col.defaultValue
2784
+ }))
2785
+ };
2786
+ this.tables.set(dbTable.name, legacyTable);
2787
+ processedTables.add(tableKey);
2788
+ }
2789
+ }
2790
+ }
2791
+ }
2279
2792
  }
2280
- async getReadWriteTransaction() {
2281
- return (await this.dbAdapter).readWriteTransaction();
2793
+ async createTableIfNotExistsNew(tableName, columns) {
2794
+ const createTableSQL = this.generateCreateTableSQL(tableName, columns);
2795
+ await this.db.exec(createTableSQL);
2282
2796
  }
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);
2797
+ async createVersionCounterTable() {
2798
+ const sql = `
2799
+ CREATE TABLE IF NOT EXISTS __arc_version_counters (
2800
+ table_name TEXT PRIMARY KEY,
2801
+ last_version BIGINT NOT NULL DEFAULT 0
2802
+ )
2803
+ `;
2804
+ await this.db.exec(sql);
2288
2805
  }
2289
- async applyChanges(changes) {
2290
- return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applyChanges(changes2)));
2806
+ async createTableVersionsTable() {
2807
+ const sql = `
2808
+ CREATE TABLE IF NOT EXISTS __arc_table_versions (
2809
+ table_name TEXT NOT NULL,
2810
+ version INTEGER NOT NULL,
2811
+ physical_table_name TEXT NOT NULL,
2812
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
2813
+ is_active BOOLEAN DEFAULT FALSE,
2814
+ PRIMARY KEY (table_name, version)
2815
+ )
2816
+ `;
2817
+ await this.db.exec(sql);
2291
2818
  }
2292
- applySerializedChanges(changes) {
2293
- return Promise.all(changes.map(({ store, changes: changes2 }) => this.getStore(store).applySerializedChanges(changes2)));
2819
+ getPhysicalTableName(logicalName, version) {
2820
+ return version ? `${logicalName}_v${version}` : logicalName;
2294
2821
  }
2295
- async commitChanges(changes) {
2296
- await Promise.all([
2297
- this.applyChanges(changes),
2298
- this.rtcAdapter.commitChanges(changes)
2299
- ]);
2822
+ async checkVersionedTableExists(logicalName, version) {
2823
+ const result = await this.db.exec("SELECT COUNT(*) as count FROM __arc_table_versions WHERE table_name = $1 AND version = $2", [logicalName, version]);
2824
+ return result[0]?.count > 0;
2300
2825
  }
2301
- fork() {
2302
- return new ForkedDataStorage(this);
2826
+ async registerTableVersion(logicalName, version, physicalName) {
2827
+ 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
2828
  }
2304
- async sync(progressCallback) {
2305
- await this.rtcAdapter.sync(progressCallback);
2829
+ hasVersioning(tableName) {
2830
+ const table = this.tables.get(tableName);
2831
+ if (!table)
2832
+ return false;
2833
+ return table.columns.some((col) => col.name === "__version");
2834
+ }
2835
+ hasSoftDelete(tableName) {
2836
+ const table = this.tables.get(tableName);
2837
+ if (!table)
2838
+ return false;
2839
+ return table.columns.some((col) => col.name === "deleted");
2840
+ }
2841
+ async executeReinitTables(dataStorage) {
2842
+ for (const { tableName, reinitFn } of this.pendingReinitTables) {
2843
+ try {
2844
+ await reinitFn(tableName, dataStorage);
2845
+ console.log(`Successfully reinitialized table: ${tableName}`);
2846
+ } catch (error) {
2847
+ console.error(`Failed to reinitialize table ${tableName}:`, error);
2848
+ throw error;
2849
+ }
2850
+ }
2851
+ this.pendingReinitTables = [];
2852
+ }
2853
+ readWriteTransaction(stores) {
2854
+ return new PostgreSQLReadWriteTransaction(this.db, this.tables, this);
2855
+ }
2856
+ readTransaction(stores) {
2857
+ return new PostgreSQLReadTransaction(this.db, this.tables, this);
2306
2858
  }
2307
2859
  }
2860
+ var createPostgreSQLAdapterFactory = (db) => {
2861
+ return async (context3) => {
2862
+ const adapter = new PostgreSQLAdapter(db, context3);
2863
+ await adapter.initialize();
2864
+ return adapter;
2865
+ };
2866
+ };
2308
2867
  // db/sqliteAdapter.ts
2309
2868
  class SQLiteReadTransaction {
2310
2869
  db;
2311
2870
  tables;
2312
- constructor(db, tables) {
2871
+ adapter;
2872
+ constructor(db, tables, adapter) {
2313
2873
  this.db = db;
2314
2874
  this.tables = tables;
2875
+ this.adapter = adapter;
2876
+ }
2877
+ hasSoftDelete(tableName) {
2878
+ if (this.adapter) {
2879
+ return this.adapter.hasSoftDelete(tableName);
2880
+ }
2881
+ const table = this.tables.get(tableName);
2882
+ if (!table)
2883
+ return false;
2884
+ return table.columns.some((col) => col.name === "deleted");
2315
2885
  }
2316
2886
  deserializeValue(value, column) {
2317
2887
  if (value === null || value === undefined)
2318
2888
  return null;
2319
2889
  switch (column.type.toLowerCase()) {
2320
2890
  case "json":
2321
- case "text": {
2322
- try {
2323
- const parsed = JSON.parse(value);
2324
- if (typeof parsed === "object" || Array.isArray(parsed)) {
2325
- return parsed;
2891
+ if (typeof value === "string") {
2892
+ try {
2893
+ return JSON.parse(value);
2894
+ } catch {
2895
+ return value;
2326
2896
  }
2327
- return value;
2328
- } catch {
2329
- return value;
2330
2897
  }
2331
- }
2898
+ return value;
2899
+ case "text":
2900
+ if (typeof value === "string" && (value.startsWith("{") || value.startsWith("["))) {
2901
+ try {
2902
+ const parsed = JSON.parse(value);
2903
+ if (typeof parsed === "object" || Array.isArray(parsed)) {
2904
+ return parsed;
2905
+ }
2906
+ } catch {}
2907
+ }
2908
+ return value;
2332
2909
  case "datetime":
2333
2910
  case "timestamp":
2334
2911
  return new Date(value);
@@ -2347,12 +2924,18 @@ class SQLiteReadTransaction {
2347
2924
  getId(store, id3) {
2348
2925
  return id3;
2349
2926
  }
2350
- buildWhereClause(where) {
2927
+ buildWhereClause(where, tableName) {
2928
+ const conditions = [];
2929
+ const params = [];
2930
+ if (tableName && this.hasSoftDelete(tableName)) {
2931
+ conditions.push("deleted = 0");
2932
+ }
2351
2933
  if (!where) {
2352
- return { sql: "deleted != 1", params: [] };
2934
+ return {
2935
+ sql: conditions.length > 0 ? conditions.join(" AND ") : "1=1",
2936
+ params
2937
+ };
2353
2938
  }
2354
- const conditions = ["deleted != 1"];
2355
- const params = [];
2356
2939
  Object.entries(where).forEach(([key, value]) => {
2357
2940
  if (typeof value === "object" && value !== null) {
2358
2941
  Object.entries(value).forEach(([operator, operand]) => {
@@ -2409,7 +2992,7 @@ class SQLiteReadTransaction {
2409
2992
  }
2410
2993
  async find(store, options) {
2411
2994
  const { where, limit, offset, orderBy } = options || {};
2412
- const whereClause = this.buildWhereClause(where);
2995
+ const whereClause = this.buildWhereClause(where, store);
2413
2996
  const orderByClause = this.buildOrderByClause(orderBy);
2414
2997
  const table = this.tables.get(store);
2415
2998
  if (!table) {
@@ -2429,6 +3012,11 @@ class SQLiteReadTransaction {
2429
3012
  }
2430
3013
 
2431
3014
  class SQLiteReadWriteTransaction extends SQLiteReadTransaction {
3015
+ adapter;
3016
+ constructor(db, tables, adapter) {
3017
+ super(db, tables, adapter);
3018
+ this.adapter = adapter;
3019
+ }
2432
3020
  async remove(store, id3) {
2433
3021
  const table = this.tables.get(store);
2434
3022
  if (!table) {
@@ -2442,7 +3030,14 @@ class SQLiteReadWriteTransaction extends SQLiteReadTransaction {
2442
3030
  if (!table) {
2443
3031
  throw new Error(`Store ${store} not found`);
2444
3032
  }
2445
- const now = new Date().toISOString();
3033
+ const hasVersioning = this.adapter.hasVersioning(store);
3034
+ if (hasVersioning) {
3035
+ await this.setWithVersioning(store, item, table);
3036
+ } else {
3037
+ await this.setWithoutVersioning(store, item, table);
3038
+ }
3039
+ }
3040
+ async setWithoutVersioning(store, item, table) {
2446
3041
  const columnNames = table.columns.map((col) => col.name);
2447
3042
  const values = table.columns.map((column) => {
2448
3043
  let value = item[column.name];
@@ -2459,6 +3054,32 @@ class SQLiteReadWriteTransaction extends SQLiteReadTransaction {
2459
3054
  `;
2460
3055
  await this.db.exec(sql, values);
2461
3056
  }
3057
+ async setWithVersioning(store, item, table) {
3058
+ const regularColumns = table.columns.filter((col) => col.name !== "__version");
3059
+ const columnNames = regularColumns.map((col) => col.name);
3060
+ const values = regularColumns.map((column) => {
3061
+ let value = item[column.name];
3062
+ if (value === undefined && column.default !== undefined) {
3063
+ value = column.default;
3064
+ }
3065
+ return this.serializeValue(value, column);
3066
+ });
3067
+ columnNames.push("__version");
3068
+ const placeholders = regularColumns.map(() => "?").join(", ");
3069
+ const sql = `
3070
+ WITH next_version AS (
3071
+ INSERT INTO __arc_version_counters (table_name, last_version)
3072
+ VALUES (?, 1)
3073
+ ON CONFLICT(table_name)
3074
+ DO UPDATE SET last_version = last_version + 1
3075
+ RETURNING last_version
3076
+ )
3077
+ INSERT OR REPLACE INTO ${table.name}
3078
+ (${columnNames.map((c) => `"${c}"`).join(", ")})
3079
+ VALUES (${placeholders}, (SELECT last_version FROM next_version))
3080
+ `;
3081
+ await this.db.exec(sql, [...values, store]);
3082
+ }
2462
3083
  async commit() {
2463
3084
  return Promise.resolve();
2464
3085
  }
@@ -2466,6 +3087,22 @@ class SQLiteReadWriteTransaction extends SQLiteReadTransaction {
2466
3087
  if (value === null || value === undefined)
2467
3088
  return null;
2468
3089
  switch (column.type.toLowerCase()) {
3090
+ case "timestamp":
3091
+ case "datetime":
3092
+ if (value instanceof Date) {
3093
+ return value.toISOString();
3094
+ }
3095
+ if (typeof value === "number") {
3096
+ const date3 = value > 10000000000 ? new Date(value) : new Date(value * 1000);
3097
+ return date3.toISOString();
3098
+ }
3099
+ if (typeof value === "string") {
3100
+ const date3 = new Date(value);
3101
+ if (!isNaN(date3.getTime())) {
3102
+ return date3.toISOString();
3103
+ }
3104
+ }
3105
+ return value;
2469
3106
  case "json":
2470
3107
  return JSON.stringify(value);
2471
3108
  default:
@@ -2484,58 +3121,238 @@ class SQLiteAdapter {
2484
3121
  db;
2485
3122
  context;
2486
3123
  tables = new Map;
3124
+ tableSchemas = new Map;
3125
+ pendingReinitTables = [];
3126
+ mapType(arcType, storeData) {
3127
+ if (storeData?.databaseType?.sqlite) {
3128
+ return storeData.databaseType.sqlite;
3129
+ }
3130
+ switch (arcType) {
3131
+ case "string":
3132
+ case "id":
3133
+ case "customId":
3134
+ case "stringEnum":
3135
+ return "TEXT";
3136
+ case "number":
3137
+ return "INTEGER";
3138
+ case "boolean":
3139
+ return "INTEGER";
3140
+ case "date":
3141
+ return "TIMESTAMP";
3142
+ case "object":
3143
+ case "array":
3144
+ case "record":
3145
+ return "JSON";
3146
+ case "blob":
3147
+ return "BLOB";
3148
+ default:
3149
+ return "TEXT";
3150
+ }
3151
+ }
3152
+ buildConstraints(storeData) {
3153
+ const constraints = [];
3154
+ if (storeData?.isPrimaryKey) {
3155
+ constraints.push("PRIMARY KEY");
3156
+ }
3157
+ if (storeData?.isAutoIncrement) {
3158
+ constraints.push("AUTOINCREMENT");
3159
+ }
3160
+ if (storeData?.isUnique) {
3161
+ constraints.push("UNIQUE");
3162
+ }
3163
+ if (storeData?.foreignKey) {
3164
+ const { table, column, onDelete, onUpdate } = storeData.foreignKey;
3165
+ let fkConstraint = `REFERENCES ${table}(${column})`;
3166
+ if (onDelete)
3167
+ fkConstraint += ` ON DELETE ${onDelete}`;
3168
+ if (onUpdate)
3169
+ fkConstraint += ` ON UPDATE ${onUpdate}`;
3170
+ constraints.push(fkConstraint);
3171
+ }
3172
+ return constraints;
3173
+ }
3174
+ generateColumnSQL(columnInfo) {
3175
+ const type = this.mapType(columnInfo.type, columnInfo.storeData);
3176
+ const constraints = [];
3177
+ if (!columnInfo.storeData?.isNullable) {
3178
+ constraints.push("NOT NULL");
3179
+ }
3180
+ constraints.push(...this.buildConstraints(columnInfo.storeData));
3181
+ if (columnInfo.defaultValue !== undefined) {
3182
+ constraints.push(`DEFAULT ${JSON.stringify(columnInfo.defaultValue)}`);
3183
+ }
3184
+ return `"${columnInfo.name}" ${type} ${constraints.join(" ")}`.trim();
3185
+ }
3186
+ generateCreateTableSQL(tableName, columns) {
3187
+ const columnDefinitions = columns.map((col) => this.generateColumnSQL(col));
3188
+ 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});`);
3189
+ let sql = `CREATE TABLE IF NOT EXISTS ${tableName} (
3190
+ ${columnDefinitions.join(`,
3191
+ `)}
3192
+ )`;
3193
+ if (indexes.length > 0) {
3194
+ sql += `;
3195
+ ` + indexes.join(`
3196
+ `);
3197
+ }
3198
+ return sql;
3199
+ }
2487
3200
  constructor(db, context3) {
2488
3201
  this.db = db;
2489
3202
  this.context = context3;
2490
3203
  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);
3204
+ if ("databaseStoreSchema" in element3 && typeof element3.databaseStoreSchema === "function") {
3205
+ const databaseSchema = element3.databaseStoreSchema();
3206
+ databaseSchema.tables.forEach((dbTable) => {
3207
+ const agnosticSchema = extractDatabaseAgnosticSchema(dbTable.schema, dbTable.name);
3208
+ const columns = agnosticSchema.columns.map((columnInfo) => ({
3209
+ name: columnInfo.name,
3210
+ type: this.mapType(columnInfo.type, columnInfo.storeData),
3211
+ constraints: this.buildConstraints(columnInfo.storeData),
3212
+ isNullable: columnInfo.storeData?.isNullable || false,
3213
+ defaultValue: columnInfo.defaultValue,
3214
+ isPrimaryKey: columnInfo.storeData?.isPrimaryKey || false,
3215
+ isAutoIncrement: columnInfo.storeData?.isAutoIncrement || false,
3216
+ isUnique: columnInfo.storeData?.isUnique || false,
3217
+ hasIndex: columnInfo.storeData?.hasIndex || false,
3218
+ foreignKey: columnInfo.storeData?.foreignKey
3219
+ }));
3220
+ this.tableSchemas.set(dbTable.name, columns);
3221
+ const physicalTableName = this.getPhysicalTableName(dbTable.name, dbTable.version);
3222
+ const legacyTable = {
3223
+ name: physicalTableName,
3224
+ primaryKey: columns.find((col) => col.isPrimaryKey)?.name || "_id",
3225
+ columns: columns.map((col) => ({
3226
+ name: col.name,
3227
+ type: col.type,
3228
+ isOptional: col.isNullable,
3229
+ default: col.defaultValue
3230
+ }))
3231
+ };
3232
+ this.tables.set(dbTable.name, legacyTable);
2494
3233
  });
2495
3234
  }
2496
3235
  });
2497
3236
  }
2498
3237
  async initialize() {
2499
- const stores = new Set;
3238
+ await this.createVersionCounterTable();
3239
+ await this.createTableVersionsTable();
3240
+ const processedSchemas = new Set;
3241
+ const processedTables = new Set;
2500
3242
  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);
3243
+ if ("databaseStoreSchema" in element3 && typeof element3.databaseStoreSchema === "function") {
3244
+ const databaseSchema = element3.databaseStoreSchema();
3245
+ if (processedSchemas.has(databaseSchema)) {
3246
+ continue;
3247
+ }
3248
+ processedSchemas.add(databaseSchema);
3249
+ for (const dbTable of databaseSchema.tables) {
3250
+ const tableKey = dbTable.version ? `${dbTable.name}_v${dbTable.version}` : dbTable.name;
3251
+ if (!processedTables.has(tableKey)) {
3252
+ const agnosticSchema = extractDatabaseAgnosticSchema(dbTable.schema, dbTable.name);
3253
+ let allColumns = [...agnosticSchema.columns];
3254
+ if (dbTable.options?.versioning) {
3255
+ allColumns.push({
3256
+ name: "__version",
3257
+ type: "number",
3258
+ storeData: { isNullable: false, hasIndex: true },
3259
+ defaultValue: 1
3260
+ });
3261
+ }
3262
+ if (dbTable.options?.softDelete) {
3263
+ allColumns.push({
3264
+ name: "deleted",
3265
+ type: "boolean",
3266
+ storeData: { isNullable: false, hasIndex: true },
3267
+ defaultValue: false
3268
+ });
3269
+ }
3270
+ const physicalTableName = this.getPhysicalTableName(dbTable.name, dbTable.version);
3271
+ if (dbTable.version && await this.checkVersionedTableExists(dbTable.name, dbTable.version)) {
3272
+ console.log(`Versioned table ${physicalTableName} already exists, skipping creation`);
3273
+ } else {
3274
+ await this.createTableIfNotExistsNew(physicalTableName, allColumns);
3275
+ if (dbTable.version) {
3276
+ await this.registerTableVersion(dbTable.name, dbTable.version, physicalTableName);
3277
+ if (databaseSchema.reinitTable) {
3278
+ this.pendingReinitTables.push({
3279
+ tableName: physicalTableName,
3280
+ reinitFn: databaseSchema.reinitTable
3281
+ });
3282
+ }
3283
+ }
3284
+ }
3285
+ processedTables.add(tableKey);
3286
+ }
3287
+ }
2509
3288
  }
2510
3289
  }
2511
3290
  }
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
- `)}
3291
+ async createTableIfNotExistsNew(tableName, columns) {
3292
+ const createTableSQL = this.generateCreateTableSQL(tableName, columns);
3293
+ await this.db.exec(createTableSQL);
3294
+ }
3295
+ async createVersionCounterTable() {
3296
+ const sql = `
3297
+ CREATE TABLE IF NOT EXISTS __arc_version_counters (
3298
+ table_name TEXT PRIMARY KEY,
3299
+ last_version INTEGER NOT NULL DEFAULT 0
2530
3300
  )
2531
3301
  `;
2532
- await this.db.exec(createTableSQL);
3302
+ await this.db.exec(sql);
3303
+ }
3304
+ async createTableVersionsTable() {
3305
+ const sql = `
3306
+ CREATE TABLE IF NOT EXISTS __arc_table_versions (
3307
+ table_name TEXT NOT NULL,
3308
+ version INTEGER NOT NULL,
3309
+ physical_table_name TEXT NOT NULL,
3310
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
3311
+ is_active INTEGER DEFAULT 0,
3312
+ PRIMARY KEY (table_name, version)
3313
+ )
3314
+ `;
3315
+ await this.db.exec(sql);
3316
+ }
3317
+ getPhysicalTableName(logicalName, version) {
3318
+ return version ? `${logicalName}_v${version}` : logicalName;
3319
+ }
3320
+ async checkVersionedTableExists(logicalName, version) {
3321
+ const result = await this.db.exec("SELECT COUNT(*) as count FROM __arc_table_versions WHERE table_name = ? AND version = ?", [logicalName, version]);
3322
+ return result[0]?.count > 0;
3323
+ }
3324
+ async registerTableVersion(logicalName, version, physicalName) {
3325
+ await this.db.exec("INSERT INTO __arc_table_versions (table_name, version, physical_table_name, is_active) VALUES (?, ?, ?, ?)", [logicalName, version, physicalName, 1]);
3326
+ }
3327
+ hasVersioning(tableName) {
3328
+ const table = this.tables.get(tableName);
3329
+ if (!table)
3330
+ return false;
3331
+ return table.columns.some((col) => col.name === "__version");
3332
+ }
3333
+ hasSoftDelete(tableName) {
3334
+ const table = this.tables.get(tableName);
3335
+ if (!table)
3336
+ return false;
3337
+ return table.columns.some((col) => col.name === "deleted");
3338
+ }
3339
+ async executeReinitTables(dataStorage) {
3340
+ for (const { tableName, reinitFn } of this.pendingReinitTables) {
3341
+ try {
3342
+ await reinitFn(tableName, dataStorage);
3343
+ console.log(`Successfully reinitialized table: ${tableName}`);
3344
+ } catch (error) {
3345
+ console.error(`Failed to reinitialize table ${tableName}:`, error);
3346
+ throw error;
3347
+ }
3348
+ }
3349
+ this.pendingReinitTables = [];
2533
3350
  }
2534
3351
  readWriteTransaction(stores) {
2535
- return new SQLiteReadWriteTransaction(this.db, this.tables);
3352
+ return new SQLiteReadWriteTransaction(this.db, this.tables, this);
2536
3353
  }
2537
3354
  readTransaction(stores) {
2538
- return new SQLiteReadTransaction(this.db, this.tables);
3355
+ return new SQLiteReadTransaction(this.db, this.tables, this);
2539
3356
  }
2540
3357
  }
2541
3358
  var createSQLiteAdapterFactory = (db) => {
@@ -2599,17 +3416,17 @@ class ArcListener extends ArcContextElement {
2599
3416
  return (e) => {
2600
3417
  const element4 = this._elements.find((element5) => element5.name === e.name);
2601
3418
  if (!element4) {
2602
- throw new Error(`Element "${String(name)}" not found in listener "${this.name}"`);
3419
+ throw new Error(`Element "${String(e.name)}" not found in listener "${this.name}"`);
2603
3420
  }
2604
3421
  if (!element4.commandContext) {
2605
- throw new Error(`Element "${String(name)}" does not have a command context`);
3422
+ throw new Error(`Element "${String(e.name)}" does not have a command context`);
2606
3423
  }
2607
3424
  return element4.commandContext(dataStorage, publishEvent, authContext);
2608
3425
  };
2609
3426
  }
2610
3427
  const element3 = this._elements.find((element4) => element4.name === name);
2611
3428
  if (!element3) {
2612
- throw new Error(`Element "${String(name)}" not found in listener "${this.name}"`);
3429
+ throw new Error(`Element "${element3}" not found in listener "${this.name}"`);
2613
3430
  }
2614
3431
  if (!element3.commandContext) {
2615
3432
  throw new Error(`Element "${String(name)}" does not have a command context`);
@@ -2673,14 +3490,14 @@ class EventPublisher {
2673
3490
  async runAsyncListeners() {
2674
3491
  const allAsyncTasks = this.asyncEvents.flatMap(({ event: event3, listeners, authContext }) => listeners.map(async (listener3) => {
2675
3492
  const listenerFork = this.dataStorage.fork();
3493
+ const childPublisher = new EventPublisher(this.context, this.dataStorage, authContext);
2676
3494
  const asyncPublishEvent = async (childEvent) => {
2677
- const childPublisher = new EventPublisher(this.context, listenerFork, authContext);
2678
3495
  await childPublisher.publishEvent(childEvent, listenerFork);
2679
- await childPublisher.runAsyncListeners();
2680
3496
  };
2681
3497
  try {
2682
3498
  await listener3(event3, listenerFork, asyncPublishEvent);
2683
3499
  await listenerFork.merge();
3500
+ childPublisher.runAsyncListeners();
2684
3501
  } catch (error) {
2685
3502
  console.error("Async listener failed:", error);
2686
3503
  }
@@ -3154,6 +3971,22 @@ class RTCClient {
3154
3971
  var rtcClientFactory = (token) => (storage) => {
3155
3972
  return new RTCClient(storage, token);
3156
3973
  };
3974
+ // context/serializable-query.ts
3975
+ class ArcSerializableQuery extends ArcQuery {
3976
+ params;
3977
+ static key;
3978
+ constructor(params) {
3979
+ super();
3980
+ this.params = params;
3981
+ }
3982
+ serialize() {
3983
+ return {
3984
+ key: this.constructor.key,
3985
+ params: this.params
3986
+ };
3987
+ }
3988
+ }
3989
+
3157
3990
  // view/queries/abstract-view-query.ts
3158
3991
  class ArcViewQuery extends ArcSerializableQuery {
3159
3992
  view;
@@ -3457,6 +4290,7 @@ class ArcView extends ArcContextElementWithStore {
3457
4290
  _elements;
3458
4291
  _handler;
3459
4292
  _isAsync = false;
4293
+ _version;
3460
4294
  restrictions;
3461
4295
  constructor(name, id3, schema) {
3462
4296
  super();
@@ -3464,8 +4298,84 @@ class ArcView extends ArcContextElementWithStore {
3464
4298
  this.id = id3;
3465
4299
  this.schema = schema;
3466
4300
  }
3467
- storeSchema = () => {
3468
- return arcObjectToStoreSchema(this.name, this.schema);
4301
+ databaseStoreSchema = () => {
4302
+ const systemFields = {
4303
+ _id: string().primaryKey()
4304
+ };
4305
+ const completeSchema = this.schema.merge(systemFields);
4306
+ return {
4307
+ tables: [
4308
+ {
4309
+ name: this.name,
4310
+ schema: completeSchema,
4311
+ version: this._version,
4312
+ options: {
4313
+ softDelete: true,
4314
+ versioning: true
4315
+ }
4316
+ }
4317
+ ],
4318
+ reinitTable: async (tableName, dataStorage) => {
4319
+ console.log(`Reinitializing view table: ${tableName} for view: ${this.name}`);
4320
+ if (this._handler && this._elements) {
4321
+ try {
4322
+ const eventStore = dataStorage.getStore("events");
4323
+ const viewStore = dataStorage.getStore(this.name);
4324
+ const events = await eventStore.find({
4325
+ where: {
4326
+ type: {
4327
+ $in: this._elements.filter((element3) => element3 instanceof ArcEvent).map((element3) => element3.name)
4328
+ }
4329
+ }
4330
+ });
4331
+ console.log(`Found ${events.length} events to replay for view ${this.name}`);
4332
+ const reinitContext = {
4333
+ set: async (id3, data) => {
4334
+ const parsed = this.schema.parse(data);
4335
+ const body = {
4336
+ _id: id3,
4337
+ lastUpdate: new Date().toISOString(),
4338
+ ...parsed
4339
+ };
4340
+ await viewStore.set(body);
4341
+ },
4342
+ modify: async (id3, data) => {
4343
+ await viewStore.modify(id3, {
4344
+ ...data,
4345
+ lastUpdate: new Date().toISOString()
4346
+ });
4347
+ },
4348
+ remove: async (id3) => {
4349
+ await viewStore.remove(id3);
4350
+ },
4351
+ find: async (options) => {
4352
+ return viewStore.find(options);
4353
+ },
4354
+ findOne: async (where) => {
4355
+ const result = await viewStore.find({ where, limit: 1 });
4356
+ return result[0];
4357
+ },
4358
+ $auth: {}
4359
+ };
4360
+ for (const event3 of events) {
4361
+ if (this._handler && this._handler[event3.type]) {
4362
+ try {
4363
+ await this._handler[event3.type](reinitContext, event3);
4364
+ } catch (error) {
4365
+ console.error(`Error processing event ${event3.type} for view ${this.name}:`, error);
4366
+ }
4367
+ }
4368
+ }
4369
+ console.log(`Successfully reinitialized view ${this.name} with ${events.length} events`);
4370
+ } catch (error) {
4371
+ console.error(`Failed to reinitialize view ${this.name}:`, error);
4372
+ throw error;
4373
+ }
4374
+ } else {
4375
+ console.log(`No handlers defined for view ${this.name}, skipping event replay`);
4376
+ }
4377
+ }
4378
+ };
3469
4379
  };
3470
4380
  auth(restrictionsFn) {
3471
4381
  const clone = this.clone();
@@ -3487,6 +4397,11 @@ class ArcView extends ArcContextElementWithStore {
3487
4397
  clone._isAsync = true;
3488
4398
  return clone;
3489
4399
  }
4400
+ version(version) {
4401
+ const clone = this.clone();
4402
+ clone._version = version;
4403
+ return clone;
4404
+ }
3490
4405
  handle(handler) {
3491
4406
  const clone = this.clone();
3492
4407
  clone._handler = handler;
@@ -3607,6 +4522,7 @@ class ArcView extends ArcContextElementWithStore {
3607
4522
  clone._elements = this._elements;
3608
4523
  clone._handler = this._handler;
3609
4524
  clone._isAsync = this._isAsync;
4525
+ clone._version = this._version;
3610
4526
  clone.restrictions = this.restrictions;
3611
4527
  return clone;
3612
4528
  }
@@ -3634,14 +4550,13 @@ export {
3634
4550
  date,
3635
4551
  customId,
3636
4552
  createSQLiteAdapterFactory,
4553
+ createPostgreSQLAdapterFactory,
3637
4554
  contextMerge,
3638
4555
  context,
3639
4556
  command,
3640
- collection,
3641
4557
  boolean,
3642
4558
  blob,
3643
4559
  array,
3644
- arcObjectToStoreSchema,
3645
4560
  any,
3646
4561
  StoreState,
3647
4562
  SQLiteAdapter,
@@ -3649,6 +4564,7 @@ export {
3649
4564
  QueryViewResult,
3650
4565
  QueryCache,
3651
4566
  QueryBuilderContext,
4567
+ PostgreSQLAdapter,
3652
4568
  ModelBase,
3653
4569
  Model,
3654
4570
  MasterStoreState,
@@ -3681,8 +4597,6 @@ export {
3681
4597
  ArcContextElement,
3682
4598
  ArcContext,
3683
4599
  ArcCommand,
3684
- ArcCollectionQuery,
3685
- ArcCollection,
3686
4600
  ArcBranded,
3687
4601
  ArcBoolean,
3688
4602
  ArcBlob,