@arcote.tech/arc 0.1.2 → 0.1.3

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 +11 -11
  3. package/dist/context/context.d.ts +7 -7
  4. package/dist/context/element.d.ts +26 -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 +1098 -197
  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 +24 -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,7 +1278,7 @@ class ArcCommand extends ArcContextElement {
972
1278
  name;
973
1279
  _description;
974
1280
  _params;
975
- _result;
1281
+ _results;
976
1282
  _elements;
977
1283
  _handler;
978
1284
  constructor(name) {
@@ -994,9 +1300,9 @@ class ArcCommand extends ArcContextElement {
994
1300
  clone._params = schema instanceof ArcObject ? schema : object(schema);
995
1301
  return clone;
996
1302
  }
997
- withResult(schema) {
1303
+ withResult(...schemas) {
998
1304
  const clone = this.clone();
999
- clone._result = object(schema);
1305
+ clone._results = schemas.map((schema) => object(schema));
1000
1306
  return clone;
1001
1307
  }
1002
1308
  handle(handler) {
@@ -1014,7 +1320,7 @@ class ArcCommand extends ArcContextElement {
1014
1320
  const clone = new ArcCommand(this.name);
1015
1321
  clone._description = this._description;
1016
1322
  clone._params = this._params;
1017
- clone._result = this._result;
1323
+ clone._results = this._results;
1018
1324
  clone._handler = this._handler;
1019
1325
  clone._elements = this._elements;
1020
1326
  return clone;
@@ -1026,20 +1332,34 @@ function command(name) {
1026
1332
  // context/context.ts
1027
1333
  class ArcContext {
1028
1334
  elements;
1029
- eventListeners;
1030
1335
  constructor(elements) {
1031
1336
  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
- });
1337
+ }
1338
+ getSyncListeners(eventType, authContext) {
1339
+ const listeners = [];
1340
+ this.elements.forEach((element2) => {
1341
+ if (element2.observer) {
1342
+ const handlers = element2.observer(authContext);
1343
+ const config = handlers[eventType];
1344
+ if (config && !config.isAsync) {
1345
+ listeners.push(config.handler);
1346
+ }
1347
+ }
1348
+ });
1349
+ return listeners;
1350
+ }
1351
+ getAsyncListeners(eventType, authContext) {
1352
+ const listeners = [];
1353
+ this.elements.forEach((element2) => {
1354
+ if (element2.observer) {
1355
+ const handlers = element2.observer(authContext);
1356
+ const config = handlers[eventType];
1357
+ if (config && config.isAsync) {
1358
+ listeners.push(config.handler);
1359
+ }
1041
1360
  }
1042
1361
  });
1362
+ return listeners;
1043
1363
  }
1044
1364
  pipe(newElements) {
1045
1365
  return new ArcContext([
@@ -1047,73 +1367,44 @@ class ArcContext {
1047
1367
  ...newElements.filter(Boolean)
1048
1368
  ]);
1049
1369
  }
1050
- queryBuilder(queryContext) {
1370
+ queryBuilder(queryContext, authContext) {
1051
1371
  return new Proxy({}, {
1052
1372
  get: (target, name) => {
1053
- const element = this.elements.find((element2) => element2.name === name);
1054
- if (!element) {
1373
+ const element2 = this.elements.find((element3) => element3.name === name);
1374
+ if (!element2) {
1055
1375
  throw new Error(`Element "${String(name)}" not found`);
1056
1376
  }
1057
- if (!element.queryBuilder) {
1377
+ if (!element2.queryBuilder) {
1058
1378
  throw new Error(`Element "${String(name)}" does not have a query builder`);
1059
1379
  }
1060
- return element.queryBuilder(queryContext);
1380
+ return element2.queryBuilder(queryContext, authContext);
1061
1381
  }
1062
1382
  });
1063
1383
  }
1064
- commandContext(client, dataStorage) {
1384
+ commandContext(dataStorage, publishEvent, authContext) {
1065
1385
  return new Proxy({}, {
1066
1386
  get: (target, name) => {
1067
- if (name === "$client") {
1068
- return client;
1387
+ if (name === "$auth") {
1388
+ return authContext;
1069
1389
  }
1070
- const element = this.elements.find((element2) => element2.name === name);
1071
- if (!element) {
1390
+ const element2 = this.elements.find((element3) => element3.name === name);
1391
+ if (!element2) {
1072
1392
  throw new Error(`Element "${String(name)}" not found`);
1073
1393
  }
1074
- if (!element.commandContext) {
1394
+ if (!element2.commandContext) {
1075
1395
  throw new Error(`Element "${String(name)}" does not have a command context`);
1076
1396
  }
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
- };
1397
+ return element2.commandContext(dataStorage, publishEvent, authContext);
1107
1398
  }
1108
1399
  });
1109
1400
  }
1110
1401
  }
1111
1402
  async function context(elementsPromise) {
1112
- const elements = await Promise.all(elementsPromise.map(async (element) => {
1113
- if (element instanceof Promise) {
1114
- return (await element).default;
1403
+ const elements = await Promise.all(elementsPromise.map(async (element2) => {
1404
+ if (element2 instanceof Promise) {
1405
+ return (await element2).default;
1115
1406
  }
1116
- return element;
1407
+ return element2;
1117
1408
  })).then((elements2) => elements2.filter(Boolean));
1118
1409
  return new ArcContext(elements);
1119
1410
  }
@@ -1150,12 +1441,44 @@ var eventId = id("event");
1150
1441
  class ArcEvent extends ArcContextElementWithStore {
1151
1442
  name;
1152
1443
  payload;
1444
+ _restrictions;
1153
1445
  storeSchema = () => eventStoreSchema;
1154
1446
  constructor(name, payload) {
1155
1447
  super();
1156
1448
  this.name = name;
1157
1449
  this.payload = payload;
1158
1450
  }
1451
+ restrictions(authContext) {
1452
+ if (this._restrictions) {
1453
+ const eventRestrictions = this._restrictions(authContext);
1454
+ return {
1455
+ read: eventRestrictions.read,
1456
+ write: eventRestrictions.write,
1457
+ modify: false,
1458
+ delete: false
1459
+ };
1460
+ }
1461
+ const isSystemUser = authContext.roles.includes("system") || authContext.roles.includes("admin");
1462
+ if (!isSystemUser) {
1463
+ return {
1464
+ read: false,
1465
+ write: false,
1466
+ modify: false,
1467
+ delete: false
1468
+ };
1469
+ }
1470
+ return {
1471
+ read: true,
1472
+ write: true,
1473
+ modify: false,
1474
+ delete: false
1475
+ };
1476
+ }
1477
+ auth(restrictionsFn) {
1478
+ const event = new ArcEvent(this.name, this.payload);
1479
+ event._restrictions = restrictionsFn;
1480
+ return event;
1481
+ }
1159
1482
  commandContext = (dataStorage, publishEvent) => {
1160
1483
  return {
1161
1484
  emit: async (payload) => {
@@ -1696,10 +2019,7 @@ class MasterDataStorage extends DataStorage {
1696
2019
  }
1697
2020
  getStore(storeName) {
1698
2021
  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));
2022
+ this.stores.set(storeName, new MasterStoreState(storeName, this));
1703
2023
  }
1704
2024
  return this.stores.get(storeName);
1705
2025
  }
@@ -1730,6 +2050,37 @@ class SQLiteReadTransaction {
1730
2050
  this.db = db;
1731
2051
  this.tables = tables;
1732
2052
  }
2053
+ deserializeValue(value, column) {
2054
+ if (value === null || value === undefined)
2055
+ return null;
2056
+ switch (column.type.toLowerCase()) {
2057
+ case "json":
2058
+ case "text": {
2059
+ try {
2060
+ const parsed = JSON.parse(value);
2061
+ if (typeof parsed === "object" || Array.isArray(parsed)) {
2062
+ return parsed;
2063
+ }
2064
+ return value;
2065
+ } catch {
2066
+ return value;
2067
+ }
2068
+ }
2069
+ case "datetime":
2070
+ case "timestamp":
2071
+ return new Date(value);
2072
+ default:
2073
+ return value;
2074
+ }
2075
+ }
2076
+ deserializeRow(row, table) {
2077
+ const result = {};
2078
+ for (const column of table.columns) {
2079
+ const value = row[column.name];
2080
+ result[column.name] = this.deserializeValue(value, column);
2081
+ }
2082
+ return result;
2083
+ }
1733
2084
  getId(store, id3) {
1734
2085
  return id3;
1735
2086
  }
@@ -1797,6 +2148,10 @@ class SQLiteReadTransaction {
1797
2148
  const { where, limit, offset, orderBy } = options || {};
1798
2149
  const whereClause = this.buildWhereClause(where);
1799
2150
  const orderByClause = this.buildOrderByClause(orderBy);
2151
+ const table = this.tables.get(store);
2152
+ if (!table) {
2153
+ throw new Error(`Store ${store} not found`);
2154
+ }
1800
2155
  const query2 = `
1801
2156
  SELECT *
1802
2157
  FROM ${store}
@@ -1805,7 +2160,8 @@ class SQLiteReadTransaction {
1805
2160
  ${limit ? `LIMIT ${limit}` : ""}
1806
2161
  ${offset ? `OFFSET ${offset}` : ""}
1807
2162
  `;
1808
- return await this.db.exec(query2, whereClause.params);
2163
+ const rows = await this.db.exec(query2, whereClause.params);
2164
+ return rows.map((row) => this.deserializeRow(row, table));
1809
2165
  }
1810
2166
  }
1811
2167
 
@@ -1819,23 +2175,23 @@ class SQLiteReadWriteTransaction extends SQLiteReadTransaction {
1819
2175
  await this.db.exec(query2, [new Date().toISOString(), id3]);
1820
2176
  }
1821
2177
  async set(store, item) {
1822
- const schema = this.tables.get(store);
1823
- if (!schema) {
2178
+ const table = this.tables.get(store);
2179
+ if (!table) {
1824
2180
  throw new Error(`Store ${store} not found`);
1825
2181
  }
1826
2182
  const now = new Date().toISOString();
1827
- const columnNames = schema.columns.map((col) => col.name);
1828
- const values = schema.columns.map((column) => {
2183
+ const columnNames = table.columns.map((col) => col.name);
2184
+ const values = table.columns.map((column) => {
1829
2185
  let value = item[column.name];
1830
2186
  if (value === undefined && column.default !== undefined) {
1831
2187
  value = column.default;
1832
2188
  }
1833
- return this.serializeValue(value);
2189
+ return this.serializeValue(value, column);
1834
2190
  });
1835
2191
  const placeholders = columnNames.map(() => "?").join(", ");
1836
2192
  const sql = `
1837
- INSERT OR REPLACE INTO ${schema.name}
1838
- (${columnNames.join(", ")})
2193
+ INSERT OR REPLACE INTO ${table.name}
2194
+ (${columnNames.map((c) => `"${c}"`).join(", ")})
1839
2195
  VALUES (${placeholders})
1840
2196
  `;
1841
2197
  await this.db.exec(sql, values);
@@ -1843,16 +2199,21 @@ class SQLiteReadWriteTransaction extends SQLiteReadTransaction {
1843
2199
  async commit() {
1844
2200
  return Promise.resolve();
1845
2201
  }
1846
- serializeValue(value) {
2202
+ serializeValue(value, column) {
1847
2203
  if (value === null || value === undefined)
1848
2204
  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);
2205
+ switch (column.type.toLowerCase()) {
2206
+ case "json":
2207
+ return JSON.stringify(value);
2208
+ default:
2209
+ if (value instanceof Date) {
2210
+ return value.toISOString();
2211
+ }
2212
+ if (Array.isArray(value) || typeof value === "object") {
2213
+ return JSON.stringify(value);
2214
+ }
2215
+ return value;
1854
2216
  }
1855
- return value;
1856
2217
  }
1857
2218
  }
1858
2219
 
@@ -1863,9 +2224,9 @@ class SQLiteAdapter {
1863
2224
  constructor(db, context3) {
1864
2225
  this.db = db;
1865
2226
  this.context = context3;
1866
- this.context.elements.forEach((element) => {
1867
- if ("storeSchema" in element && typeof element.storeSchema === "function") {
1868
- element.storeSchema().tables.forEach((table) => {
2227
+ this.context.elements.forEach((element3) => {
2228
+ if ("storeSchema" in element3 && typeof element3.storeSchema === "function") {
2229
+ element3.storeSchema().tables.forEach((table) => {
1869
2230
  this.tables.set(table.name, table);
1870
2231
  });
1871
2232
  }
@@ -1873,9 +2234,9 @@ class SQLiteAdapter {
1873
2234
  }
1874
2235
  async initialize() {
1875
2236
  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();
2237
+ for (const element3 of this.context.elements) {
2238
+ if ("storeSchema" in element3 && typeof element3.storeSchema === "function") {
2239
+ const schema = element3.storeSchema();
1879
2240
  stores.add(schema);
1880
2241
  }
1881
2242
  }
@@ -1921,62 +2282,212 @@ var createSQLiteAdapterFactory = (db) => {
1921
2282
  return adapter;
1922
2283
  };
1923
2284
  };
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) {
2285
+ // listener/listener.ts
2286
+ class ArcListener extends ArcContextElement {
2287
+ name;
2288
+ _description;
2289
+ _elements;
2290
+ _eventElements;
2291
+ _handler;
2292
+ _isAsync = false;
2293
+ constructor(name) {
1935
2294
  super();
1936
- this.context = context3;
1937
- this.dataStorage = dataStorage;
1938
- this.client = client;
1939
- this.catchErrorCallback = catchErrorCallback;
1940
- }
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);
1946
- }
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
1961
- };
2295
+ this.name = name;
1962
2296
  }
1963
- commands() {
1964
- const queryContext = new QueryBuilderContext(this.queryCache, this.dataStorage);
1965
- return this.context.commandsClient(this.client, queryContext, this.dataStorage, this.catchErrorCallback);
2297
+ use(elements) {
2298
+ const clone = this.clone();
2299
+ clone._elements = elements;
2300
+ return clone;
1966
2301
  }
1967
- fork() {
1968
- return new ForkedModel(this.context, this.dataStorage.fork(), this.client, this.catchErrorCallback);
2302
+ description(description) {
2303
+ const clone = this.clone();
2304
+ clone._description = description;
2305
+ return clone;
1969
2306
  }
1970
- get $debug() {
1971
- return {};
2307
+ listenTo(events) {
2308
+ const clone = this.clone();
2309
+ clone._eventElements = events;
2310
+ return clone;
2311
+ }
2312
+ async() {
2313
+ const clone = this.clone();
2314
+ clone._isAsync = true;
2315
+ return clone;
2316
+ }
2317
+ handle(handler) {
2318
+ const clone = this.clone();
2319
+ clone._handler = handler;
2320
+ return clone;
2321
+ }
2322
+ observer = (authContext) => {
2323
+ if (!this._handler || !this._elements || !this._eventElements) {
2324
+ return {};
2325
+ }
2326
+ const eventTypes = this._eventElements.filter((element3) => element3 instanceof ArcEvent).map((event3) => event3.name);
2327
+ return eventTypes.reduce((acc, eventType) => {
2328
+ acc[eventType] = {
2329
+ handler: async (event3, dataStorage, publishEvent) => {
2330
+ const ctx = new Proxy({}, {
2331
+ get: (target, name) => {
2332
+ if (name === "$auth") {
2333
+ return authContext;
2334
+ }
2335
+ const element3 = this._elements.find((element4) => element4.name === name);
2336
+ if (!element3) {
2337
+ throw new Error(`Element "${String(name)}" not found in listener "${this.name}"`);
2338
+ }
2339
+ if (!element3.commandContext) {
2340
+ throw new Error(`Element "${String(name)}" does not have a command context`);
2341
+ }
2342
+ return element3.commandContext(dataStorage, publishEvent, authContext);
2343
+ }
2344
+ });
2345
+ await this._handler(ctx, event3);
2346
+ },
2347
+ isAsync: this._isAsync
2348
+ };
2349
+ return acc;
2350
+ }, {});
2351
+ };
2352
+ clone() {
2353
+ const clone = new ArcListener(this.name);
2354
+ clone._description = this._description;
2355
+ clone._elements = this._elements;
2356
+ clone._eventElements = this._eventElements;
2357
+ clone._handler = this._handler;
2358
+ clone._isAsync = this._isAsync;
2359
+ return clone;
2360
+ }
2361
+ }
2362
+ function listener(name) {
2363
+ return new ArcListener(name);
2364
+ }
2365
+ // model/model.ts
2366
+ class EventPublisher {
2367
+ context;
2368
+ dataStorage;
2369
+ authContext;
2370
+ asyncEvents = [];
2371
+ constructor(context3, dataStorage, authContext) {
2372
+ this.context = context3;
2373
+ this.dataStorage = dataStorage;
2374
+ this.authContext = authContext;
2375
+ }
2376
+ async publishEvent(event3, commandDataStorage) {
2377
+ const recursivePublishEvent = async (childEvent) => {
2378
+ await this.publishEvent(childEvent, commandDataStorage);
2379
+ };
2380
+ const syncListeners = this.context.getSyncListeners(event3.type, this.authContext);
2381
+ for (const listener3 of syncListeners) {
2382
+ try {
2383
+ await listener3(event3, commandDataStorage, recursivePublishEvent);
2384
+ } catch (error) {
2385
+ console.error("Sync listener failed:", error);
2386
+ throw error;
2387
+ }
2388
+ }
2389
+ const asyncListeners = this.context.getAsyncListeners(event3.type, this.authContext);
2390
+ if (asyncListeners.length > 0) {
2391
+ this.asyncEvents.push({
2392
+ event: event3,
2393
+ listeners: asyncListeners,
2394
+ authContext: this.authContext
2395
+ });
2396
+ }
2397
+ }
2398
+ async runAsyncListeners() {
2399
+ const allAsyncTasks = this.asyncEvents.flatMap(({ event: event3, listeners, authContext }) => listeners.map(async (listener3) => {
2400
+ const listenerFork = this.dataStorage.fork();
2401
+ const asyncPublishEvent = async (childEvent) => {
2402
+ const childPublisher = new EventPublisher(this.context, listenerFork, authContext);
2403
+ await childPublisher.publishEvent(childEvent, listenerFork);
2404
+ await childPublisher.runAsyncListeners();
2405
+ };
2406
+ try {
2407
+ await listener3(event3, listenerFork, asyncPublishEvent);
2408
+ await listenerFork.merge();
2409
+ } catch (error) {
2410
+ console.error("Async listener failed:", error);
2411
+ }
2412
+ }));
2413
+ await Promise.all(allAsyncTasks);
2414
+ this.asyncEvents = [];
1972
2415
  }
1973
2416
  }
1974
2417
 
1975
- class ForkedModel extends Model {
1976
- constructor(context3, dataStorage, client, catchErrorCallback) {
1977
- super(context3, dataStorage, client, catchErrorCallback);
2418
+ class ModelBase {
2419
+ token = null;
2420
+ setAuthToken(token) {
2421
+ this.token = token;
2422
+ }
2423
+ }
2424
+
2425
+ class Model extends ModelBase {
2426
+ context;
2427
+ dataStorage;
2428
+ catchErrorCallback;
2429
+ queryCache = new QueryCache;
2430
+ constructor(context3, dataStorage, catchErrorCallback) {
2431
+ super();
2432
+ this.context = context3;
2433
+ this.dataStorage = dataStorage;
2434
+ this.catchErrorCallback = catchErrorCallback;
2435
+ }
2436
+ async query(queryBuilderFn, authContext) {
2437
+ const queryContext = new QueryBuilderContext(this.queryCache, this.dataStorage);
2438
+ const queryBuilder = this.context.queryBuilder(queryContext, authContext);
2439
+ const query2 = queryBuilderFn(queryBuilder).toQuery(queryContext);
2440
+ return query2.run(this.dataStorage);
2441
+ }
2442
+ subscribe(queryBuilderFn, callback, authContext) {
2443
+ const queryContext = new QueryBuilderContext(this.queryCache, this.dataStorage);
2444
+ const queryBuilder = this.context.queryBuilder(queryContext, authContext);
2445
+ const query2 = queryBuilderFn(queryBuilder).toQuery(queryContext);
2446
+ query2.subscribe(callback);
2447
+ const runPromise = query2.run(this.dataStorage);
2448
+ runPromise.then((result) => {
2449
+ callback(result);
2450
+ });
2451
+ return {
2452
+ unsubscribe: () => {
2453
+ query2.unsubscribe(callback);
2454
+ },
2455
+ result: runPromise
2456
+ };
2457
+ }
2458
+ commands(authContext) {
2459
+ return new Proxy({}, {
2460
+ get: (_, name) => {
2461
+ const element3 = this.context.elements.find((element4) => element4.name === name);
2462
+ if (!element3) {
2463
+ throw new Error(`Element "${String(name)}" not found`);
2464
+ }
2465
+ if (!element3.commandClient) {
2466
+ throw new Error(`Element "${String(name)}" does not have a command client`);
2467
+ }
2468
+ return async (arg) => {
2469
+ const forkedDataStorage = this.dataStorage.fork();
2470
+ const eventPublisher = new EventPublisher(this.context, this.dataStorage, authContext);
2471
+ const publishEvent = async (event3) => {
2472
+ await eventPublisher.publishEvent(event3, forkedDataStorage);
2473
+ };
2474
+ const commandContext = this.context.commandContext(forkedDataStorage, publishEvent, authContext);
2475
+ try {
2476
+ const result = await element3.commandClient(commandContext, authContext)(arg);
2477
+ await forkedDataStorage.merge();
2478
+ eventPublisher.runAsyncListeners();
2479
+ return result;
2480
+ } catch (error) {
2481
+ this.catchErrorCallback(error);
2482
+ return error;
2483
+ }
2484
+ };
2485
+ }
2486
+ });
2487
+ }
2488
+ get $debug() {
2489
+ return {};
1978
2490
  }
1979
- merge() {}
1980
2491
  }
1981
2492
 
1982
2493
  class RemoteModelClient extends ModelBase {
@@ -1991,14 +2502,107 @@ class RemoteModelClient extends ModelBase {
1991
2502
  this.client = client;
1992
2503
  this.catchErrorCallback = catchErrorCallback;
1993
2504
  }
1994
- async query(queryBuilderFn) {
2505
+ schemaContainsBlobOrFile(schema) {
2506
+ if (!schema)
2507
+ return false;
2508
+ if (schema && typeof schema === "object" && schema.rawShape) {
2509
+ return this.checkShapeForBlobOrFile(schema.rawShape);
2510
+ }
2511
+ if (schema && typeof schema === "object" && schema._shape) {
2512
+ return this.checkShapeForBlobOrFile(schema._shape);
2513
+ }
2514
+ if (schema && typeof schema === "object") {
2515
+ return this.checkShapeForBlobOrFile(schema);
2516
+ }
2517
+ return false;
2518
+ }
2519
+ checkShapeForBlobOrFile(shape) {
2520
+ if (!shape || typeof shape !== "object") {
2521
+ return false;
2522
+ }
2523
+ for (const key in shape) {
2524
+ const element3 = shape[key];
2525
+ if (this.isBlobOrFileElement(element3)) {
2526
+ return true;
2527
+ }
2528
+ if (element3 && element3.parent && this.isBlobOrFileElement(element3.parent)) {
2529
+ return true;
2530
+ }
2531
+ if (element3 && element3._shape) {
2532
+ if (this.checkShapeForBlobOrFile(element3._shape)) {
2533
+ return true;
2534
+ }
2535
+ }
2536
+ if (element3 && element3.parent && element3.parent._shape) {
2537
+ if (this.checkShapeForBlobOrFile(element3.parent._shape)) {
2538
+ return true;
2539
+ }
2540
+ }
2541
+ }
2542
+ return false;
2543
+ }
2544
+ isBlobOrFileElement(element3) {
2545
+ if (!element3) {
2546
+ return false;
2547
+ }
2548
+ const constructorName = element3.constructor?.name;
2549
+ if (constructorName === "ArcBlob" || constructorName === "ArcFile") {
2550
+ return true;
2551
+ }
2552
+ if (element3.validations || element3._validators) {
2553
+ const validators = element3.validations || element3._validators;
2554
+ const hasFileValidator = validators.some((validator) => validator.name === "blob" || validator.name === "file");
2555
+ if (hasFileValidator) {
2556
+ return true;
2557
+ }
2558
+ }
2559
+ return false;
2560
+ }
2561
+ objectToFormData(obj, formData = new FormData, parentKey = "") {
2562
+ for (const key in obj) {
2563
+ if (obj.hasOwnProperty(key)) {
2564
+ const fullKey = parentKey ? `${parentKey}[${key}]` : key;
2565
+ const value = obj[key];
2566
+ if (value instanceof Blob || value instanceof File) {
2567
+ formData.append(fullKey, value);
2568
+ } else if (Array.isArray(value)) {
2569
+ value.forEach((item, index) => {
2570
+ const arrayKey = `${fullKey}[${index}]`;
2571
+ if (item instanceof Blob || item instanceof File) {
2572
+ formData.append(arrayKey, item);
2573
+ } else if (typeof item === "object" && item !== null) {
2574
+ this.objectToFormData(item, formData, arrayKey);
2575
+ } else {
2576
+ formData.append(arrayKey, String(item));
2577
+ }
2578
+ });
2579
+ } else if (typeof value === "object" && value !== null) {
2580
+ this.objectToFormData(value, formData, fullKey);
2581
+ } else {
2582
+ formData.append(fullKey, String(value));
2583
+ }
2584
+ }
2585
+ }
2586
+ return formData;
2587
+ }
2588
+ getAuthHeaders(isFormData = false) {
2589
+ const headers = {};
2590
+ if (!isFormData) {
2591
+ headers["Content-Type"] = "application/json";
2592
+ }
2593
+ if (this.token) {
2594
+ headers.Authorization = `Bearer ${this.token}`;
2595
+ }
2596
+ return headers;
2597
+ }
2598
+ async query(queryBuilderFn, authContext) {
1995
2599
  const queryTracking = {
1996
2600
  element: null,
1997
2601
  queryType: null,
1998
2602
  params: null
1999
2603
  };
2000
2604
  const queryContext = new QueryBuilderContext(new QueryCache, null);
2001
- const originalQueryBuilder = this.context.queryBuilder(queryContext);
2605
+ const originalQueryBuilder = this.context.queryBuilder(queryContext, authContext);
2002
2606
  const queryBuilderProxy = new Proxy(originalQueryBuilder, {
2003
2607
  get: (target, prop) => {
2004
2608
  queryTracking.element = prop;
@@ -2009,16 +2613,15 @@ class RemoteModelClient extends ModelBase {
2009
2613
  const originalMethod = Reflect.get(elementTarget, methodProp);
2010
2614
  return (...args) => {
2011
2615
  queryTracking.params = args;
2012
- return originalMethod(...args);
2013
2616
  };
2014
2617
  }
2015
2618
  });
2016
2619
  }
2017
2620
  });
2018
- const query2 = queryBuilderFn(queryBuilderProxy).toQuery(queryContext);
2621
+ queryBuilderFn(queryBuilderProxy);
2019
2622
  const response = await fetch(`${this.apiBaseUrl}/query`, {
2020
2623
  method: "POST",
2021
- headers: { "Content-Type": "application/json" },
2624
+ headers: this.getAuthHeaders(),
2022
2625
  body: JSON.stringify({
2023
2626
  query: {
2024
2627
  element: queryTracking.element,
@@ -2033,8 +2636,8 @@ class RemoteModelClient extends ModelBase {
2033
2636
  const { result } = await response.json();
2034
2637
  return result;
2035
2638
  }
2036
- subscribe(queryBuilderFn, callback) {
2037
- const result = this.query(queryBuilderFn);
2639
+ subscribe(queryBuilderFn, callback, authContext) {
2640
+ const result = this.query(queryBuilderFn, authContext);
2038
2641
  result.then((initialResult) => {
2039
2642
  callback(initialResult);
2040
2643
  }).catch(this.catchErrorCallback);
@@ -2043,24 +2646,34 @@ class RemoteModelClient extends ModelBase {
2043
2646
  result
2044
2647
  };
2045
2648
  }
2046
- commands() {
2649
+ commands(authContext) {
2047
2650
  const commandsProxy = new Proxy({}, {
2048
2651
  get: (target, prop) => {
2049
- const element = this.context.elements.find((e) => e.name === prop);
2050
- if (!element) {
2652
+ const element3 = this.context.elements.find((e) => e.name === prop);
2653
+ if (!element3) {
2051
2654
  throw new Error(`Command ${String(prop)} not found`);
2052
2655
  }
2053
2656
  return Object.assign(async (argument) => {
2657
+ let body;
2658
+ let headers;
2659
+ const commandParams = element3._params;
2660
+ if (this.schemaContainsBlobOrFile(commandParams)) {
2661
+ body = this.objectToFormData(argument);
2662
+ headers = this.getAuthHeaders(true);
2663
+ } else {
2664
+ body = JSON.stringify(argument);
2665
+ headers = this.getAuthHeaders(false);
2666
+ }
2054
2667
  const response = await fetch(`${this.apiBaseUrl}/command/${String(prop)}`, {
2055
2668
  method: "POST",
2056
- headers: { "Content-Type": "application/json" },
2057
- body: JSON.stringify(argument)
2669
+ headers,
2670
+ body
2058
2671
  });
2059
2672
  if (!response.ok) {
2060
2673
  throw new Error(`Command ${String(prop)} failed: ${response.statusText}`);
2061
2674
  }
2062
2675
  return response.json();
2063
- }, { params: element._params });
2676
+ }, { params: element3._params });
2064
2677
  }
2065
2678
  });
2066
2679
  return commandsProxy;
@@ -2141,6 +2754,236 @@ class RTCClient {
2141
2754
  var rtcClientFactory = (token) => (storage) => {
2142
2755
  return new RTCClient(storage, token);
2143
2756
  };
2757
+ // view/queries/abstract-view-query.ts
2758
+ class ArcViewQuery extends ArcSerializableQuery {
2759
+ view;
2760
+ bindedChangeHandler = this.changeHandler.bind(this);
2761
+ store;
2762
+ constructor(view, params) {
2763
+ super(params);
2764
+ this.view = view;
2765
+ }
2766
+ async run(dataStorage) {
2767
+ const store = dataStorage.getStore(this.view.name);
2768
+ this.store = store;
2769
+ const result = await this.fetch(store);
2770
+ this.lastResult = result;
2771
+ return result;
2772
+ }
2773
+ changeHandler(changes) {
2774
+ let resultChanged = false;
2775
+ for (const change of changes) {
2776
+ const response = this.onChange(change);
2777
+ if (response !== false) {
2778
+ this.lastResult = response;
2779
+ resultChanged = true;
2780
+ }
2781
+ }
2782
+ if (resultChanged)
2783
+ this.nextResult(this.lastResult);
2784
+ }
2785
+ }
2786
+
2787
+ // view/queries/find.ts
2788
+ class QueryViewResult {
2789
+ result;
2790
+ constructor(result) {
2791
+ this.result = result;
2792
+ }
2793
+ get(id3) {
2794
+ return id3 ? this.result.find((r) => r._id === id3) : undefined;
2795
+ }
2796
+ map(callbackfn) {
2797
+ return this.result.map(callbackfn);
2798
+ }
2799
+ toArray() {
2800
+ return this.result;
2801
+ }
2802
+ }
2803
+
2804
+ class ArcViewFindQuery extends ArcViewQuery {
2805
+ params;
2806
+ constructor(view, params) {
2807
+ super(view, params);
2808
+ this.params = params;
2809
+ }
2810
+ async fetch(store) {
2811
+ const results = await store.find(this.params, this.bindedChangeHandler);
2812
+ return this.createResult(results);
2813
+ }
2814
+ checkItem(item) {
2815
+ if (!this.params.where)
2816
+ return true;
2817
+ return Object.entries(this.params.where).every(([key, condition]) => {
2818
+ const value = item[key];
2819
+ if (typeof condition !== "object" || condition === null) {
2820
+ return value === condition;
2821
+ }
2822
+ return Object.entries(condition).every(([operator, operatorValue]) => {
2823
+ switch (operator) {
2824
+ case "$eq":
2825
+ return value === operatorValue;
2826
+ case "$ne":
2827
+ return value !== operatorValue;
2828
+ case "$gt":
2829
+ return typeof value === "number" && typeof operatorValue === "number" && value > operatorValue;
2830
+ case "$gte":
2831
+ return typeof value === "number" && typeof operatorValue === "number" && value >= operatorValue;
2832
+ case "$lt":
2833
+ return typeof value === "number" && typeof operatorValue === "number" && value < operatorValue;
2834
+ case "$lte":
2835
+ return typeof value === "number" && typeof operatorValue === "number" && value <= operatorValue;
2836
+ case "$in":
2837
+ return Array.isArray(operatorValue) && operatorValue.includes(value);
2838
+ case "$nin":
2839
+ return Array.isArray(operatorValue) && !operatorValue.includes(value);
2840
+ case "$exists":
2841
+ return typeof operatorValue === "boolean" ? operatorValue ? value !== undefined : value === undefined : true;
2842
+ default:
2843
+ return true;
2844
+ }
2845
+ });
2846
+ });
2847
+ }
2848
+ onChange(change) {
2849
+ const lastResult = this.lastResult;
2850
+ const lastResultAsArray = lastResult?.toArray() || [];
2851
+ const index = lastResultAsArray.findIndex((e) => e._id === (change.type === "delete" ? change.id : change.id));
2852
+ const isInLastResult = index !== -1;
2853
+ const shouldBeInTheResult = change.type !== "delete" && this.checkItem(change.item);
2854
+ if (isInLastResult && shouldBeInTheResult)
2855
+ return this.createResult(lastResultAsArray.toSpliced(index, 1, change.item));
2856
+ else if (isInLastResult && (change.type === "delete" || !shouldBeInTheResult))
2857
+ return this.createResult(lastResultAsArray.toSpliced(index, 1));
2858
+ else if (!isInLastResult && shouldBeInTheResult)
2859
+ return this.createResult(lastResultAsArray.concat(change.item));
2860
+ return false;
2861
+ }
2862
+ createResult(result) {
2863
+ return new QueryViewResult(result);
2864
+ }
2865
+ }
2866
+
2867
+ // view/query-builders/find.ts
2868
+ class ArcViewFindQueryBuilder {
2869
+ view;
2870
+ queryContext;
2871
+ options;
2872
+ constructor(view, queryContext, options) {
2873
+ this.view = view;
2874
+ this.queryContext = queryContext;
2875
+ this.options = options;
2876
+ }
2877
+ toQuery() {
2878
+ return this.queryContext.cacheQuery(ArcViewFindQuery, [
2879
+ this.view,
2880
+ this.options
2881
+ ]);
2882
+ }
2883
+ run() {
2884
+ return this.queryContext.runQuery(this.toQuery());
2885
+ }
2886
+ }
2887
+
2888
+ // view/queries/find-one.ts
2889
+ class QueryViewFindOneResult {
2890
+ result;
2891
+ constructor(result) {
2892
+ this.result = result;
2893
+ }
2894
+ get() {
2895
+ return this.result;
2896
+ }
2897
+ exists() {
2898
+ return this.result !== undefined;
2899
+ }
2900
+ }
2901
+
2902
+ class ArcViewFindOneQuery extends ArcViewQuery {
2903
+ params;
2904
+ constructor(view, params) {
2905
+ super(view, params);
2906
+ this.params = params;
2907
+ }
2908
+ async fetch(store) {
2909
+ const results = await store.find({ ...this.params, limit: 1 }, this.bindedChangeHandler);
2910
+ return this.createResult(results[0]);
2911
+ }
2912
+ checkItem(item) {
2913
+ if (!this.params.where)
2914
+ return true;
2915
+ return Object.entries(this.params.where).every(([key, condition]) => {
2916
+ const value = item[key];
2917
+ if (typeof condition !== "object" || condition === null) {
2918
+ return value === condition;
2919
+ }
2920
+ return Object.entries(condition).every(([operator, operatorValue]) => {
2921
+ switch (operator) {
2922
+ case "$eq":
2923
+ return value === operatorValue;
2924
+ case "$ne":
2925
+ return value !== operatorValue;
2926
+ case "$gt":
2927
+ return typeof value === "number" && typeof operatorValue === "number" && value > operatorValue;
2928
+ case "$gte":
2929
+ return typeof value === "number" && typeof operatorValue === "number" && value >= operatorValue;
2930
+ case "$lt":
2931
+ return typeof value === "number" && typeof operatorValue === "number" && value < operatorValue;
2932
+ case "$lte":
2933
+ return typeof value === "number" && typeof operatorValue === "number" && value <= operatorValue;
2934
+ case "$in":
2935
+ return Array.isArray(operatorValue) && operatorValue.includes(value);
2936
+ case "$nin":
2937
+ return Array.isArray(operatorValue) && !operatorValue.includes(value);
2938
+ case "$exists":
2939
+ return typeof operatorValue === "boolean" ? operatorValue ? value !== undefined : value === undefined : true;
2940
+ default:
2941
+ return true;
2942
+ }
2943
+ });
2944
+ });
2945
+ }
2946
+ onChange(change) {
2947
+ const lastResult = this.lastResult;
2948
+ const currentItem = lastResult?.get();
2949
+ const itemId = change.type === "delete" ? change.id : change.id;
2950
+ const hasCurrentItem = currentItem !== undefined;
2951
+ const isCurrentItem = hasCurrentItem && currentItem._id === itemId;
2952
+ const shouldBeTheResult = change.type !== "delete" && this.checkItem(change.item);
2953
+ if (isCurrentItem && shouldBeTheResult)
2954
+ return this.createResult(change.item);
2955
+ else if (isCurrentItem && (change.type === "delete" || !shouldBeTheResult))
2956
+ return this.createResult(undefined);
2957
+ else if (!hasCurrentItem && shouldBeTheResult)
2958
+ return this.createResult(change.item);
2959
+ return false;
2960
+ }
2961
+ createResult(result) {
2962
+ return new QueryViewFindOneResult(result);
2963
+ }
2964
+ }
2965
+
2966
+ // view/query-builders/find-one.ts
2967
+ class ArcViewFindOneQueryBuilder {
2968
+ view;
2969
+ queryContext;
2970
+ options;
2971
+ constructor(view, queryContext, options) {
2972
+ this.view = view;
2973
+ this.queryContext = queryContext;
2974
+ this.options = options;
2975
+ }
2976
+ toQuery() {
2977
+ return this.queryContext.cacheQuery(ArcViewFindOneQuery, [
2978
+ this.view,
2979
+ this.options
2980
+ ]);
2981
+ }
2982
+ run() {
2983
+ return this.queryContext.runQuery(this.toQuery());
2984
+ }
2985
+ }
2986
+
2144
2987
  // view/view.ts
2145
2988
  class ArcView extends ArcContextElementWithStore {
2146
2989
  name;
@@ -2149,6 +2992,8 @@ class ArcView extends ArcContextElementWithStore {
2149
2992
  _description;
2150
2993
  _elements;
2151
2994
  _handler;
2995
+ _isAsync = false;
2996
+ restrictions;
2152
2997
  constructor(name, id3, schema) {
2153
2998
  super();
2154
2999
  this.name = name;
@@ -2158,6 +3003,11 @@ class ArcView extends ArcContextElementWithStore {
2158
3003
  storeSchema = () => {
2159
3004
  return arcObjectToStoreSchema(this.name, this.schema);
2160
3005
  };
3006
+ auth(restrictionsFn) {
3007
+ const clone = this.clone();
3008
+ clone.restrictions = restrictionsFn;
3009
+ return clone;
3010
+ }
2161
3011
  use(elements) {
2162
3012
  const clone = this.clone();
2163
3013
  clone._elements = elements;
@@ -2168,71 +3018,112 @@ class ArcView extends ArcContextElementWithStore {
2168
3018
  clone._description = description;
2169
3019
  return clone;
2170
3020
  }
3021
+ async() {
3022
+ const clone = this.clone();
3023
+ clone._isAsync = true;
3024
+ return clone;
3025
+ }
2171
3026
  handle(handler) {
2172
3027
  const clone = this.clone();
2173
3028
  clone._handler = handler;
2174
3029
  return clone;
2175
3030
  }
2176
- queryBuilder = (context3) => {
3031
+ applyRestrictions(authContext, options) {
3032
+ if (!this.restrictions) {
3033
+ return options;
3034
+ }
3035
+ const restrictions = this.restrictions(authContext);
3036
+ return {
3037
+ ...options,
3038
+ where: {
3039
+ ...options.where,
3040
+ ...restrictions
3041
+ }
3042
+ };
3043
+ }
3044
+ queryBuilder = (context3, authContext) => {
2177
3045
  return {
2178
3046
  find: (options) => {
2179
- throw new Error("Not implemented");
3047
+ const restrictedOptions = this.applyRestrictions(authContext, options || {});
3048
+ return new ArcViewFindQueryBuilder(this, context3, restrictedOptions);
3049
+ },
3050
+ findOne: (where) => {
3051
+ const restrictedOptions = this.applyRestrictions(authContext, {
3052
+ where,
3053
+ limit: 1
3054
+ });
3055
+ return new ArcViewFindOneQueryBuilder(this, context3, restrictedOptions);
2180
3056
  }
2181
3057
  };
2182
3058
  };
2183
- commandContext = (dataStorage, publishEvent) => {
3059
+ commandContext = (dataStorage, publishEvent, authContext) => {
2184
3060
  const store = dataStorage.getStore(this.name);
2185
3061
  return {
2186
3062
  find: async (options) => {
2187
- return store.find(options);
3063
+ const restrictedOptions = this.applyRestrictions(authContext, options);
3064
+ return store.find(restrictedOptions);
2188
3065
  },
2189
3066
  findOne: async (where) => {
2190
- const result = await store.find({
3067
+ const restrictedOptions = this.applyRestrictions(authContext, {
2191
3068
  where,
2192
3069
  limit: 1
2193
3070
  });
3071
+ const result = await store.find(restrictedOptions);
2194
3072
  return result[0];
2195
3073
  }
2196
3074
  };
2197
3075
  };
2198
- observer = () => {
3076
+ observer = (authContext) => {
2199
3077
  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);
3078
+ acc[key] = {
3079
+ handler: async (event3, dataStorage, publishEvent) => {
3080
+ const store = dataStorage.getStore(this.name);
3081
+ const ctx = {
3082
+ remove: async (id3) => {
3083
+ await store.remove(id3);
3084
+ return { success: true };
3085
+ },
3086
+ set: async (id3, data) => {
3087
+ const parsed = this.schema.parse(data);
3088
+ const body = {
3089
+ _id: id3,
3090
+ lastUpdate: new Date().toISOString(),
3091
+ ...parsed
3092
+ };
3093
+ await store.set(body);
3094
+ await publishEvent({
3095
+ type: "set",
3096
+ to: body
3097
+ });
3098
+ return { success: true };
3099
+ },
3100
+ find: async (options) => {
3101
+ return store.find(options);
3102
+ },
3103
+ findOne: async (where) => {
3104
+ const result = await store.find({
3105
+ where,
3106
+ limit: 1
3107
+ });
3108
+ return result[0];
3109
+ },
3110
+ modify: async (id3, data) => {
3111
+ const deserialized = this.schema.serializePartial(data);
3112
+ const { from, to } = await store.modify(id3, {
3113
+ ...deserialized,
3114
+ lastUpdate: new Date().toISOString()
3115
+ });
3116
+ await publishEvent({
3117
+ type: "modify",
3118
+ changes: deserialized,
3119
+ from,
3120
+ to
3121
+ });
3122
+ }
3123
+ };
3124
+ await value(ctx, event3);
3125
+ },
3126
+ isAsync: this._isAsync
2236
3127
  };
2237
3128
  return acc;
2238
3129
  }, {});
@@ -2242,6 +3133,8 @@ class ArcView extends ArcContextElementWithStore {
2242
3133
  clone._description = this._description;
2243
3134
  clone._elements = this._elements;
2244
3135
  clone._handler = this._handler;
3136
+ clone._isAsync = this._isAsync;
3137
+ clone.restrictions = this.restrictions;
2245
3138
  return clone;
2246
3139
  }
2247
3140
  }
@@ -2257,8 +3150,11 @@ export {
2257
3150
  reactive,
2258
3151
  object,
2259
3152
  number,
3153
+ listener,
2260
3154
  id,
3155
+ file,
2261
3156
  event,
3157
+ deepMerge,
2262
3158
  date,
2263
3159
  customId,
2264
3160
  createSQLiteAdapterFactory,
@@ -2266,6 +3162,7 @@ export {
2266
3162
  command,
2267
3163
  collection,
2268
3164
  boolean,
3165
+ blob,
2269
3166
  array,
2270
3167
  StoreState,
2271
3168
  SQLiteAdapter,
@@ -2277,7 +3174,6 @@ export {
2277
3174
  MasterStoreState,
2278
3175
  MasterDataStorage,
2279
3176
  ForkedStoreState,
2280
- ForkedModel,
2281
3177
  ForkedDataStorage,
2282
3178
  DataStorage,
2283
3179
  ArcView,
@@ -2289,15 +3185,20 @@ export {
2289
3185
  ArcOptional,
2290
3186
  ArcObject,
2291
3187
  ArcNumber,
3188
+ ArcListener,
2292
3189
  ArcId,
3190
+ ArcFile,
2293
3191
  ArcEvent,
2294
3192
  ArcDate,
2295
3193
  ArcCustomId,
3194
+ ArcContextElementWithStore,
3195
+ ArcContextElement,
2296
3196
  ArcContext,
2297
3197
  ArcCommand,
2298
3198
  ArcCollectionQuery,
2299
3199
  ArcCollection,
2300
3200
  ArcBranded,
2301
3201
  ArcBoolean,
3202
+ ArcBlob,
2302
3203
  ArcArray
2303
3204
  };