@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.
- package/dist/collection/collection.d.ts +12 -1
- package/dist/command/command.d.ts +11 -11
- package/dist/context/context.d.ts +7 -7
- package/dist/context/element.d.ts +26 -5
- package/dist/context/event.d.ts +12 -1
- package/dist/context/index.d.ts +1 -0
- package/dist/context/simple-query.d.ts +33 -0
- package/dist/data-storage/data-storage-builder.d.ts +16 -0
- package/dist/data-storage/index.d.ts +2 -0
- package/dist/data-storage/query-processor.d.ts +22 -0
- package/dist/data-storage/store-state-authorized.d.ts +26 -0
- package/dist/db/sqliteAdapter.d.ts +3 -1
- package/dist/elements/blob.d.ts +79 -0
- package/dist/elements/boolean.d.ts +13 -2
- package/dist/elements/date.d.ts +1 -1
- package/dist/elements/file.d.ts +139 -0
- package/dist/elements/index.d.ts +3 -0
- package/dist/elements/object.d.ts +7 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1098 -197
- package/dist/listener/index.d.ts +2 -0
- package/dist/listener/listener.d.ts +23 -0
- package/dist/model/model.d.ts +37 -16
- package/dist/tests/utils/test-model.d.ts +0 -1
- package/dist/view/index.d.ts +4 -0
- package/dist/view/queries/abstract-view-query.d.ts +20 -0
- package/dist/view/queries/find-one.d.ts +18 -0
- package/dist/view/queries/find.d.ts +20 -0
- package/dist/view/queries/index.d.ts +3 -0
- package/dist/view/query-builders/find-one.d.ts +13 -0
- package/dist/view/query-builders/find.d.ts +13 -0
- package/dist/view/query-builders/index.d.ts +2 -0
- package/dist/view/view.d.ts +24 -13
- package/package.json +1 -1
- package/dist/db/index-query.d.ts +0 -3
- package/dist/dist/index.d.ts +0 -513
- package/dist/elements/tests/example.d.ts +0 -6
- 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
|
-
|
|
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(
|
|
658
|
-
if (
|
|
946
|
+
function getSQLiteType(element2) {
|
|
947
|
+
if (element2 instanceof ArcDate) {
|
|
659
948
|
return "TEXT";
|
|
660
949
|
}
|
|
661
|
-
if (
|
|
950
|
+
if (element2 instanceof ArcArray || element2 instanceof ArcObject) {
|
|
662
951
|
return "TEXT";
|
|
663
952
|
}
|
|
664
|
-
if (
|
|
953
|
+
if (element2 instanceof ArcBoolean) {
|
|
665
954
|
return "INTEGER";
|
|
666
955
|
}
|
|
667
|
-
if (
|
|
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,
|
|
962
|
+
const columns = schema.entries().map(([name, element2]) => ({
|
|
674
963
|
name,
|
|
675
|
-
type: getSQLiteType(
|
|
676
|
-
isOptional:
|
|
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
|
-
|
|
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(
|
|
1303
|
+
withResult(...schemas) {
|
|
998
1304
|
const clone = this.clone();
|
|
999
|
-
clone.
|
|
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.
|
|
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
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
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
|
|
1054
|
-
if (!
|
|
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 (!
|
|
1377
|
+
if (!element2.queryBuilder) {
|
|
1058
1378
|
throw new Error(`Element "${String(name)}" does not have a query builder`);
|
|
1059
1379
|
}
|
|
1060
|
-
return
|
|
1380
|
+
return element2.queryBuilder(queryContext, authContext);
|
|
1061
1381
|
}
|
|
1062
1382
|
});
|
|
1063
1383
|
}
|
|
1064
|
-
commandContext(
|
|
1384
|
+
commandContext(dataStorage, publishEvent, authContext) {
|
|
1065
1385
|
return new Proxy({}, {
|
|
1066
1386
|
get: (target, name) => {
|
|
1067
|
-
if (name === "$
|
|
1068
|
-
return
|
|
1387
|
+
if (name === "$auth") {
|
|
1388
|
+
return authContext;
|
|
1069
1389
|
}
|
|
1070
|
-
const
|
|
1071
|
-
if (!
|
|
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 (!
|
|
1394
|
+
if (!element2.commandContext) {
|
|
1075
1395
|
throw new Error(`Element "${String(name)}" does not have a command context`);
|
|
1076
1396
|
}
|
|
1077
|
-
return
|
|
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 (
|
|
1113
|
-
if (
|
|
1114
|
-
return (await
|
|
1403
|
+
const elements = await Promise.all(elementsPromise.map(async (element2) => {
|
|
1404
|
+
if (element2 instanceof Promise) {
|
|
1405
|
+
return (await element2).default;
|
|
1115
1406
|
}
|
|
1116
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1823
|
-
if (!
|
|
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 =
|
|
1828
|
-
const values =
|
|
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 ${
|
|
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
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
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((
|
|
1867
|
-
if ("storeSchema" in
|
|
1868
|
-
|
|
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
|
|
1877
|
-
if ("storeSchema" in
|
|
1878
|
-
const schema =
|
|
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
|
-
//
|
|
1925
|
-
class
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
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.
|
|
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
|
-
|
|
1964
|
-
const
|
|
1965
|
-
|
|
2297
|
+
use(elements) {
|
|
2298
|
+
const clone = this.clone();
|
|
2299
|
+
clone._elements = elements;
|
|
2300
|
+
return clone;
|
|
1966
2301
|
}
|
|
1967
|
-
|
|
1968
|
-
|
|
2302
|
+
description(description) {
|
|
2303
|
+
const clone = this.clone();
|
|
2304
|
+
clone._description = description;
|
|
2305
|
+
return clone;
|
|
1969
2306
|
}
|
|
1970
|
-
|
|
1971
|
-
|
|
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
|
|
1976
|
-
|
|
1977
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2621
|
+
queryBuilderFn(queryBuilderProxy);
|
|
2019
2622
|
const response = await fetch(`${this.apiBaseUrl}/query`, {
|
|
2020
2623
|
method: "POST",
|
|
2021
|
-
headers:
|
|
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
|
|
2050
|
-
if (!
|
|
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
|
|
2057
|
-
body
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3063
|
+
const restrictedOptions = this.applyRestrictions(authContext, options);
|
|
3064
|
+
return store.find(restrictedOptions);
|
|
2188
3065
|
},
|
|
2189
3066
|
findOne: async (where) => {
|
|
2190
|
-
const
|
|
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] =
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
}
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
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
|
};
|