@arcote.tech/arc 0.1.2 → 0.1.4

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 (38) hide show
  1. package/dist/collection/collection.d.ts +12 -1
  2. package/dist/command/command.d.ts +14 -11
  3. package/dist/context/context.d.ts +7 -7
  4. package/dist/context/element.d.ts +27 -5
  5. package/dist/context/event.d.ts +12 -1
  6. package/dist/context/index.d.ts +1 -0
  7. package/dist/context/simple-query.d.ts +33 -0
  8. package/dist/data-storage/data-storage-builder.d.ts +16 -0
  9. package/dist/data-storage/index.d.ts +2 -0
  10. package/dist/data-storage/query-processor.d.ts +22 -0
  11. package/dist/data-storage/store-state-authorized.d.ts +26 -0
  12. package/dist/db/sqliteAdapter.d.ts +3 -1
  13. package/dist/elements/blob.d.ts +79 -0
  14. package/dist/elements/boolean.d.ts +13 -2
  15. package/dist/elements/date.d.ts +1 -1
  16. package/dist/elements/file.d.ts +139 -0
  17. package/dist/elements/index.d.ts +3 -0
  18. package/dist/elements/object.d.ts +7 -0
  19. package/dist/index.d.ts +1 -0
  20. package/dist/index.js +1106 -194
  21. package/dist/listener/index.d.ts +2 -0
  22. package/dist/listener/listener.d.ts +23 -0
  23. package/dist/model/model.d.ts +37 -16
  24. package/dist/tests/utils/test-model.d.ts +0 -1
  25. package/dist/view/index.d.ts +4 -0
  26. package/dist/view/queries/abstract-view-query.d.ts +20 -0
  27. package/dist/view/queries/find-one.d.ts +18 -0
  28. package/dist/view/queries/find.d.ts +20 -0
  29. package/dist/view/queries/index.d.ts +3 -0
  30. package/dist/view/query-builders/find-one.d.ts +13 -0
  31. package/dist/view/query-builders/find.d.ts +13 -0
  32. package/dist/view/query-builders/index.d.ts +2 -0
  33. package/dist/view/view.d.ts +25 -13
  34. package/package.json +1 -1
  35. package/dist/db/index-query.d.ts +0 -3
  36. package/dist/dist/index.d.ts +0 -513
  37. package/dist/elements/tests/example.d.ts +0 -6
  38. package/dist/elements/tests/test.d.ts +0 -2
package/dist/index.js CHANGED
@@ -396,6 +396,22 @@ class ArcObject extends ArcAbstract {
396
396
  return acc;
397
397
  }, {});
398
398
  }
399
+ validatePartial(value) {
400
+ const errors = Object.entries(value).reduce((acc, [key, val]) => {
401
+ if (!this.rawShape[key]) {
402
+ return acc;
403
+ }
404
+ const result = this.rawShape[key].validate(val);
405
+ if (result) {
406
+ acc[key] = result;
407
+ }
408
+ return acc;
409
+ }, {});
410
+ if (Object.keys(errors).length > 0) {
411
+ return { schema: errors };
412
+ }
413
+ return false;
414
+ }
399
415
  pick(...keys) {
400
416
  return new ArcObject(Object.fromEntries(keys.map((key) => [key, this.rawShape[key]])));
401
417
  }
