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