@@ -498,8 +514,111 @@ class ArcArray extends ArcAbstract {
498
514
  function array(element) {
499
515
  return new ArcArray(element);
500
516
  }
517
+ // elements/blob.ts
518
+ var blobValidator = {
519
+ name: "blob",
520
+ validator: (value) => {
521
+ if (!(value instanceof Blob)) {
522
+ return {
523
+ currentType: typeof value,
524
+ expectedType: "Blob"
525
+ };
526
+ }
527
+ }
528
+ };
529
+
530
+ class ArcBlob extends ArcPrimitive {
531
+ constructor() {
532
+ super([blobValidator]);
533
+ }
534
+ type(mimeType) {
535
+ return this.validation("type", (value) => {
536
+ if (!(value instanceof Blob))
537
+ return;
538
+ if (value.type !== mimeType) {
539
+ return {
540
+ currentType: value.type,
541
+ expectedType: mimeType
542
+ };
543
+ }
544
+ });
545
+ }
546
+ types(mimeTypes) {
547
+ return this.validation("types", (value) => {
548
+ if (!(value instanceof Blob))
549
+ return;
550
+ if (!mimeTypes.includes(value.type)) {
551
+ return {
552
+ currentType: value.type,
553
+ expectedTypes: mimeTypes
554
+ };
555
+ }
556
+ });
557
+ }
558
+ minSize(bytes) {
559
+ return this.validation("minSize", (value) => {
560
+ if (!(value instanceof Blob))
561
+ return;
562
+ if (value.size < bytes) {
563
+ return {
564
+ currentSize: value.size,
565
+ minSize: bytes
566
+ };
567
+ }
568
+ });
569
+ }
570
+ maxSize(bytes) {
571
+ return this.validation("maxSize", (value) => {
572
+ if (!(value instanceof Blob))
573
+ return;
574
+ if (value.size > bytes) {
575
+ return {
576
+ currentSize: value.size,
577
+ maxSize: bytes
578
+ };
579
+ }
580
+ });
581
+ }
582
+ size(bytes) {
583
+ return this.validation("size", (value) => {
584
+ if (!(value instanceof Blob))
585
+ return;
586
+ if (value.size !== bytes) {
587
+ return {
588
+ currentSize: value.size,
589
+ expectedSize: bytes
590
+ };
591
+ }
592
+ });
593
+ }
594
+ validation(name, validator) {
595
+ const instance = this.pipeValidation(name, validator);
596
+ return instance;
597
+ }
598
+ parse(value) {
599
+ if (value instanceof Blob) {
600
+ return value;
601
+ }
602
+ throw new Error("Expected Blob object");
603
+ }
604
+ }
605
+ function blob() {
606
+ return new ArcBlob;
607
+ }
501
608
  // elements/boolean.ts
502
609
  class ArcBoolean extends ArcPrimitive {
610
+ hasToBeTrue() {
611
+ return this.validation("hasToBeTrue", (value) => {
612
+ if (!value)
613
+ return {
614
+ current: value
615
+ };
616
+ });
617
+ }
618
+ validation(name, validator) {
619
+ const instance = this.pipeValidation(name, validator);
620
+ return instance;
621
+ }
503
622
  }
504
623
  function boolean() {
505
624
  return new ArcBoolean;
@@ -515,7 +634,10 @@ class ArcDate extends ArcAbstract {
515
634
  return new Date(value);
516
635
  }
517
636
  serialize(value) {
518
- return value.getTime();
637
+ if (value instanceof Date) {
638
+ return value.getTime();
639
+ }
640
+ return value;
519
641
  }
520
642
  deserialize(value) {
521
643
  return new Date(value);
@@ -540,6 +662,173 @@ class ArcDate extends ArcAbstract {
540
662
  function date() {
541
663
  return new ArcDate;
542
664
  }
665
+ // elements/file.ts
666
+ var fileValidator = {
667
+ name: "file",
668
+ validator: (value) => {
669
+ if (!(value instanceof File)) {
670
+ return {
671
+ currentType: typeof value,
672
+ expectedType: "File"
673
+ };
674
+ }
675
+ }
676
+ };
677
+
678
+ class ArcFile extends ArcPrimitive {
679
+ constructor() {
680
+ super([fileValidator]);
681
+ }
682
+ type(mimeType) {
683
+ return this.validation("type", (value) => {
684
+ if (!(value instanceof File))
685
+ return;
686
+ if (value.type !== mimeType) {
687
+ return {
688
+ currentType: value.type,
689
+ expectedType: mimeType
690
+ };
691
+ }
692
+ });
693
+ }
694
+ types(mimeTypes) {
695
+ return this.validation("types", (value) => {
696
+ if (!(value instanceof File))
697
+ return;
698
+ if (!mimeTypes.includes(value.type)) {
699
+ return {
700
+ currentType: value.type,
701
+ expectedTypes: mimeTypes
702
+ };
703
+ }
704
+ });
705
+ }
706
+ minSize(bytes) {
707
+ return this.validation("minSize", (value) => {
708
+ if (!(value instanceof File))
709
+ return;
710
+ if (value.size < bytes) {
711
+ return {
712
+ currentSize: value.size,
713
+ minSize: bytes
714
+ };
715
+ }
716
+ });
717
+ }
718
+ maxSize(bytes) {
719
+ return this.validation("maxSize", (value) => {
720
+ if (!(value instanceof File))
721
+ return;
722
+ if (value.size > bytes) {
723
+ return {
724
+ currentSize: value.size,
725
+ maxSize: bytes
726
+ };
727
+ }
728
+ });
729
+ }
730
+ size(bytes) {
731
+ return this.validation("size", (value) => {
732
+ if (!(value instanceof File))
733
+ return;
734
+ if (value.size !== bytes) {
735
+ return {
736
+ currentSize: value.size,
737
+ expectedSize: bytes
738
+ };
739
+ }
740
+ });
741
+ }
742
+ name(expectedName) {
743
+ return this.validation("name", (value) => {
744
+ if (!(value instanceof File))
745
+ return;
746
+ if (value.name !== expectedName) {
747
+ return {
748
+ currentName: value.name,
749
+ expectedName
750
+ };
751
+ }
752
+ });
753
+ }
754
+ namePattern(pattern) {
755
+ return this.validation("namePattern", (value) => {
756
+ if (!(value instanceof File))
757
+ return;
758
+ if (!pattern.test(value.name)) {
759
+ return {
760
+ currentName: value.name,
761
+ pattern: pattern.toString()
762
+ };
763
+ }
764
+ });
765
+ }
766
+ extension(ext) {
767
+ return this.validation("extension", (value) => {
768
+ if (!(value instanceof File))
769
+ return;
770
+ const fileExt = value.name.split(".").pop()?.toLowerCase();
771
+ const expectedExt = ext.startsWith(".") ? ext.slice(1).toLowerCase() : ext.toLowerCase();
772
+ if (fileExt !== expectedExt) {
773
+ return {
774
+ currentExtension: fileExt,
775
+ expectedExtension: expectedExt
776
+ };
777
+ }
778
+ });
779
+ }
780
+ extensions(exts) {
781
+ return this.validation("extensions", (value) => {
782
+ if (!(value instanceof File))
783
+ return;
784
+ const fileExt = value.name.split(".").pop()?.toLowerCase();
785
+ const normalizedExts = exts.map((ext) => ext.startsWith(".") ? ext.slice(1).toLowerCase() : ext.toLowerCase());
786
+ if (!fileExt || !normalizedExts.includes(fileExt)) {
787
+ return {
788
+ currentExtension: fileExt,
789
+ expectedExtensions: normalizedExts
790
+ };
791
+ }
792
+ });
793
+ }
794
+ lastModifiedAfter(date2) {
795
+ return this.validation("lastModifiedAfter", (value) => {
796
+ if (!(value instanceof File))
797
+ return;
798
+ if (value.lastModified < date2.getTime()) {
799
+ return {
800
+ currentLastModified: new Date(value.lastModified),
801
+ expectedAfter: date2
802
+ };
803
+ }
804
+ });
805
+ }
806
+ lastModifiedBefore(date2) {
807
+ return this.validation("lastModifiedBefore", (value) => {
808
+ if (!(value instanceof File))
809
+ return;
810
+ if (value.lastModified > date2.getTime()) {
811
+ return {
812
+ currentLastModified: new Date(value.lastModified),
813
+ expectedBefore: date2
814
+ };
815
+ }
816
+ });
817
+ }
818
+ validation(name, validator) {
819
+ const instance = this.pipeValidation(name, validator);
820
+ return instance;
821
+ }
822
+ parse(value) {
823
+ if (value instanceof File) {
824
+ return value;
825
+ }
826
+ throw new Error("Expected File object");
827
+ }
828
+ }
829
+ function file() {
830
+ return new ArcFile;
831
+ }
543
832
  // elements/number.ts
544
833
  var numberValidator = typeValidatorBuilder("number");
545
834
 
@@ -654,26 +943,26 @@ function stringEnum(...values) {
654
943
  return new ArcStringEnum(values);
655
944
  }
656
945
  // utils/arcObjectToStoreSchema.ts
657
- function getSQLiteType(element) {
658
- if (element instanceof ArcDate) {
946
+ function getSQLiteType(element2) {
947
+ if (element2 instanceof ArcDate) {
659
948
  return "TEXT";
660
949
  }
661
- if (element instanceof ArcArray || element instanceof ArcObject) {
950
+ if (element2 instanceof ArcArray || element2 instanceof ArcObject) {
662
951
  return "TEXT";
663
952
  }
664
- if (element instanceof ArcBoolean) {
953
+ if (element2 instanceof ArcBoolean) {
665
954
  return "INTEGER";
666
955
  }
667
- if (element instanceof ArcNumber) {
956
+ if (element2 instanceof ArcNumber) {
668
957
  return "NUMBER";
669
958
  }
670
959
  return "TEXT";
671
960
  }
672
961
  function arcObjectToStoreSchema(tableName, schema) {
673
- const columns = schema.entries().map(([name, element]) => ({
962
+ const columns = schema.entries().map(([name, element2]) => ({
674
963
  name,
675
- type: getSQLiteType(element),
676
- isOptional: element instanceof ArcOptional
964
+ type: getSQLiteType(element2),
965
+ isOptional: element2 instanceof ArcOptional
677
966
  }));
678
967
  columns.unshift({
679
968
  name: "_id",
@@ -869,6 +1158,7 @@ class ArcCollection extends ArcContextElementWithStore {
869
1158
  id;
870
1159
  schema;
871
1160
  options;
1161
+ _restrictions;
872
1162
  constructor(name, id3, schema, options) {
873
1163
  super();
874
1164
  this.name = name;
@@ -879,6 +1169,22 @@ class ArcCollection extends ArcContextElementWithStore {
879
1169
  storeSchema() {
880
1170
  return arcObjectToStoreSchema(this.name, this.schema);
881
1171
  }
1172
+ restrictions(authContext) {
1173
+ if (this._restrictions) {
1174
+ return this._restrictions(authContext);
1175
+ }
1176
+ return {
1177
+ read: false,
1178
+ write: false,
1179
+ modify: false,
1180
+ delete: false
1181
+ };
1182
+ }
1183
+ auth(restrictionsFn) {
1184
+ const collection = new ArcCollection(this.name, this.id, this.schema, this.options);
1185
+ collection._restrictions = restrictionsFn;
1186
+ return collection;
1187
+ }
882
1188
  serialize(data) {
883
1189
  return {
884
1190
  _id: this.id.serialize(data._id),
@@ -972,9 +1278,10 @@ class ArcCommand extends ArcContextElement {
972
1278
  name;
973
1279
  _description;
974
1280
  _params;
975
- _result;
1281
+ _results;
976
1282
  _elements;
977
1283
  _handler;
1284
+ _isPublic = false;
978
1285
  constructor(name) {
979
1286
  super();
980
1287
  this.name = name;
@@ -989,14 +1296,22 @@ class ArcCommand extends ArcContextElement {
989
1296
  clone._description = description;
990
1297
  return clone;
991
1298
  }
1299
+ public() {
1300
+ const clone = this.clone();
1301
+ clone._isPublic = true;
1302
+ return clone;
1303
+ }
1304
+ get isPublic() {
1305
+ return this._isPublic;
1306
+ }
992
1307
  withParams(schema) {
993
1308
  const clone = this.clone();
994
1309
  clone._params = schema instanceof ArcObject ? schema : object(schema);
995
1310
  return clone;
996
1311
  }
997
- withResult(schema) {
1312
+ withResult(...schemas) {
998
1313
  const clone = this.clone();
999
- clone._result = object(schema);
1314
+ clone._results = schemas.map((schema) => object(schema));
1000
1315
  return clone;
1001
1316
  }
1002
1317
  handle(handler) {
@@ -1014,9 +1329,10 @@ class ArcCommand extends ArcContextElement {
1014
1329
  const clone = new ArcCommand(this.name);
1015
1330
  clone._description = this._description;
1016
1331
  clone._params = this._params;
1017
- clone._result = this._result;
1332
+ clone._results = this._results;
1018
1333
  clone._handler = this._handler;
1019
1334
  clone._elements = this._elements;
1335
+ clone._isPublic = this._isPublic;
1020
1336
  return clone;
1021
1337
  }
1022
1338
  }
@@ -1026,20 +1342,34 @@ function command(name) {
1026
1342
  // context/context.ts
1027
1343
  class ArcContext {
1028
1344
  elements;
1029
- eventListeners;
1030
1345
  constructor(elements) {
1031
1346
  this.elements = elements;
1032
- this.eventListeners = new Map;
1033
- elements.forEach((element) => {
1034
- if (element.observer) {
1035
- const handlers = element.observer();
1036
- Object.entries(handlers).forEach(([alias, handler]) => {
1037
- const listeners = this.eventListeners.get(alias) || [];
1038
- listeners.push(handler);
1039
- this.eventListeners.set(alias, listeners);
1040
- });
1347
+ }
1348
+ getSyncListeners(eventType, authContext) {
1349
+ const listeners = [];
1350
+ this.elements.forEach((element2) => {
1351
+ if (element2.observer) {
1352
+ const handlers = element2.observer(authContext);
1353
+ const config = handlers[eventType];
1354
+ if (config && !config.isAsync) {
1355
+ listeners.push(config.handler);
1356
+ }
1357
+ }
1358
+ });
1359
+ return listeners;
1360
+ }
1361
+ getAsyncListeners(eventType, authContext) {
1362
+ const listeners = [];
1363
+ this.elements.forEach((element2) => {
1364
+ if (element2.observer) {
1365
+ const handlers = element2.observer(authContext);
1366
+ const config = handlers[eventType];
1367
+ if (config && config.isAsync) {
1368
+ listeners.push(config.handler);
1369
+ }
1041
1370
  }
1042
1371
  });
1372
+ return listeners;
1043
1373
  }
1044
1374
  pipe(newElements) {
1045
1375
  return new ArcContext([
@@ -1047,73 +1377,44 @@ class ArcContext {
1047
1377
  ...newElements.filter(Boolean)
1048
1378
  ]);
1049
1379
  }
1050
- queryBuilder(queryContext) {
1380
+ queryBuilder(queryContext, authContext) {
1051
1381
  return new Proxy({}, {
1052
1382
  get: (target, name) => {
1053
- const element = this.elements.find((element2) => element2.name === name);
1054
- if (!element) {
1383
+ const element2 = this.elements.find((element3) => element3.name === name);
1384
+ if (!element2) {
1055
1385
  throw new Error(`Element "${String(name)}" not found`);
1056
1386
  }
1057
- if (!element.queryBuilder) {
1387
+ if (!element2.queryBuilder) {
1058
1388
  throw new Error(`Element "${String(name)}" does not have a query builder`);
1059
1389
  }
1060
- return element.queryBuilder(queryContext);
1390
+ return element2.queryBuilder(queryContext, authContext);
1061
1391
  }
1062
1392
  });
1063
1393
  }
1064
- commandContext(client, dataStorage) {
1394
+ commandContext(dataStorage, publishEvent, authContext) {
1065
1395
  return new Proxy({}, {
1066
1396
  get: (target, name) => {
1067
- if (name === "$client") {
1068
- return client;
1397
+ if (name === "$auth") {
1398
+ return authContext;
1069
1399
  }
1070
- const element = this.elements.find((element2) => element2.name === name);
1071
- if (!element) {
1400
+ const element2 = this.elements.find((element3) => element3.name === name);
1401
+ if (!element2) {
1072
1402
  throw new Error(`Element "${String(name)}" not found`);
1073
1403
  }
1074
- if (!element.commandContext) {
1404
+ if (!element2.commandContext) {
1075
1405
  throw new Error(`Element "${String(name)}" does not have a command context`);
1076
1406
  }
1077
- return element.commandContext(dataStorage, async (event) => {
1078
- const listeners = this.eventListeners.get(event.type) || [];
1079
- await Promise.all(listeners.map((listener) => listener(event, dataStorage)));
1080
- });
1081
- }
1082
- });
1083
- }
1084
- commandsClient(client, queryContext, dataStorage, catchErrorCallback) {
1085
- return new Proxy({}, {
1086
- get: (_, name) => {
1087
- const element = this.elements.find((element2) => element2.name === name);
1088
- if (!element) {
1089
- throw new Error(`Element "${String(name)}" not found`);
1090
- }
1091
- if (!element.commandClient) {
1092
- throw new Error(`Element "${String(name)}" does not have a command client`);
1093
- }
1094
- return async (arg) => {
1095
- const forkedDataStorage = dataStorage.fork();
1096
- const commandContext = this.commandContext(client, forkedDataStorage);
1097
- try {
1098
- const result = await element.commandClient(commandContext)(arg);
1099
- await forkedDataStorage.merge();
1100
- return result;
1101
- } catch (error) {
1102
- console.log("error", error);
1103
- catchErrorCallback(error);
1104
- return error;
1105
- }
1106
- };
1407
+ return element2.commandContext(dataStorage, publishEvent, authContext);
1107
1408
  }
1108
1409
  });
1109
1410
  }
1110
1411
  }
1111
1412
  async function context(elementsPromise) {
1112
- const elements = await Promise.all(elementsPromise.map(async (element) => {
1113
- if (element instanceof Promise) {
1114
- return (await element).default;
1413
+ const elements = await Promise.all(elementsPromise.map(async (element2) => {
1414
+ if (element2 instanceof Promise) {
1415
+ return (await element2).default;
1115
1416
  }
1116
- return element;
1417
+ return element2;
1117
1418
  })).then((elements2) => elements2.filter(Boolean));
1118
1419
  return new ArcContext(elements);
1119
1420
  }
@@ -1150,12 +1451,44 @@ var eventId = id("event");
1150
1451
  class ArcEvent extends ArcContextElementWithStore {
1151
1452
  name;
1152
1453
  payload;
1454
+ _restrictions;
1153
1455
  storeSchema = () => eventStoreSchema;
1154
1456
  constructor(name, payload) {
1155
1457
  super();
1156
1458
  this.name = name;
1157
1459
  this.payload = payload;
1158
1460
  }
1461
+ restrictions(authContext) {
1462
+ if (this._restrictions) {
1463
+ const eventRestrictions = this._restrictions(authContext);
1464
+ return {
1465
+ read: eventRestrictions.read,
1466
+ write: eventRestrictions.write,
1467
+ modify: false,
1468
+ delete: false
1469
+ };
1470
+ }
1471
+ const isSystemUser = authContext.roles.includes("system") || authContext.roles.includes("admin");
1472
+ if (!isSystemUser) {
1473
+ return {
1474
+ read: false,
1475
+ write: false,
1476
+ modify: false,
1477
+ delete: false
1478
+ };
1479
+ }
1480
+ return {
1481
+ read: true,
1482
+ write: true,
1483
+ modify: false,
1484
+ delete: false
1485
+ };
1486
+ }
1487
+ auth(restrictionsFn) {
1488
+ const event = new ArcEvent(this.name, this.payload);
1489
+ event._restrictions = restrictionsFn;
1490
+ return event;
1491
+ }
1159
1492
  commandContext = (dataStorage, publishEvent) => {
1160
1493
  return {
1161
1494
  emit: async (payload) => {
@@ -1696,10 +2029,7 @@ class MasterDataStorage extends DataStorage {
1696
2029
  }
1697
2030
  getStore(storeName) {
1698
2031
  if (!this.stores.has(storeName)) {
1699
- const contextElement = this.arcContext.elements.find((element) => element.name === storeName);
1700
- if (!contextElement)
1701
- console.log(`Can't find ${storeName} as context element`);
1702
- this.stores.set(storeName, new MasterStoreState(storeName, this, contextElement && "deserialize" in contextElement && typeof contextElement.deserialize === "function" ? (a) => contextElement.deserialize(a) : undefined));
2032
+ this.stores.set(storeName, new MasterStoreState(storeName, this));
1703
2033
  }
1704
2034
  return this.stores.get(storeName);
1705
2035
  }
@@ -1730,6 +2060,37 @@ class SQLiteReadTransaction {
1730
2060
  this.db = db;
1731
2061
  this.tables = tables;
1732
2062
  }
2063
+ deserializeValue(value, column) {
2064
+ if (value === null || value === undefined)
2065
+ return null;
2066
+ switch (column.type.toLowerCase()) {
2067
+ case "json":
2068
+ case "text": {
2069
+ try {
2070
+ const parsed = JSON.parse(value);
2071
+ if (typeof parsed === "object" || Array.isArray(parsed)) {
2072
+ return parsed;
2073
+ }
2074
+ return value;
2075
+ } catch {
2076
+ return value;
2077
+ }
2078
+ }
2079
+ case "datetime":
2080
+ case "timestamp":
2081
+ return new Date(value);
2082
+ default:
2083
+ return value;
2084
+ }
2085
+ }
2086
+ deserializeRow(row, table) {
2087
+ const result = {};
2088
+ for (const column of table.columns) {
2089
+ const value = row[column.name];
2090
+ result[column.name] = this.deserializeValue(value, column);
2091
+ }
2092
+ return result;
2093
+ }
1733
2094
  getId(store, id3) {
1734
2095
  return id3;
1735
2096
  }
@@ -1797,6 +2158,10 @@ class SQLiteReadTransaction {
1797
2158
  const { where, limit, offset, orderBy } = options || {};
1798
2159
  const whereClause = this.buildWhereClause(where);
1799
2160
  const orderByClause = this.buildOrderByClause(orderBy);
2161
+ const table = this.tables.get(store);
2162
+ if (!table) {
2163
+ throw new Error(`Store ${store} not found`);
2164
+ }
1800
2165
  const query2 = `
1801
2166
  SELECT *
1802
2167
  FROM ${store}
@@ -1805,7 +2170,8 @@ class SQLiteReadTransaction {
1805
2170
  ${limit ? `LIMIT ${limit}` : ""}
1806
2171
  ${offset ? `OFFSET ${offset}` : ""}
1807
2172
  `;
1808
- return await this.db.exec(query2, whereClause.params);
2173
+ const rows = await this.db.exec(query2, whereClause.params);
2174
+ return rows.map((row) => this.deserializeRow(row, table));
1809
2175
  }
1810
2176
  }
1811
2177
 
@@ -1819,23 +2185,23 @@ class SQLiteReadWriteTransaction extends SQLiteReadTransaction {
1819
2185
  await this.db.exec(query2, [new Date().toISOString(), id3]);
1820
2186
  }
1821
2187
  async set(store, item) {
1822
- const schema = this.tables.get(store);
1823
- if (!schema) {
2188
+ const table = this.tables.get(store);
2189
+ if (!table) {
1824
2190
  throw new Error(`Store ${store} not found`);
1825
2191
  }
1826
2192
  const now = new Date().toISOString();
1827
- const columnNames = schema.columns.map((col) => col.name);
1828
- const values = schema.columns.map((column) => {
2193
+ const columnNames = table.columns.map((col) => col.name);
2194
+ const values = table.columns.map((column) => {
1829
2195
  let value = item[column.name];
1830
2196
  if (value === undefined && column.default !== undefined) {
1831
2197
  value = column.default;
1832
2198
  }
1833
- return this.serializeValue(value);
2199
+ return this.serializeValue(value, column);
1834
2200
  });
1835
2201
  const placeholders = columnNames.map(() => "?").join(", ");
1836
2202
  const sql = `
1837
- INSERT OR REPLACE INTO ${schema.name}
1838
- (${columnNames.join(", ")})
2203
+ INSERT OR REPLACE INTO ${table.name}
2204
+ (${columnNames.map((c) => `"${c}"`).join(", ")})
1839
2205
  VALUES (${placeholders})
1840
2206
  `;
1841
2207
  await this.db.exec(sql, values);
@@ -1843,16 +2209,21 @@ class SQLiteReadWriteTransaction extends SQLiteReadTransaction {
1843
2209
  async commit() {
1844
2210
  return Promise.resolve();
1845
2211
  }
1846
- serializeValue(value) {
2212
+ serializeValue(value, column) {
1847
2213
  if (value === null || value === undefined)
1848
2214
  return null;
1849
- if (value instanceof Date) {
1850
- return value.toISOString();
1851
- }
1852
- if (Array.isArray(value) || typeof value === "object") {
1853
- return JSON.stringify(value);
2215
+ switch (column.type.toLowerCase()) {
2216
+ case "json":
2217
+ return JSON.stringify(value);
2218
+ default:
2219
+ if (value instanceof Date) {
2220
+ return value.toISOString();
2221
+ }
2222
+ if (Array.isArray(value) || typeof value === "object") {
2223
+ return JSON.stringify(value);
2224
+ }
2225
+ return value;
1854
2226
  }
1855
- return value;
1856
2227
  }
1857
2228
  }
1858
2229
 
@@ -1863,9 +2234,9 @@ class SQLiteAdapter {
1863
2234
  constructor(db, context3) {
1864
2235
  this.db = db;
1865
2236
  this.context = context3;
1866
- this.context.elements.forEach((element) => {
1867
- if ("storeSchema" in element && typeof element.storeSchema === "function") {
1868
- element.storeSchema().tables.forEach((table) => {
2237
+ this.context.elements.forEach((element3) => {
2238
+ if ("storeSchema" in element3 && typeof element3.storeSchema === "function") {
2239
+ element3.storeSchema().tables.forEach((table) => {
1869
2240
  this.tables.set(table.name, table);
1870
2241
  });
1871
2242
  }
@@ -1873,9 +2244,9 @@ class SQLiteAdapter {
1873
2244
  }
1874
2245
  async initialize() {
1875
2246
  const stores = new Set;
1876
- for (const element of this.context.elements) {
1877
- if ("storeSchema" in element && typeof element.storeSchema === "function") {
1878
- const schema = element.storeSchema();
2247
+ for (const element3 of this.context.elements) {
2248
+ if ("storeSchema" in element3 && typeof element3.storeSchema === "function") {
2249
+ const schema = element3.storeSchema();
1879
2250
  stores.add(schema);
1880
2251
  }
1881
2252
  }
@@ -1921,64 +2292,214 @@ var createSQLiteAdapterFactory = (db) => {
1921
2292
  return adapter;
1922
2293
  };
1923
2294
  };
1924
- // model/model.ts
1925
- class ModelBase {
1926
- }
1927
-
1928
- class Model extends ModelBase {
1929
- context;
1930
- dataStorage;
1931
- client;
1932
- catchErrorCallback;
1933
- queryCache = new QueryCache;
1934
- constructor(context3, dataStorage, client, catchErrorCallback) {
2295
+ // listener/listener.ts
2296
+ class ArcListener extends ArcContextElement {
2297
+ name;
2298
+ _description;
2299
+ _elements;
2300
+ _eventElements;
2301
+ _handler;
2302
+ _isAsync = false;
2303
+ constructor(name) {
1935
2304
  super();
1936
- this.context = context3;
1937
- this.dataStorage = dataStorage;
1938
- this.client = client;
1939
- this.catchErrorCallback = catchErrorCallback;
2305
+ this.name = name;
1940
2306
  }
1941
- async query(queryBuilderFn) {
1942
- const queryContext = new QueryBuilderContext(this.queryCache, this.dataStorage);
1943
- const queryBuilder = this.context.queryBuilder(queryContext);
1944
- const query2 = queryBuilderFn(queryBuilder).toQuery(queryContext);
1945
- return query2.run(this.dataStorage);
2307
+ use(elements) {
2308
+ const clone = this.clone();
2309
+ clone._elements = elements;
2310
+ return clone;
1946
2311
  }
1947
- subscribe(queryBuilderFn, callback) {
1948
- const queryContext = new QueryBuilderContext(this.queryCache, this.dataStorage);
1949
- const queryBuilder = this.context.queryBuilder(queryContext);
1950
- const query2 = queryBuilderFn(queryBuilder).toQuery(queryContext);
1951
- query2.subscribe(callback);
1952
- const runPromise = query2.run(this.dataStorage);
1953
- runPromise.then((result) => {
1954
- callback(result);
1955
- });
1956
- return {
1957
- unsubscribe: () => {
1958
- query2.unsubscribe(callback);
1959
- },
1960
- result: runPromise
2312
+ description(description) {
2313
+ const clone = this.clone();
2314
+ clone._description = description;
2315
+ return clone;
2316
+ }
2317
+ listenTo(events) {
2318
+ const clone = this.clone();
2319
+ clone._eventElements = events;
2320
+ return clone;
2321
+ }
2322
+ async() {
2323
+ const clone = this.clone();
2324
+ clone._isAsync = true;
2325
+ return clone;
2326
+ }
2327
+ handle(handler) {
2328
+ const clone = this.clone();
2329
+ clone._handler = handler;
2330
+ return clone;
2331
+ }
2332
+ observer = (authContext) => {
2333
+ if (!this._handler || !this._elements || !this._eventElements) {
2334
+ return {};
2335
+ }
2336
+ const eventTypes = this._eventElements.filter((element3) => element3 instanceof ArcEvent).map((event3) => event3.name);
2337
+ return eventTypes.reduce((acc, eventType) => {
2338
+ acc[eventType] = {
2339
+ handler: async (event3, dataStorage, publishEvent) => {
2340
+ const ctx = new Proxy({}, {
2341
+ get: (target, name) => {
2342
+ if (name === "$auth") {
2343
+ return authContext;
2344
+ }
2345
+ const element3 = this._elements.find((element4) => element4.name === name);
2346
+ if (!element3) {
2347
+ throw new Error(`Element "${String(name)}" not found in listener "${this.name}"`);
2348
+ }
2349
+ if (!element3.commandContext) {
2350
+ throw new Error(`Element "${String(name)}" does not have a command context`);
2351
+ }
2352
+ return element3.commandContext(dataStorage, publishEvent, authContext);
2353
+ }
2354
+ });
2355
+ await this._handler(ctx, event3);
2356
+ },
2357
+ isAsync: this._isAsync
2358
+ };
2359
+ return acc;
2360
+ }, {});
2361
+ };
2362
+ clone() {
2363
+ const clone = new ArcListener(this.name);
2364
+ clone._description = this._description;
2365
+ clone._elements = this._elements;
2366
+ clone._eventElements = this._eventElements;
2367
+ clone._handler = this._handler;
2368
+ clone._isAsync = this._isAsync;
2369
+ return clone;
2370
+ }
2371
+ }
2372
+ function listener(name) {
2373
+ return new ArcListener(name);
2374
+ }
2375
+ // model/model.ts
2376
+ class EventPublisher {
2377
+ context;
2378
+ dataStorage;
2379
+ authContext;
2380
+ asyncEvents = [];
2381
+ constructor(context3, dataStorage, authContext) {
2382
+ this.context = context3;
2383
+ this.dataStorage = dataStorage;
2384
+ this.authContext = authContext;
2385
+ }
2386
+ async publishEvent(event3, commandDataStorage) {
2387
+ const recursivePublishEvent = async (childEvent) => {
2388
+ await this.publishEvent(childEvent, commandDataStorage);
1961
2389
  };
2390
+ const syncListeners = this.context.getSyncListeners(event3.type, this.authContext);
2391
+ for (const listener3 of syncListeners) {
2392
+ try {
2393
+ await listener3(event3, commandDataStorage, recursivePublishEvent);
2394
+ } catch (error) {
2395
+ console.error("Sync listener failed:", error);
2396
+ throw error;
2397
+ }
2398
+ }
2399
+ const asyncListeners = this.context.getAsyncListeners(event3.type, this.authContext);
2400
+ if (asyncListeners.length > 0) {
2401
+ this.asyncEvents.push({
2402
+ event: event3,
2403
+ listeners: asyncListeners,
2404
+ authContext: this.authContext
2405
+ });
2406
+ }
2407
+ }
2408
+ async runAsyncListeners() {
2409
+ const allAsyncTasks = this.asyncEvents.flatMap(({ event: event3, listeners, authContext }) => listeners.map(async (listener3) => {
2410
+ const listenerFork = this.dataStorage.fork();
2411
+ const asyncPublishEvent = async (childEvent) => {
2412
+ const childPublisher = new EventPublisher(this.context, listenerFork, authContext);
2413
+ await childPublisher.publishEvent(childEvent, listenerFork);
2414
+ await childPublisher.runAsyncListeners();
2415
+ };
2416
+ try {
2417
+ await listener3(event3, listenerFork, asyncPublishEvent);
2418
+ await listenerFork.merge();
2419
+ } catch (error) {
2420
+ console.error("Async listener failed:", error);
2421
+ }
2422
+ }));
2423
+ await Promise.all(allAsyncTasks);
2424
+ this.asyncEvents = [];
2425
+ }
2426
+ }
2427
+
2428
+ class ModelBase {
2429
+ token = null;
2430
+ setAuthToken(token) {
2431
+ this.token = token;
1962
2432
  }
1963
- commands() {
2433
+ }
2434
+
2435
+ class Model extends ModelBase {
2436
+ context;
2437
+ dataStorage;
2438
+ catchErrorCallback;
2439
+ queryCache = new QueryCache;
2440
+ constructor(context3, dataStorage, catchErrorCallback) {
2441
+ super();
2442
+ this.context = context3;
2443
+ this.dataStorage = dataStorage;
2444
+ this.catchErrorCallback = catchErrorCallback;
2445
+ }
2446
+ async query(queryBuilderFn, authContext) {
1964
2447
  const queryContext = new QueryBuilderContext(this.queryCache, this.dataStorage);
1965
- return this.context.commandsClient(this.client, queryContext, this.dataStorage, this.catchErrorCallback);
2448
+ const queryBuilder = this.context.queryBuilder(queryContext, authContext);
2449
+ const query2 = queryBuilderFn(queryBuilder).toQuery(queryContext);
2450
+ return query2.run(this.dataStorage);
1966
2451
  }
1967
- fork() {
1968
- return new ForkedModel(this.context, this.dataStorage.fork(), this.client, this.catchErrorCallback);
2452
+ subscribe(queryBuilderFn, callback, authContext) {
2453
+ const queryContext = new QueryBuilderContext(this.queryCache, this.dataStorage);
2454
+ const queryBuilder = this.context.queryBuilder(queryContext, authContext);
2455
+ const query2 = queryBuilderFn(queryBuilder).toQuery(queryContext);
2456
+ query2.subscribe(callback);
2457
+ const runPromise = query2.run(this.dataStorage);
2458
+ runPromise.then((result) => {
2459
+ callback(result);
2460
+ });
2461
+ return {
2462
+ unsubscribe: () => {
2463
+ query2.unsubscribe(callback);
2464
+ },
2465
+ result: runPromise
2466
+ };
2467
+ }
2468
+ commands(authContext) {
2469
+ return new Proxy({}, {
2470
+ get: (_, name) => {
2471
+ const element3 = this.context.elements.find((element4) => element4.name === name);
2472
+ if (!element3) {
2473
+ throw new Error(`Element "${String(name)}" not found`);
2474
+ }
2475
+ if (!element3.commandClient) {
2476
+ throw new Error(`Element "${String(name)}" does not have a command client`);
2477
+ }
2478
+ return async (arg) => {
2479
+ const forkedDataStorage = this.dataStorage.fork();
2480
+ const eventPublisher = new EventPublisher(this.context, this.dataStorage, authContext);
2481
+ const publishEvent = async (event3) => {
2482
+ await eventPublisher.publishEvent(event3, forkedDataStorage);
2483
+ };
2484
+ const commandContext = this.context.commandContext(forkedDataStorage, publishEvent, authContext);
2485
+ try {
2486
+ const result = await element3.commandClient(commandContext, authContext)(arg);
2487
+ await forkedDataStorage.merge();
2488
+ eventPublisher.runAsyncListeners();
2489
+ return result;
2490
+ } catch (error) {
2491
+ this.catchErrorCallback(error);
2492
+ return error;
2493
+ }
2494
+ };
2495
+ }
2496
+ });
1969
2497
  }
1970
2498
  get $debug() {
1971
2499
  return {};
1972
2500
  }
1973
2501
  }
1974
2502
 
1975
- class ForkedModel extends Model {
1976
- constructor(context3, dataStorage, client, catchErrorCallback) {
1977
- super(context3, dataStorage, client, catchErrorCallback);
1978
- }
1979
- merge() {}
1980
- }
1981
-
1982
2503
  class RemoteModelClient extends ModelBase {
1983
2504
  context;
1984
2505
  apiBaseUrl;
@@ -1991,14 +2512,107 @@ class RemoteModelClient extends ModelBase {
1991
2512
  this.client = client;
1992
2513
  this.catchErrorCallback = catchErrorCallback;
1993
2514
  }
1994
- async query(queryBuilderFn) {
2515
+ schemaContainsBlobOrFile(schema) {
2516
+ if (!schema)
2517
+ return false;
2518
+ if (schema && typeof schema === "object" && schema.rawShape) {
2519
+ return this.checkShapeForBlobOrFile(schema.rawShape);
2520
+ }
2521
+ if (schema && typeof schema === "object" && schema._shape) {
2522
+ return this.checkShapeForBlobOrFile(schema._shape);
2523
+ }
2524
+ if (schema && typeof schema === "object") {
2525
+ return this.checkShapeForBlobOrFile(schema);
2526
+ }
2527
+ return false;
2528
+ }
2529
+ checkShapeForBlobOrFile(shape) {
2530
+ if (!shape || typeof shape !== "object") {
2531
+ return false;
2532
+ }
2533
+ for (const key in shape) {
2534
+ const element3 = shape[key];
2535
+ if (this.isBlobOrFileElement(element3)) {
2536
+ return true;
2537
+ }
2538
+ if (element3 && element3.parent && this.isBlobOrFileElement(element3.parent)) {
2539
+ return true;
2540
+ }
2541
+ if (element3 && element3._shape) {
2542
+ if (this.checkShapeForBlobOrFile(element3._shape)) {
2543
+ return true;
2544
+ }
2545
+ }
2546
+ if (element3 && element3.parent && element3.parent._shape) {
2547
+ if (this.checkShapeForBlobOrFile(element3.parent._shape)) {
2548
+ return true;
2549
+ }
2550
+ }
2551
+ }
2552
+ return false;
2553
+ }
2554
+ isBlobOrFileElement(element3) {
2555
+ if (!element3) {
2556
+ return false;
2557
+ }
2558
+ const constructorName = element3.constructor?.name;
2559
+ if (constructorName === "ArcBlob" || constructorName === "ArcFile") {
2560
+ return true;
2561
+ }
2562
+ if (element3.validations || element3._validators) {
2563
+ const validators = element3.validations || element3._validators;
2564
+ const hasFileValidator = validators.some((validator) => validator.name === "blob" || validator.name === "file");
2565
+ if (hasFileValidator) {
2566
+ return true;
2567
+ }
2568
+ }
2569
+ return false;
2570
+ }
2571
+ objectToFormData(obj, formData = new FormData, parentKey = "") {
2572
+ for (const key in obj) {
2573
+ if (obj.hasOwnProperty(key)) {
2574
+ const fullKey = parentKey ? `${parentKey}[${key}]` : key;
2575
+ const value = obj[key];
2576
+ if (value instanceof Blob || value instanceof File) {
2577
+ formData.append(fullKey, value);
2578
+ } else if (Array.isArray(value)) {
2579
+ value.forEach((item, index) => {
2580
+ const arrayKey = `${fullKey}[${index}]`;
2581
+ if (item instanceof Blob || item instanceof File) {
2582
+ formData.append(arrayKey, item);
2583
+ } else if (typeof item === "object" && item !== null) {
2584
+ this.objectToFormData(item, formData, arrayKey);
2585
+ } else {
2586
+ formData.append(arrayKey, String(item));
2587
+ }
2588
+ });
2589
+ } else if (typeof value === "object" && value !== null) {
2590
+ this.objectToFormData(value, formData, fullKey);
2591
+ } else {
2592
+ formData.append(fullKey, String(value));
2593
+ }
2594
+ }
2595
+ }
2596
+ return formData;
2597
+ }
2598
+ getAuthHeaders(isFormData = false) {
2599
+ const headers = {};
2600
+ if (!isFormData) {
2601
+ headers["Content-Type"] = "application/json";
2602
+ }
2603
+ if (this.token) {
2604
+ headers.Authorization = `Bearer ${this.token}`;
2605
+ }
2606
+ return headers;
2607
+ }
2608
+ async query(queryBuilderFn, authContext) {
1995
2609
  const queryTracking = {
1996
2610
  element: null,
1997
2611
  queryType: null,
1998
2612
  params: null
1999
2613
  };
2000
2614
  const queryContext = new QueryBuilderContext(new QueryCache, null);
2001
- const originalQueryBuilder = this.context.queryBuilder(queryContext);
2615
+ const originalQueryBuilder = this.context.queryBuilder(queryContext, authContext);
2002
2616
  const queryBuilderProxy = new Proxy(originalQueryBuilder, {
2003
2617
  get: (target, prop) => {
2004
2618
  queryTracking.element = prop;
@@ -2009,16 +2623,15 @@ class RemoteModelClient extends ModelBase {
2009
2623
  const originalMethod = Reflect.get(elementTarget, methodProp);
2010
2624
  return (...args) => {
2011
2625
  queryTracking.params = args;
2012
- return originalMethod(...args);
2013
2626
  };
2014
2627
  }
2015
2628
  });
2016
2629
  }
2017
2630
  });
2018
- const query2 = queryBuilderFn(queryBuilderProxy).toQuery(queryContext);
2631
+ queryBuilderFn(queryBuilderProxy);
2019
2632
  const response = await fetch(`${this.apiBaseUrl}/query`, {
2020
2633
  method: "POST",
2021
- headers: { "Content-Type": "application/json" },
2634
+ headers: this.getAuthHeaders(),
2022
2635
  body: JSON.stringify({
2023
2636
  query: {
2024
2637
  element: queryTracking.element,
@@ -2033,8 +2646,8 @@ class RemoteModelClient extends ModelBase {
2033
2646
  const { result } = await response.json();
2034
2647
  return result;
2035
2648
  }
2036
- subscribe(queryBuilderFn, callback) {
2037
- const result = this.query(queryBuilderFn);
2649
+ subscribe(queryBuilderFn, callback, authContext) {
2650
+ const result = this.query(queryBuilderFn, authContext);
2038
2651
  result.then((initialResult) => {
2039
2652
  callback(initialResult);
2040
2653
  }).catch(this.catchErrorCallback);
@@ -2043,24 +2656,34 @@ class RemoteModelClient extends ModelBase {
2043
2656
  result
2044
2657
  };
2045
2658
  }
2046
- commands() {
2659
+ commands(authContext) {
2047
2660
  const commandsProxy = new Proxy({}, {
2048
2661
  get: (target, prop) => {
2049
- const element = this.context.elements.find((e) => e.name === prop);
2050
- if (!element) {
2662
+ const element3 = this.context.elements.find((e) => e.name === prop);
2663
+ if (!element3) {
2051
2664
  throw new Error(`Command ${String(prop)} not found`);
2052
2665
  }
2053
2666
  return Object.assign(async (argument) => {
2667
+ let body;
2668
+ let headers;
2669
+ const commandParams = element3._params;
2670
+ if (this.schemaContainsBlobOrFile(commandParams)) {
2671
+ body = this.objectToFormData(argument);
2672
+ headers = this.getAuthHeaders(true);
2673
+ } else {
2674
+ body = JSON.stringify(argument);
2675
+ headers = this.getAuthHeaders(false);
2676
+ }
2054
2677
  const response = await fetch(`${this.apiBaseUrl}/command/${String(prop)}`, {
2055
2678
  method: "POST",
2056
- headers: { "Content-Type": "application/json" },
2057
- body: JSON.stringify(argument)
2679
+ headers,
2680
+ body
2058
2681
  });
2059
2682
  if (!response.ok) {
2060
2683
  throw new Error(`Command ${String(prop)} failed: ${response.statusText}`);
2061
2684
  }
2062
2685
  return response.json();
2063
- }, { params: element._params });
2686
+ }, { params: element3._params });
2064
2687
  }
2065
2688
  });
2066
2689
  return commandsProxy;
@@ -2141,6 +2764,236 @@ class RTCClient {
2141
2764
  var rtcClientFactory = (token) => (storage) => {
2142
2765
  return new RTCClient(storage, token);
2143
2766
  };
2767
+ // view/queries/abstract-view-query.ts
2768
+ class ArcViewQuery extends ArcSerializableQuery {
2769
+ view;
2770
+ bindedChangeHandler = this.changeHandler.bind(this);
2771
+ store;
2772
+ constructor(view, params) {
2773
+ super(params);
2774
+ this.view = view;
2775
+ }
2776
+ async run(dataStorage) {
2777
+ const store = dataStorage.getStore(this.view.name);
2778
+ this.store = store;
2779
+ const result = await this.fetch(store);
2780
+ this.lastResult = result;
2781
+ return result;
2782
+ }
2783
+ changeHandler(changes) {
2784
+ let resultChanged = false;
2785
+ for (const change of changes) {
2786
+ const response = this.onChange(change);
2787
+ if (response !== false) {
2788
+ this.lastResult = response;
2789
+ resultChanged = true;
2790
+ }
2791
+ }
2792
+ if (resultChanged)
2793
+ this.nextResult(this.lastResult);
2794
+ }
2795
+ }
2796
+
2797
+ // view/queries/find.ts
2798
+ class QueryViewResult {
2799
+ result;
2800
+ constructor(result) {
2801
+ this.result = result;
2802
+ }
2803
+ get(id3) {
2804
+ return id3 ? this.result.find((r) => r._id === id3) : undefined;
2805
+ }
2806
+ map(callbackfn) {
2807
+ return this.result.map(callbackfn);
2808
+ }
2809
+ toArray() {
2810
+ return this.result;
2811
+ }
2812
+ }
2813
+
2814
+ class ArcViewFindQuery extends ArcViewQuery {
2815
+ params;
2816
+ constructor(view, params) {
2817
+ super(view, params);
2818
+ this.params = params;
2819
+ }
2820
+ async fetch(store) {
2821
+ const results = await store.find(this.params, this.bindedChangeHandler);
2822
+ return this.createResult(results);
2823
+ }
2824
+ checkItem(item) {
2825
+ if (!this.params.where)
2826
+ return true;
2827
+ return Object.entries(this.params.where).every(([key, condition]) => {
2828
+ const value = item[key];
2829
+ if (typeof condition !== "object" || condition === null) {
2830
+ return value === condition;
2831
+ }
2832
+ return Object.entries(condition).every(([operator, operatorValue]) => {
2833
+ switch (operator) {
2834
+ case "$eq":
2835
+ return value === operatorValue;
2836
+ case "$ne":
2837
+ return value !== operatorValue;
2838
+ case "$gt":
2839
+ return typeof value === "number" && typeof operatorValue === "number" && value > operatorValue;
2840
+ case "$gte":
2841
+ return typeof value === "number" && typeof operatorValue === "number" && value >= operatorValue;
2842
+ case "$lt":
2843
+ return typeof value === "number" && typeof operatorValue === "number" && value < operatorValue;
2844
+ case "$lte":
2845
+ return typeof value === "number" && typeof operatorValue === "number" && value <= operatorValue;
2846
+ case "$in":
2847
+ return Array.isArray(operatorValue) && operatorValue.includes(value);
2848
+ case "$nin":
2849
+ return Array.isArray(operatorValue) && !operatorValue.includes(value);
2850
+ case "$exists":
2851
+ return typeof operatorValue === "boolean" ? operatorValue ? value !== undefined : value === undefined : true;
2852
+ default:
2853
+ return true;
2854
+ }
2855
+ });
2856
+ });
2857
+ }
2858
+ onChange(change) {
2859
+ const lastResult = this.lastResult;
2860
+ const lastResultAsArray = lastResult?.toArray() || [];
2861
+ const index = lastResultAsArray.findIndex((e) => e._id === (change.type === "delete" ? change.id : change.id));
2862
+ const isInLastResult = index !== -1;
2863
+ const shouldBeInTheResult = change.type !== "delete" && this.checkItem(change.item);
2864
+ if (isInLastResult && shouldBeInTheResult)
2865
+ return this.createResult(lastResultAsArray.toSpliced(index, 1, change.item));
2866
+ else if (isInLastResult && (change.type === "delete" || !shouldBeInTheResult))
2867
+ return this.createResult(lastResultAsArray.toSpliced(index, 1));
2868
+ else if (!isInLastResult && shouldBeInTheResult)
2869
+ return this.createResult(lastResultAsArray.concat(change.item));
2870
+ return false;
2871
+ }
2872
+ createResult(result) {
2873
+ return new QueryViewResult(result);
2874
+ }
2875
+ }
2876
+
2877
+ // view/query-builders/find.ts
2878
+ class ArcViewFindQueryBuilder {
2879
+ view;
2880
+ queryContext;
2881
+ options;
2882
+ constructor(view, queryContext, options) {
2883
+ this.view = view;
2884
+ this.queryContext = queryContext;
2885
+ this.options = options;
2886
+ }
2887
+ toQuery() {
2888
+ return this.queryContext.cacheQuery(ArcViewFindQuery, [
2889
+ this.view,
2890
+ this.options
2891
+ ]);
2892
+ }
2893
+ run() {
2894
+ return this.queryContext.runQuery(this.toQuery());
2895
+ }
2896
+ }
2897
+
2898
+ // view/queries/find-one.ts
2899
+ class QueryViewFindOneResult {
2900
+ result;
2901
+ constructor(result) {
2902
+ this.result = result;
2903
+ }
2904
+ get() {
2905
+ return this.result;
2906
+ }
2907
+ exists() {
2908
+ return this.result !== undefined;
2909
+ }
2910
+ }
2911
+
2912
+ class ArcViewFindOneQuery extends ArcViewQuery {
2913
+ params;
2914
+ constructor(view, params) {
2915
+ super(view, params);
2916
+ this.params = params;
2917
+ }
2918
+ async fetch(store) {
2919
+ const results = await store.find({ ...this.params, limit: 1 }, this.bindedChangeHandler);
2920
+ return this.createResult(results[0]);
2921
+ }
2922
+ checkItem(item) {
2923
+ if (!this.params.where)
2924
+ return true;
2925
+ return Object.entries(this.params.where).every(([key, condition]) => {
2926
+ const value = item[key];
2927
+ if (typeof condition !== "object" || condition === null) {
2928
+ return value === condition;
2929
+ }
2930
+ return Object.entries(condition).every(([operator, operatorValue]) => {
2931
+ switch (operator) {
2932
+ case "$eq":
2933
+ return value === operatorValue;
2934
+ case "$ne":
2935
+ return value !== operatorValue;
2936
+ case "$gt":
2937
+ return typeof value === "number" && typeof operatorValue === "number" && value > operatorValue;
2938
+ case "$gte":
2939
+ return typeof value === "number" && typeof operatorValue === "number" && value >= operatorValue;
2940
+ case "$lt":
2941
+ return typeof value === "number" && typeof operatorValue === "number" && value < operatorValue;
2942
+ case "$lte":
2943
+ return typeof value === "number" && typeof operatorValue === "number" && value <= operatorValue;
2944
+ case "$in":
2945
+ return Array.isArray(operatorValue) && operatorValue.includes(value);
2946
+ case "$nin":
2947
+ return Array.isArray(operatorValue) && !operatorValue.includes(value);
2948
+ case "$exists":
2949
+ return typeof operatorValue === "boolean" ? operatorValue ? value !== undefined : value === undefined : true;
2950
+ default:
2951
+ return true;
2952
+ }
2953
+ });
2954
+ });
2955
+ }
2956
+ onChange(change) {
2957
+ const lastResult = this.lastResult;
2958
+ const currentItem = lastResult?.get();
2959
+ const itemId = change.type === "delete" ? change.id : change.id;
2960
+ const hasCurrentItem = currentItem !== undefined;
2961
+ const isCurrentItem = hasCurrentItem && currentItem._id === itemId;
2962
+ const shouldBeTheResult = change.type !== "delete" && this.checkItem(change.item);
2963
+ if (isCurrentItem && shouldBeTheResult)
2964
+ return this.createResult(change.item);
2965
+ else if (isCurrentItem && (change.type === "delete" || !shouldBeTheResult))
2966
+ return this.createResult(undefined);
2967
+ else if (!hasCurrentItem && shouldBeTheResult)
2968
+ return this.createResult(change.item);
2969
+ return false;
2970
+ }
2971
+ createResult(result) {
2972
+ return new QueryViewFindOneResult(result);
2973
+ }
2974
+ }
2975
+
2976
+ // view/query-builders/find-one.ts
2977
+ class ArcViewFindOneQueryBuilder {
2978
+ view;
2979
+ queryContext;
2980
+ options;
2981
+ constructor(view, queryContext, options) {
2982
+ this.view = view;
2983
+ this.queryContext = queryContext;
2984
+ this.options = options;
2985
+ }
2986
+ toQuery() {
2987
+ return this.queryContext.cacheQuery(ArcViewFindOneQuery, [
2988
+ this.view,
2989
+ this.options
2990
+ ]);
2991
+ }
2992
+ run() {
2993
+ return this.queryContext.runQuery(this.toQuery());
2994
+ }
2995
+ }
2996
+
2144
2997
  // view/view.ts
2145
2998
  class ArcView extends ArcContextElementWithStore {
2146
2999
  name;
@@ -2149,6 +3002,8 @@ class ArcView extends ArcContextElementWithStore {
2149
3002
  _description;
2150
3003
  _elements;
2151
3004
  _handler;
3005
+ _isAsync = false;
3006
+ restrictions;
2152
3007
  constructor(name, id3, schema) {
2153
3008
  super();
2154
3009
  this.name = name;
@@ -2158,6 +3013,11 @@ class ArcView extends ArcContextElementWithStore {
2158
3013
  storeSchema = () => {
2159
3014
  return arcObjectToStoreSchema(this.name, this.schema);
2160
3015
  };
3016
+ auth(restrictionsFn) {
3017
+ const clone = this.clone();
3018
+ clone.restrictions = restrictionsFn;
3019
+ return clone;
3020
+ }
2161
3021
  use(elements) {
2162
3022
  const clone = this.clone();
2163
3023
  clone._elements = elements;
@@ -2168,71 +3028,113 @@ class ArcView extends ArcContextElementWithStore {
2168
3028
  clone._description = description;
2169
3029
  return clone;
2170
3030
  }
3031
+ async() {
3032
+ const clone = this.clone();
3033
+ clone._isAsync = true;
3034
+ return clone;
3035
+ }
2171
3036
  handle(handler) {
2172
3037
  const clone = this.clone();
2173
3038
  clone._handler = handler;
2174
3039
  return clone;
2175
3040
  }
2176
- queryBuilder = (context3) => {
3041
+ applyRestrictions(authContext, options) {
3042
+ if (!this.restrictions) {
3043
+ return options;
3044
+ }
3045
+ const restrictions = this.restrictions(authContext);
3046
+ return {
3047
+ ...options,
3048
+ where: {
3049
+ ...options.where,
3050
+ ...restrictions
3051
+ }
3052
+ };
3053
+ }
3054
+ queryBuilder = (context3, authContext) => {
2177
3055
  return {
2178
3056
  find: (options) => {
2179
- throw new Error("Not implemented");
3057
+ const restrictedOptions = this.applyRestrictions(authContext, options || {});
3058
+ return new ArcViewFindQueryBuilder(this, context3, restrictedOptions);
3059
+ },
3060
+ findOne: (where) => {
3061
+ const restrictedOptions = this.applyRestrictions(authContext, {
3062
+ where,
3063
+ limit: 1
3064
+ });
3065
+ return new ArcViewFindOneQueryBuilder(this, context3, restrictedOptions);
2180
3066
  }
2181
3067
  };
2182
3068
  };
2183
- commandContext = (dataStorage, publishEvent) => {
3069
+ commandContext = (dataStorage, publishEvent, authContext) => {
2184
3070
  const store = dataStorage.getStore(this.name);
2185
3071
  return {
2186
3072
  find: async (options) => {
2187
- return store.find(options);
3073
+ const restrictedOptions = this.applyRestrictions(authContext, options);
3074
+ return store.find(restrictedOptions);
2188
3075
  },
2189
3076
  findOne: async (where) => {
2190
- const result = await store.find({
3077
+ const restrictedOptions = this.applyRestrictions(authContext, {
2191
3078
  where,
2192
3079
  limit: 1
2193
3080
  });
3081
+ const result = await store.find(restrictedOptions);
2194
3082
  return result[0];
2195
3083
  }
2196
3084
  };
2197
3085
  };
2198
- observer = () => {
3086
+ observer = (authContext) => {
2199
3087
  return Object.entries(this._handler ?? {}).reduce((acc, [key, value]) => {
2200
- acc[key] = (event3, dataStorage) => {
2201
- const store = dataStorage.getStore(this.name);
2202
- const ctx = {
2203
- remove: async (id3) => {
2204
- await store.remove(id3);
2205
- return { success: true };
2206
- },
2207
- set: async (id3, data) => {
2208
- const parsed = this.schema.parse(data);
2209
- const body = {
2210
- _id: id3,
2211
- lastUpdate: new Date().toISOString(),
2212
- ...parsed
2213
- };
2214
- await store.set(body);
2215
- return { success: true };
2216
- },
2217
- find: async (options) => {
2218
- return store.find(options);
2219
- },
2220
- findOne: async (where) => {
2221
- const result = await store.find({
2222
- where,
2223
- limit: 1
2224
- });
2225
- return result[0];
2226
- },
2227
- modify: async (id3, data) => {
2228
- const deserialized = this.schema.serializePartial(data);
2229
- const { from, to } = await store.modify(id3, {
2230
- ...deserialized,
2231
- lastUpdate: new Date().toISOString()
2232
- });
2233
- }
2234
- };
2235
- value(ctx, event3);
3088
+ acc[key] = {
3089
+ handler: async (event3, dataStorage, publishEvent) => {
3090
+ const store = dataStorage.getStore(this.name);
3091
+ const ctx = {
3092
+ remove: async (id3) => {
3093
+ await store.remove(id3);
3094
+ return { success: true };
3095
+ },
3096
+ set: async (id3, data) => {
3097
+ const parsed = this.schema.parse(data);
3098
+ const body = {
3099
+ _id: id3,
3100
+ lastUpdate: new Date().toISOString(),
3101
+ ...parsed
3102
+ };
3103
+ await store.set(body);
3104
+ await publishEvent({
3105
+ type: "set",
3106
+ to: body
3107
+ });
3108
+ return { success: true };
3109
+ },
3110
+ find: async (options) => {
3111
+ return store.find(options);
3112
+ },
3113
+ findOne: async (where) => {
3114
+ const result = await store.find({
3115
+ where,
3116
+ limit: 1
3117
+ });
3118
+ return result[0];
3119
+ },
3120
+ modify: async (id3, data) => {
3121
+ const deserialized = this.schema.serializePartial(data);
3122
+ const { from, to } = await store.modify(id3, {
3123
+ ...deserialized,
3124
+ lastUpdate: new Date().toISOString()
3125
+ });
3126
+ await publishEvent({
3127
+ type: "modify",
3128
+ changes: deserialized,
3129
+ from,
3130
+ to
3131
+ });
3132
+ },
3133
+ $auth: authContext
3134
+ };
3135
+ await value(ctx, event3);
3136
+ },
3137
+ isAsync: this._isAsync
2236
3138
  };
2237
3139
  return acc;
2238
3140
  }, {});
@@ -2242,6 +3144,8 @@ class ArcView extends ArcContextElementWithStore {
2242
3144
  clone._description = this._description;
2243
3145
  clone._elements = this._elements;
2244
3146
  clone._handler = this._handler;
3147
+ clone._isAsync = this._isAsync;
3148
+ clone.restrictions = this.restrictions;
2245
3149
  return clone;
2246
3150
  }
2247
3151
  }
@@ -2257,8 +3161,11 @@ export {
2257
3161
  reactive,
2258
3162
  object,
2259
3163
  number,
3164
+ listener,
2260
3165
  id,
3166
+ file,
2261
3167
  event,
3168
+ deepMerge,
2262
3169
  date,
2263
3170
  customId,
2264
3171
  createSQLiteAdapterFactory,
@@ -2266,6 +3173,7 @@ export {
2266
3173
  command,
2267
3174
  collection,
2268
3175
  boolean,
3176
+ blob,
2269
3177
  array,
2270
3178
  StoreState,
2271
3179
  SQLiteAdapter,
@@ -2277,7 +3185,6 @@ export {
2277
3185
  MasterStoreState,
2278
3186
  MasterDataStorage,
2279
3187
  ForkedStoreState,
2280
- ForkedModel,
2281
3188
  ForkedDataStorage,
2282
3189
  DataStorage,
2283
3190
  ArcView,
@@ -2289,15 +3196,20 @@ export {
2289
3196
  ArcOptional,
2290
3197
  ArcObject,
2291
3198
  ArcNumber,
3199
+ ArcListener,
2292
3200
  ArcId,
3201
+ ArcFile,
2293
3202
  ArcEvent,
2294
3203
  ArcDate,
2295
3204
  ArcCustomId,
3205
+ ArcContextElementWithStore,
3206
+ ArcContextElement,
2296
3207
  ArcContext,
2297
3208
  ArcCommand,
2298
3209
  ArcCollectionQuery,
2299
3210
  ArcCollection,
2300
3211
  ArcBranded,
2301
3212
  ArcBoolean,
3213
+ ArcBlob,
2302
3214
  ArcArray
2303
3215
  };