rads-db 3.0.59 → 3.0.62
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/index.cjs +164 -123
- package/dist/index.d.ts +11 -1
- package/dist/index.mjs +164 -122
- package/drivers/restApi.cjs +6 -6
- package/drivers/restApi.mjs +5 -5
- package/features/cache.cjs +2 -3
- package/features/cache.mjs +2 -2
- package/integrations/lib.cjs +36 -14
- package/integrations/lib.mjs +48 -14
- package/package.json +1 -3
package/dist/index.cjs
CHANGED
|
@@ -5,13 +5,11 @@ const _ = require('lodash');
|
|
|
5
5
|
const uuid = require('uuid');
|
|
6
6
|
const createMerge = require('@fastify/deepmerge');
|
|
7
7
|
const _radsDb = require('_rads-db');
|
|
8
|
-
const pluralize = require('pluralize');
|
|
9
8
|
|
|
10
9
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
11
10
|
|
|
12
11
|
const ___default = /*#__PURE__*/_interopDefaultCompat(_);
|
|
13
12
|
const createMerge__default = /*#__PURE__*/_interopDefaultCompat(createMerge);
|
|
14
|
-
const pluralize__default = /*#__PURE__*/_interopDefaultCompat(pluralize);
|
|
15
13
|
|
|
16
14
|
function generateValidators(schema) {
|
|
17
15
|
const zodSchemas = {};
|
|
@@ -58,7 +56,7 @@ function getZodSchema(zodSchemas, schema, key) {
|
|
|
58
56
|
const objectSchema = {};
|
|
59
57
|
for (const fieldName in type.fields) {
|
|
60
58
|
const f = type.fields[fieldName];
|
|
61
|
-
if (f.decorators?.computed || f.decorators?.precomputed) {
|
|
59
|
+
if (f.decorators?.computed || f.decorators?.precomputed || f.isInverseRelation) {
|
|
62
60
|
continue;
|
|
63
61
|
}
|
|
64
62
|
objectSchema[fieldName] = getFieldZodSchema(zodSchemas, schema, f);
|
|
@@ -510,6 +508,73 @@ async function handleEffectsAfterPut(context, docs, beforePutResults, ctx) {
|
|
|
510
508
|
);
|
|
511
509
|
}
|
|
512
510
|
|
|
511
|
+
function cleanUndefinedAndNull(obj, isChange = false) {
|
|
512
|
+
for (const key in obj) {
|
|
513
|
+
if (obj[key] == null && !isChange)
|
|
514
|
+
delete obj[key];
|
|
515
|
+
if (___default.isPlainObject(obj[key]))
|
|
516
|
+
cleanUndefinedAndNull(obj[key], isChange || key === "change");
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const mergeFn = createMerge__default({
|
|
521
|
+
mergeArray(options) {
|
|
522
|
+
const clone = options.clone;
|
|
523
|
+
return function(target, source) {
|
|
524
|
+
return clone(source);
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
function merge(target, source) {
|
|
529
|
+
cleanUndefined(source);
|
|
530
|
+
const result = mergeFn(target, source);
|
|
531
|
+
return result;
|
|
532
|
+
}
|
|
533
|
+
function cleanUndefined(obj) {
|
|
534
|
+
if (!obj || !___default.isPlainObject(obj))
|
|
535
|
+
return;
|
|
536
|
+
cleanUndefinedInner(obj);
|
|
537
|
+
}
|
|
538
|
+
function cleanUndefinedInner(obj) {
|
|
539
|
+
for (const key in obj) {
|
|
540
|
+
if (obj[key] === void 0)
|
|
541
|
+
delete obj[key];
|
|
542
|
+
if (___default.isPlainObject(obj[key]))
|
|
543
|
+
cleanUndefinedInner(obj[key]);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function diff(object, base, includeDenormFields = false) {
|
|
548
|
+
if (!base)
|
|
549
|
+
return object;
|
|
550
|
+
return ___default.transform(object, (result, value, key) => {
|
|
551
|
+
const oldValue = base[key];
|
|
552
|
+
if (!___default.isEqual(value, oldValue)) {
|
|
553
|
+
if (mustUseDeepDiff(value, oldValue)) {
|
|
554
|
+
const isRelationField = (value.id || oldValue.id) && key !== "change";
|
|
555
|
+
if (isRelationField && !includeDenormFields) {
|
|
556
|
+
if (value.id !== oldValue.id)
|
|
557
|
+
result[key] = value;
|
|
558
|
+
} else {
|
|
559
|
+
const subDiff = diff(value, oldValue, includeDenormFields);
|
|
560
|
+
if (!___default.isEmpty(subDiff))
|
|
561
|
+
result[key] = subDiff;
|
|
562
|
+
}
|
|
563
|
+
} else {
|
|
564
|
+
result[key] = value;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
function mustUseDeepDiff(value, oldValue) {
|
|
570
|
+
return ___default.isObject(value) && ___default.isObject(oldValue) && !___default.isArray(value) && !___default.isArray(oldValue);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const radsDbRelations = {
|
|
574
|
+
verifyRelationsSetup,
|
|
575
|
+
fillDenormFieldsBeforePut,
|
|
576
|
+
handleInclude
|
|
577
|
+
};
|
|
513
578
|
function verifyRelationsSetup(schema, effects) {
|
|
514
579
|
for (const entityName in schema) {
|
|
515
580
|
const entity = schema[entityName];
|
|
@@ -589,67 +654,96 @@ async function fillDenormFieldsBeforePut(computedContext, docUpdates, ctx) {
|
|
|
589
654
|
});
|
|
590
655
|
await Promise.all(promises);
|
|
591
656
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
for (const key in obj) {
|
|
595
|
-
if (obj[key] == null && !isChange)
|
|
596
|
-
delete obj[key];
|
|
597
|
-
if (___default.isPlainObject(obj[key]))
|
|
598
|
-
cleanUndefinedAndNull(obj[key], isChange || key === "change");
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
const mergeFn = createMerge__default({
|
|
603
|
-
mergeArray(options) {
|
|
604
|
-
const clone = options.clone;
|
|
605
|
-
return function(target, source) {
|
|
606
|
-
return clone(source);
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
});
|
|
610
|
-
function merge(target, source) {
|
|
611
|
-
cleanUndefined(source);
|
|
612
|
-
const result = mergeFn(target, source);
|
|
613
|
-
return result;
|
|
614
|
-
}
|
|
615
|
-
function cleanUndefined(obj) {
|
|
616
|
-
if (!obj || !___default.isPlainObject(obj))
|
|
657
|
+
async function handleInclude(computedContext, include, result, ctx) {
|
|
658
|
+
if (!result || !result.length || !include)
|
|
617
659
|
return;
|
|
618
|
-
|
|
660
|
+
const { schema, typeName } = computedContext;
|
|
661
|
+
const fields = schema[typeName].fields || {};
|
|
662
|
+
const fieldsToInclude = ___default.keys(include).filter((key) => include[key]);
|
|
663
|
+
const relationsToInclude = fieldsToInclude.filter((key) => fields[key].isRelation);
|
|
664
|
+
const args = {
|
|
665
|
+
entityTypeName: typeName,
|
|
666
|
+
computedContext,
|
|
667
|
+
ctx,
|
|
668
|
+
result,
|
|
669
|
+
include
|
|
670
|
+
};
|
|
671
|
+
const inverseRelationsToInclude = fieldsToInclude.filter((key) => fields[key].isInverseRelation);
|
|
672
|
+
const downloadRelationsPromises = relationsToInclude.map(
|
|
673
|
+
(fieldName) => downloadRelatedDocuments({ ...args, fieldName })
|
|
674
|
+
);
|
|
675
|
+
const downloadInverseRelationsPromises = inverseRelationsToInclude.map(
|
|
676
|
+
(fieldName) => downloadInverseRelationDocuments({ ...args, fieldName })
|
|
677
|
+
);
|
|
678
|
+
await Promise.all([...downloadRelationsPromises, ...downloadInverseRelationsPromises]);
|
|
619
679
|
}
|
|
620
|
-
function
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
680
|
+
async function downloadInverseRelationDocuments(args) {
|
|
681
|
+
const { computedContext, ctx, entityTypeName, fieldName, result, include } = args;
|
|
682
|
+
const { schema, db } = computedContext;
|
|
683
|
+
const field = schema[entityTypeName].fields?.[fieldName];
|
|
684
|
+
if (!field?.inverseRelationField)
|
|
685
|
+
throw new Error(`Cannot find field ${fieldName}`);
|
|
686
|
+
const typeName = field.type;
|
|
687
|
+
const type = schema[typeName];
|
|
688
|
+
if (!type)
|
|
689
|
+
throw new Error(`Cannot find entity ${typeName}`);
|
|
690
|
+
for (const item of result) {
|
|
691
|
+
if (!item?.id)
|
|
692
|
+
continue;
|
|
693
|
+
const entityHandle = schema[typeName].handle;
|
|
694
|
+
const documentsRelatedToThisDoc = await db[entityHandle].getMany(
|
|
695
|
+
{ where: { [field.inverseRelationField]: { id: item.id } }, include: include?.[fieldName], maxItemCount: 100 },
|
|
696
|
+
ctx
|
|
697
|
+
);
|
|
698
|
+
item[fieldName] = documentsRelatedToThisDoc.nodes;
|
|
626
699
|
}
|
|
627
700
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
701
|
+
async function downloadRelatedDocuments(args) {
|
|
702
|
+
const { computedContext, ctx, entityTypeName, fieldName, result, include } = args;
|
|
703
|
+
const { schema, db } = computedContext;
|
|
704
|
+
const typeName = schema[entityTypeName].fields?.[fieldName].type || "";
|
|
705
|
+
const type = schema[typeName];
|
|
706
|
+
if (!type)
|
|
707
|
+
throw new Error(`Cannot find entity ${typeName}`);
|
|
708
|
+
const idsToGet = /* @__PURE__ */ new Set();
|
|
709
|
+
for (const item of result) {
|
|
710
|
+
if (!item)
|
|
711
|
+
continue;
|
|
712
|
+
const fieldValue = item[fieldName];
|
|
713
|
+
if (!fieldValue)
|
|
714
|
+
continue;
|
|
715
|
+
const fieldValues = ___default.isArray(fieldValue) ? fieldValue : [fieldValue];
|
|
716
|
+
for (const fv of fieldValues) {
|
|
717
|
+
const id = fv?.id;
|
|
718
|
+
if (!id)
|
|
719
|
+
continue;
|
|
720
|
+
idsToGet.add(id);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
const entityHandle = schema[typeName].handle;
|
|
724
|
+
const relatedEntities = await db[entityHandle].getAll(
|
|
725
|
+
{ where: { id_in: [...idsToGet] }, include: include?.[fieldName] },
|
|
726
|
+
ctx
|
|
727
|
+
);
|
|
728
|
+
const relatedEntitiesById = ___default.keyBy(relatedEntities, "id");
|
|
729
|
+
for (const item of result) {
|
|
730
|
+
if (!item)
|
|
731
|
+
continue;
|
|
732
|
+
const fieldValue = item[fieldName];
|
|
733
|
+
if (!fieldValue)
|
|
734
|
+
continue;
|
|
735
|
+
const fieldValues = ___default.isArray(fieldValue) ? fieldValue : [fieldValue];
|
|
736
|
+
for (const fv of fieldValues) {
|
|
737
|
+
const id = fv?.id;
|
|
738
|
+
if (!id)
|
|
739
|
+
continue;
|
|
740
|
+
const relatedEntity = relatedEntitiesById[id];
|
|
741
|
+
if (!relatedEntity) {
|
|
742
|
+
console.warn(`Cannot find ${typeName} with id "${id}" (for ${typeName}.${fieldName} with id "${item.id}")`);
|
|
647
743
|
}
|
|
744
|
+
Object.assign(fv, relatedEntity);
|
|
648
745
|
}
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
function mustUseDeepDiff(value, oldValue) {
|
|
652
|
-
return ___default.isObject(value) && ___default.isObject(oldValue) && !___default.isArray(value) && !___default.isArray(oldValue);
|
|
746
|
+
}
|
|
653
747
|
}
|
|
654
748
|
|
|
655
749
|
function getRadsDbMethods(key, computedContext, driverInstance) {
|
|
@@ -662,7 +756,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
|
|
|
662
756
|
if (!result2)
|
|
663
757
|
result2 = await driverInstance.getMany(args, ctx);
|
|
664
758
|
if (args.include)
|
|
665
|
-
await handleInclude(computedContext, args.include, result2.nodes, ctx);
|
|
759
|
+
await radsDbRelations.handleInclude(computedContext, args.include, result2.nodes, ctx);
|
|
666
760
|
await handleComputed(computedContext, result2.nodes, ctx);
|
|
667
761
|
await afterGet(result2.nodes, args, ctx, computedContext);
|
|
668
762
|
return result2;
|
|
@@ -699,7 +793,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
|
|
|
699
793
|
}
|
|
700
794
|
d.doc = validatedDoc;
|
|
701
795
|
}
|
|
702
|
-
await fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
|
|
796
|
+
await radsDbRelations.fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
|
|
703
797
|
const docsToSave = docArgsToSave.map((x) => x.doc);
|
|
704
798
|
await driverInstance.putMany(docsToSave, ctx);
|
|
705
799
|
await handleEffectsAfterPut(computedContext, docArgsToSave, beforePutResults, ctx);
|
|
@@ -742,7 +836,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
|
|
|
742
836
|
}
|
|
743
837
|
d.doc = validatedDoc;
|
|
744
838
|
}
|
|
745
|
-
await fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
|
|
839
|
+
await radsDbRelations.fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
|
|
746
840
|
for (const { oldDoc, doc } of docArgsToSave) {
|
|
747
841
|
const d = diff(doc, oldDoc);
|
|
748
842
|
if (___default.isEmpty(d))
|
|
@@ -801,7 +895,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
|
|
|
801
895
|
result2 = await driverInstance.get(args, ctx);
|
|
802
896
|
const resultArray = result2 ? [result2] : [];
|
|
803
897
|
if (result2 && args.include)
|
|
804
|
-
await handleInclude(computedContext, args.include, resultArray, ctx);
|
|
898
|
+
await radsDbRelations.handleInclude(computedContext, args.include, resultArray, ctx);
|
|
805
899
|
if (result2)
|
|
806
900
|
await handleComputed(computedContext, resultArray, ctx);
|
|
807
901
|
await afterGet(resultArray, args, ctx, computedContext);
|
|
@@ -814,7 +908,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
|
|
|
814
908
|
if (!result2)
|
|
815
909
|
result2 = await driverInstance.getAll(args, ctx);
|
|
816
910
|
if (args.include)
|
|
817
|
-
await handleInclude(computedContext, args.include, result2, ctx);
|
|
911
|
+
await radsDbRelations.handleInclude(computedContext, args.include, result2, ctx);
|
|
818
912
|
await handleComputed(computedContext, result2, ctx);
|
|
819
913
|
await afterGet(result2, args, ctx, computedContext);
|
|
820
914
|
return result2;
|
|
@@ -857,7 +951,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
|
|
|
857
951
|
}
|
|
858
952
|
}
|
|
859
953
|
doc = validatedDoc;
|
|
860
|
-
await fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
|
|
954
|
+
await radsDbRelations.fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
|
|
861
955
|
await driverInstance.put(doc, ctx);
|
|
862
956
|
await handleEffectsAfterPut(computedContext, docArgsToSave, beforePutResults, ctx);
|
|
863
957
|
await afterPut(docArgsToSave, ctx, computedContext);
|
|
@@ -892,61 +986,6 @@ async function afterPut(items, ctx, computedContext) {
|
|
|
892
986
|
await f.afterPut?.(items, ctx, computedContext);
|
|
893
987
|
}
|
|
894
988
|
}
|
|
895
|
-
async function handleInclude(computedContext, include, result, ctx) {
|
|
896
|
-
if (!result || !result.length || !include)
|
|
897
|
-
return;
|
|
898
|
-
const { schema, typeName, drivers } = computedContext;
|
|
899
|
-
const fields = schema[typeName].fields || {};
|
|
900
|
-
const relationsToInclude = ___default.keys(include).filter((key) => include[key] && fields[key].isRelation);
|
|
901
|
-
const downloadRelationsPromises = relationsToInclude.map(async (fieldName) => {
|
|
902
|
-
const typeName2 = fields[fieldName].type;
|
|
903
|
-
const type = schema[typeName2];
|
|
904
|
-
if (!type)
|
|
905
|
-
throw new Error(`Cannot find entity ${typeName2}`);
|
|
906
|
-
const idsToGet = /* @__PURE__ */ new Set();
|
|
907
|
-
for (const item of result) {
|
|
908
|
-
if (!item)
|
|
909
|
-
continue;
|
|
910
|
-
const fieldValue = item[fieldName];
|
|
911
|
-
if (!fieldValue)
|
|
912
|
-
continue;
|
|
913
|
-
const fieldValues = ___default.isArray(fieldValue) ? fieldValue : [fieldValue];
|
|
914
|
-
for (const fv of fieldValues) {
|
|
915
|
-
const id = fv?.id;
|
|
916
|
-
if (!id)
|
|
917
|
-
continue;
|
|
918
|
-
idsToGet.add(id);
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
const driverInstance = getExistingDriverInstance(typeName2, drivers);
|
|
922
|
-
const relatedEntities = await driverInstance.getAll({ where: { id_in: [...idsToGet] } }, ctx);
|
|
923
|
-
const relatedEntitiesById = ___default.keyBy(relatedEntities, "id");
|
|
924
|
-
for (const item of result) {
|
|
925
|
-
if (!item)
|
|
926
|
-
continue;
|
|
927
|
-
const fieldValue = item[fieldName];
|
|
928
|
-
if (!fieldValue)
|
|
929
|
-
continue;
|
|
930
|
-
const fieldValues = ___default.isArray(fieldValue) ? fieldValue : [fieldValue];
|
|
931
|
-
for (const fv of fieldValues) {
|
|
932
|
-
const id = fv?.id;
|
|
933
|
-
if (!id)
|
|
934
|
-
continue;
|
|
935
|
-
const relatedEntity = relatedEntitiesById[id];
|
|
936
|
-
if (!relatedEntity)
|
|
937
|
-
console.warn(`Cannot find ${typeName2} with id "${id}" (for ${typeName2}.${fieldName} with id "${item.id}")`);
|
|
938
|
-
Object.assign(fv, relatedEntity);
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
});
|
|
942
|
-
await Promise.all(downloadRelationsPromises);
|
|
943
|
-
}
|
|
944
|
-
function getExistingDriverInstance(entityName, drivers) {
|
|
945
|
-
if (!drivers[entityName]) {
|
|
946
|
-
throw new Error(`Driver for entity ${entityName} was not found!`);
|
|
947
|
-
}
|
|
948
|
-
return drivers[entityName];
|
|
949
|
-
}
|
|
950
989
|
|
|
951
990
|
function generateMethods(schema, validators, options) {
|
|
952
991
|
const drivers = {};
|
|
@@ -976,7 +1015,7 @@ function generateMethods(schema, validators, options) {
|
|
|
976
1015
|
if (!opts.noComputed) {
|
|
977
1016
|
verifyComputedPresense(schema, opts.computed, effects);
|
|
978
1017
|
}
|
|
979
|
-
verifyRelationsSetup(schema, effects);
|
|
1018
|
+
radsDbRelations.verifyRelationsSetup(schema, effects);
|
|
980
1019
|
const computedContextGlobal = {
|
|
981
1020
|
schema,
|
|
982
1021
|
validators,
|
|
@@ -1144,12 +1183,14 @@ const restApi = (options) => (schema, entity) => {
|
|
|
1144
1183
|
}
|
|
1145
1184
|
return responseJson;
|
|
1146
1185
|
}
|
|
1147
|
-
const
|
|
1186
|
+
const { handlePlural } = schema[entity] || {};
|
|
1187
|
+
if (!handlePlural)
|
|
1188
|
+
throw new Error(`Entity ${entity} was not found in schema`);
|
|
1148
1189
|
const instance = {
|
|
1149
1190
|
driverName: "restApi",
|
|
1150
1191
|
async getMany(args) {
|
|
1151
1192
|
args = args || {};
|
|
1152
|
-
const url = `${opts.baseUrl}/${
|
|
1193
|
+
const url = `${opts.baseUrl}/${handlePlural}`;
|
|
1153
1194
|
const fetchOptions = {
|
|
1154
1195
|
method: "POST",
|
|
1155
1196
|
body: JSON.stringify(args),
|
|
@@ -1159,7 +1200,7 @@ const restApi = (options) => (schema, entity) => {
|
|
|
1159
1200
|
return await fetchInner(url, fetchOptions);
|
|
1160
1201
|
},
|
|
1161
1202
|
async putMany(item) {
|
|
1162
|
-
const url = `${opts.baseUrl}/${
|
|
1203
|
+
const url = `${opts.baseUrl}/${handlePlural}`;
|
|
1163
1204
|
const fetchOptions = {
|
|
1164
1205
|
method: "PUT",
|
|
1165
1206
|
body: JSON.stringify({ data: item }),
|
package/dist/index.d.ts
CHANGED
|
@@ -89,9 +89,17 @@ type DeepPartialWithNullsItem<T> = T extends {
|
|
|
89
89
|
type DeepPartial<T> = {
|
|
90
90
|
[K in keyof T]?: NonNullable<T[K]> extends any[] ? DeepPartial<NonNullable<T[K]>[number]>[] : NonNullable<T[K]> extends Record<string, any> ? DeepPartial<T[K]> : T[K];
|
|
91
91
|
};
|
|
92
|
+
/** Indicates that this field is a relation to another entity in the database.
|
|
93
|
+
* Only id will be stored in the database.
|
|
94
|
+
* If you want to store additional fields, please, pass them as the second type argument */
|
|
92
95
|
type Relation<T extends {
|
|
93
96
|
id: any;
|
|
94
97
|
}, K extends Exclude<keyof T, 'id'> = never> = Pick<T, K | 'id'>;
|
|
98
|
+
/** Indicates that this is computed field - all documents that point to this document via Relation<>.
|
|
99
|
+
* Note: this field is not stored in the database at all. Returns up to 100 items and doesn't support pagination
|
|
100
|
+
* If you need more control, please, use separate request instead.
|
|
101
|
+
*/
|
|
102
|
+
type InverseRelation<EN extends keyof EntityMeta, _Field extends keyof EntityMeta[EN]['relations']> = EntityMeta[EN]['type'];
|
|
95
103
|
type PutArgs<T> = {
|
|
96
104
|
id: string;
|
|
97
105
|
} & DeepPartialWithNulls<T>;
|
|
@@ -298,7 +306,9 @@ interface FieldDefinition {
|
|
|
298
306
|
isRequired?: boolean;
|
|
299
307
|
isArray?: boolean;
|
|
300
308
|
isRelation?: boolean;
|
|
309
|
+
isInverseRelation?: boolean;
|
|
301
310
|
isChange?: boolean;
|
|
311
|
+
inverseRelationField?: string;
|
|
302
312
|
relationDenormFields?: string[];
|
|
303
313
|
comment?: string;
|
|
304
314
|
decorators?: Record<string, Record<string, any>>;
|
|
@@ -448,4 +458,4 @@ declare function createRadsDb(args?: CreateRadsDbArgs): RadsDb;
|
|
|
448
458
|
*/
|
|
449
459
|
declare function createRadsDbClient(args?: CreateRadsDbClientArgs): RadsDb;
|
|
450
460
|
|
|
451
|
-
export { Change, ComputedContext, ComputedContextGlobal, ComputedDecoratorArgs, CreateRadsArgsDrivers, CreateRadsDbArgs, CreateRadsDbArgsNormalized, CreateRadsDbClientArgs, DeepKeys, DeepPartial, DeepPartialWithNulls, DeepPartialWithNullsItem, Driver, DriverConstructor, EntityDecoratorArgs, EntityMethods, EnumDefinition, FieldDecoratorArgs, FieldDefinition, FileSystemNode, FileUploadArgs, FileUploadDriver, FileUploadResult, GenerateClientNormalizedOptions, GenerateClientOptions, Get, GetAggArgs, GetAggArgsAgg, GetAggArgsAny, GetAggResponse, GetArgs, GetArgsAny, GetArgsInclude, GetManyArgs, GetManyArgsAny, GetManyResponse, GetResponse, GetResponseInclude, GetResponseIncludeSelect, GetResponseNoInclude, GetRestRoutesArgs, GetRestRoutesOptions, GetRestRoutesResponse, MinimalDriver, Put, PutArgs, PutEffect, RadsFeature, RadsHookDoc, RadsRequestContext, RadsUiSlotDefinition, RadsUiSlotName, RadsVitePluginOptions, Relation, RequiredFields, RestDriverOptions, RestFileUploadDriverOptions, Schema, SchemaValidators, TypeDefinition, UiDecoratorArgs, UiFieldDecoratorArgs, ValidateEntityDecoratorArgs, ValidateFieldDecoratorArgs, ValidateStringDecoratorArgs, VerifyManyArgs, VerifyManyArgsAny, VerifyManyResponse, Where, WhereJsonContains, cleanUndefinedAndNull, computed, createRadsDb, createRadsDbClient, diff, entity, field, getDriverInstance, handlePrecomputed, keepHistory, merge, precomputed, ui, validate };
|
|
461
|
+
export { Change, ComputedContext, ComputedContextGlobal, ComputedDecoratorArgs, CreateRadsArgsDrivers, CreateRadsDbArgs, CreateRadsDbArgsNormalized, CreateRadsDbClientArgs, DeepKeys, DeepPartial, DeepPartialWithNulls, DeepPartialWithNullsItem, Driver, DriverConstructor, EntityDecoratorArgs, EntityMethods, EnumDefinition, FieldDecoratorArgs, FieldDefinition, FileSystemNode, FileUploadArgs, FileUploadDriver, FileUploadResult, GenerateClientNormalizedOptions, GenerateClientOptions, Get, GetAggArgs, GetAggArgsAgg, GetAggArgsAny, GetAggResponse, GetArgs, GetArgsAny, GetArgsInclude, GetManyArgs, GetManyArgsAny, GetManyResponse, GetResponse, GetResponseInclude, GetResponseIncludeSelect, GetResponseNoInclude, GetRestRoutesArgs, GetRestRoutesOptions, GetRestRoutesResponse, InverseRelation, MinimalDriver, Put, PutArgs, PutEffect, RadsFeature, RadsHookDoc, RadsRequestContext, RadsUiSlotDefinition, RadsUiSlotName, RadsVitePluginOptions, Relation, RequiredFields, RestDriverOptions, RestFileUploadDriverOptions, Schema, SchemaValidators, TypeDefinition, UiDecoratorArgs, UiFieldDecoratorArgs, ValidateEntityDecoratorArgs, ValidateFieldDecoratorArgs, ValidateStringDecoratorArgs, VerifyManyArgs, VerifyManyArgsAny, VerifyManyResponse, Where, WhereJsonContains, cleanUndefinedAndNull, computed, createRadsDb, createRadsDbClient, diff, entity, field, getDriverInstance, handlePrecomputed, keepHistory, merge, precomputed, ui, validate };
|
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,6 @@ import _ from 'lodash';
|
|
|
3
3
|
import { v4 } from 'uuid';
|
|
4
4
|
import createMerge from '@fastify/deepmerge';
|
|
5
5
|
import { schema } from '_rads-db';
|
|
6
|
-
import pluralize from 'pluralize';
|
|
7
6
|
|
|
8
7
|
function generateValidators(schema) {
|
|
9
8
|
const zodSchemas = {};
|
|
@@ -50,7 +49,7 @@ function getZodSchema(zodSchemas, schema, key) {
|
|
|
50
49
|
const objectSchema = {};
|
|
51
50
|
for (const fieldName in type.fields) {
|
|
52
51
|
const f = type.fields[fieldName];
|
|
53
|
-
if (f.decorators?.computed || f.decorators?.precomputed) {
|
|
52
|
+
if (f.decorators?.computed || f.decorators?.precomputed || f.isInverseRelation) {
|
|
54
53
|
continue;
|
|
55
54
|
}
|
|
56
55
|
objectSchema[fieldName] = getFieldZodSchema(zodSchemas, schema, f);
|
|
@@ -502,6 +501,73 @@ async function handleEffectsAfterPut(context, docs, beforePutResults, ctx) {
|
|
|
502
501
|
);
|
|
503
502
|
}
|
|
504
503
|
|
|
504
|
+
function cleanUndefinedAndNull(obj, isChange = false) {
|
|
505
|
+
for (const key in obj) {
|
|
506
|
+
if (obj[key] == null && !isChange)
|
|
507
|
+
delete obj[key];
|
|
508
|
+
if (_.isPlainObject(obj[key]))
|
|
509
|
+
cleanUndefinedAndNull(obj[key], isChange || key === "change");
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const mergeFn = createMerge({
|
|
514
|
+
mergeArray(options) {
|
|
515
|
+
const clone = options.clone;
|
|
516
|
+
return function(target, source) {
|
|
517
|
+
return clone(source);
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
function merge(target, source) {
|
|
522
|
+
cleanUndefined(source);
|
|
523
|
+
const result = mergeFn(target, source);
|
|
524
|
+
return result;
|
|
525
|
+
}
|
|
526
|
+
function cleanUndefined(obj) {
|
|
527
|
+
if (!obj || !_.isPlainObject(obj))
|
|
528
|
+
return;
|
|
529
|
+
cleanUndefinedInner(obj);
|
|
530
|
+
}
|
|
531
|
+
function cleanUndefinedInner(obj) {
|
|
532
|
+
for (const key in obj) {
|
|
533
|
+
if (obj[key] === void 0)
|
|
534
|
+
delete obj[key];
|
|
535
|
+
if (_.isPlainObject(obj[key]))
|
|
536
|
+
cleanUndefinedInner(obj[key]);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function diff(object, base, includeDenormFields = false) {
|
|
541
|
+
if (!base)
|
|
542
|
+
return object;
|
|
543
|
+
return _.transform(object, (result, value, key) => {
|
|
544
|
+
const oldValue = base[key];
|
|
545
|
+
if (!_.isEqual(value, oldValue)) {
|
|
546
|
+
if (mustUseDeepDiff(value, oldValue)) {
|
|
547
|
+
const isRelationField = (value.id || oldValue.id) && key !== "change";
|
|
548
|
+
if (isRelationField && !includeDenormFields) {
|
|
549
|
+
if (value.id !== oldValue.id)
|
|
550
|
+
result[key] = value;
|
|
551
|
+
} else {
|
|
552
|
+
const subDiff = diff(value, oldValue, includeDenormFields);
|
|
553
|
+
if (!_.isEmpty(subDiff))
|
|
554
|
+
result[key] = subDiff;
|
|
555
|
+
}
|
|
556
|
+
} else {
|
|
557
|
+
result[key] = value;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
function mustUseDeepDiff(value, oldValue) {
|
|
563
|
+
return _.isObject(value) && _.isObject(oldValue) && !_.isArray(value) && !_.isArray(oldValue);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const radsDbRelations = {
|
|
567
|
+
verifyRelationsSetup,
|
|
568
|
+
fillDenormFieldsBeforePut,
|
|
569
|
+
handleInclude
|
|
570
|
+
};
|
|
505
571
|
function verifyRelationsSetup(schema, effects) {
|
|
506
572
|
for (const entityName in schema) {
|
|
507
573
|
const entity = schema[entityName];
|
|
@@ -581,67 +647,96 @@ async function fillDenormFieldsBeforePut(computedContext, docUpdates, ctx) {
|
|
|
581
647
|
});
|
|
582
648
|
await Promise.all(promises);
|
|
583
649
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
for (const key in obj) {
|
|
587
|
-
if (obj[key] == null && !isChange)
|
|
588
|
-
delete obj[key];
|
|
589
|
-
if (_.isPlainObject(obj[key]))
|
|
590
|
-
cleanUndefinedAndNull(obj[key], isChange || key === "change");
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
const mergeFn = createMerge({
|
|
595
|
-
mergeArray(options) {
|
|
596
|
-
const clone = options.clone;
|
|
597
|
-
return function(target, source) {
|
|
598
|
-
return clone(source);
|
|
599
|
-
};
|
|
600
|
-
}
|
|
601
|
-
});
|
|
602
|
-
function merge(target, source) {
|
|
603
|
-
cleanUndefined(source);
|
|
604
|
-
const result = mergeFn(target, source);
|
|
605
|
-
return result;
|
|
606
|
-
}
|
|
607
|
-
function cleanUndefined(obj) {
|
|
608
|
-
if (!obj || !_.isPlainObject(obj))
|
|
650
|
+
async function handleInclude(computedContext, include, result, ctx) {
|
|
651
|
+
if (!result || !result.length || !include)
|
|
609
652
|
return;
|
|
610
|
-
|
|
653
|
+
const { schema, typeName } = computedContext;
|
|
654
|
+
const fields = schema[typeName].fields || {};
|
|
655
|
+
const fieldsToInclude = _.keys(include).filter((key) => include[key]);
|
|
656
|
+
const relationsToInclude = fieldsToInclude.filter((key) => fields[key].isRelation);
|
|
657
|
+
const args = {
|
|
658
|
+
entityTypeName: typeName,
|
|
659
|
+
computedContext,
|
|
660
|
+
ctx,
|
|
661
|
+
result,
|
|
662
|
+
include
|
|
663
|
+
};
|
|
664
|
+
const inverseRelationsToInclude = fieldsToInclude.filter((key) => fields[key].isInverseRelation);
|
|
665
|
+
const downloadRelationsPromises = relationsToInclude.map(
|
|
666
|
+
(fieldName) => downloadRelatedDocuments({ ...args, fieldName })
|
|
667
|
+
);
|
|
668
|
+
const downloadInverseRelationsPromises = inverseRelationsToInclude.map(
|
|
669
|
+
(fieldName) => downloadInverseRelationDocuments({ ...args, fieldName })
|
|
670
|
+
);
|
|
671
|
+
await Promise.all([...downloadRelationsPromises, ...downloadInverseRelationsPromises]);
|
|
611
672
|
}
|
|
612
|
-
function
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
673
|
+
async function downloadInverseRelationDocuments(args) {
|
|
674
|
+
const { computedContext, ctx, entityTypeName, fieldName, result, include } = args;
|
|
675
|
+
const { schema, db } = computedContext;
|
|
676
|
+
const field = schema[entityTypeName].fields?.[fieldName];
|
|
677
|
+
if (!field?.inverseRelationField)
|
|
678
|
+
throw new Error(`Cannot find field ${fieldName}`);
|
|
679
|
+
const typeName = field.type;
|
|
680
|
+
const type = schema[typeName];
|
|
681
|
+
if (!type)
|
|
682
|
+
throw new Error(`Cannot find entity ${typeName}`);
|
|
683
|
+
for (const item of result) {
|
|
684
|
+
if (!item?.id)
|
|
685
|
+
continue;
|
|
686
|
+
const entityHandle = schema[typeName].handle;
|
|
687
|
+
const documentsRelatedToThisDoc = await db[entityHandle].getMany(
|
|
688
|
+
{ where: { [field.inverseRelationField]: { id: item.id } }, include: include?.[fieldName], maxItemCount: 100 },
|
|
689
|
+
ctx
|
|
690
|
+
);
|
|
691
|
+
item[fieldName] = documentsRelatedToThisDoc.nodes;
|
|
618
692
|
}
|
|
619
693
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
694
|
+
async function downloadRelatedDocuments(args) {
|
|
695
|
+
const { computedContext, ctx, entityTypeName, fieldName, result, include } = args;
|
|
696
|
+
const { schema, db } = computedContext;
|
|
697
|
+
const typeName = schema[entityTypeName].fields?.[fieldName].type || "";
|
|
698
|
+
const type = schema[typeName];
|
|
699
|
+
if (!type)
|
|
700
|
+
throw new Error(`Cannot find entity ${typeName}`);
|
|
701
|
+
const idsToGet = /* @__PURE__ */ new Set();
|
|
702
|
+
for (const item of result) {
|
|
703
|
+
if (!item)
|
|
704
|
+
continue;
|
|
705
|
+
const fieldValue = item[fieldName];
|
|
706
|
+
if (!fieldValue)
|
|
707
|
+
continue;
|
|
708
|
+
const fieldValues = _.isArray(fieldValue) ? fieldValue : [fieldValue];
|
|
709
|
+
for (const fv of fieldValues) {
|
|
710
|
+
const id = fv?.id;
|
|
711
|
+
if (!id)
|
|
712
|
+
continue;
|
|
713
|
+
idsToGet.add(id);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
const entityHandle = schema[typeName].handle;
|
|
717
|
+
const relatedEntities = await db[entityHandle].getAll(
|
|
718
|
+
{ where: { id_in: [...idsToGet] }, include: include?.[fieldName] },
|
|
719
|
+
ctx
|
|
720
|
+
);
|
|
721
|
+
const relatedEntitiesById = _.keyBy(relatedEntities, "id");
|
|
722
|
+
for (const item of result) {
|
|
723
|
+
if (!item)
|
|
724
|
+
continue;
|
|
725
|
+
const fieldValue = item[fieldName];
|
|
726
|
+
if (!fieldValue)
|
|
727
|
+
continue;
|
|
728
|
+
const fieldValues = _.isArray(fieldValue) ? fieldValue : [fieldValue];
|
|
729
|
+
for (const fv of fieldValues) {
|
|
730
|
+
const id = fv?.id;
|
|
731
|
+
if (!id)
|
|
732
|
+
continue;
|
|
733
|
+
const relatedEntity = relatedEntitiesById[id];
|
|
734
|
+
if (!relatedEntity) {
|
|
735
|
+
console.warn(`Cannot find ${typeName} with id "${id}" (for ${typeName}.${fieldName} with id "${item.id}")`);
|
|
639
736
|
}
|
|
737
|
+
Object.assign(fv, relatedEntity);
|
|
640
738
|
}
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
function mustUseDeepDiff(value, oldValue) {
|
|
644
|
-
return _.isObject(value) && _.isObject(oldValue) && !_.isArray(value) && !_.isArray(oldValue);
|
|
739
|
+
}
|
|
645
740
|
}
|
|
646
741
|
|
|
647
742
|
function getRadsDbMethods(key, computedContext, driverInstance) {
|
|
@@ -654,7 +749,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
|
|
|
654
749
|
if (!result2)
|
|
655
750
|
result2 = await driverInstance.getMany(args, ctx);
|
|
656
751
|
if (args.include)
|
|
657
|
-
await handleInclude(computedContext, args.include, result2.nodes, ctx);
|
|
752
|
+
await radsDbRelations.handleInclude(computedContext, args.include, result2.nodes, ctx);
|
|
658
753
|
await handleComputed(computedContext, result2.nodes, ctx);
|
|
659
754
|
await afterGet(result2.nodes, args, ctx, computedContext);
|
|
660
755
|
return result2;
|
|
@@ -691,7 +786,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
|
|
|
691
786
|
}
|
|
692
787
|
d.doc = validatedDoc;
|
|
693
788
|
}
|
|
694
|
-
await fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
|
|
789
|
+
await radsDbRelations.fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
|
|
695
790
|
const docsToSave = docArgsToSave.map((x) => x.doc);
|
|
696
791
|
await driverInstance.putMany(docsToSave, ctx);
|
|
697
792
|
await handleEffectsAfterPut(computedContext, docArgsToSave, beforePutResults, ctx);
|
|
@@ -734,7 +829,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
|
|
|
734
829
|
}
|
|
735
830
|
d.doc = validatedDoc;
|
|
736
831
|
}
|
|
737
|
-
await fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
|
|
832
|
+
await radsDbRelations.fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
|
|
738
833
|
for (const { oldDoc, doc } of docArgsToSave) {
|
|
739
834
|
const d = diff(doc, oldDoc);
|
|
740
835
|
if (_.isEmpty(d))
|
|
@@ -793,7 +888,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
|
|
|
793
888
|
result2 = await driverInstance.get(args, ctx);
|
|
794
889
|
const resultArray = result2 ? [result2] : [];
|
|
795
890
|
if (result2 && args.include)
|
|
796
|
-
await handleInclude(computedContext, args.include, resultArray, ctx);
|
|
891
|
+
await radsDbRelations.handleInclude(computedContext, args.include, resultArray, ctx);
|
|
797
892
|
if (result2)
|
|
798
893
|
await handleComputed(computedContext, resultArray, ctx);
|
|
799
894
|
await afterGet(resultArray, args, ctx, computedContext);
|
|
@@ -806,7 +901,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
|
|
|
806
901
|
if (!result2)
|
|
807
902
|
result2 = await driverInstance.getAll(args, ctx);
|
|
808
903
|
if (args.include)
|
|
809
|
-
await handleInclude(computedContext, args.include, result2, ctx);
|
|
904
|
+
await radsDbRelations.handleInclude(computedContext, args.include, result2, ctx);
|
|
810
905
|
await handleComputed(computedContext, result2, ctx);
|
|
811
906
|
await afterGet(result2, args, ctx, computedContext);
|
|
812
907
|
return result2;
|
|
@@ -849,7 +944,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
|
|
|
849
944
|
}
|
|
850
945
|
}
|
|
851
946
|
doc = validatedDoc;
|
|
852
|
-
await fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
|
|
947
|
+
await radsDbRelations.fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
|
|
853
948
|
await driverInstance.put(doc, ctx);
|
|
854
949
|
await handleEffectsAfterPut(computedContext, docArgsToSave, beforePutResults, ctx);
|
|
855
950
|
await afterPut(docArgsToSave, ctx, computedContext);
|
|
@@ -884,61 +979,6 @@ async function afterPut(items, ctx, computedContext) {
|
|
|
884
979
|
await f.afterPut?.(items, ctx, computedContext);
|
|
885
980
|
}
|
|
886
981
|
}
|
|
887
|
-
async function handleInclude(computedContext, include, result, ctx) {
|
|
888
|
-
if (!result || !result.length || !include)
|
|
889
|
-
return;
|
|
890
|
-
const { schema, typeName, drivers } = computedContext;
|
|
891
|
-
const fields = schema[typeName].fields || {};
|
|
892
|
-
const relationsToInclude = _.keys(include).filter((key) => include[key] && fields[key].isRelation);
|
|
893
|
-
const downloadRelationsPromises = relationsToInclude.map(async (fieldName) => {
|
|
894
|
-
const typeName2 = fields[fieldName].type;
|
|
895
|
-
const type = schema[typeName2];
|
|
896
|
-
if (!type)
|
|
897
|
-
throw new Error(`Cannot find entity ${typeName2}`);
|
|
898
|
-
const idsToGet = /* @__PURE__ */ new Set();
|
|
899
|
-
for (const item of result) {
|
|
900
|
-
if (!item)
|
|
901
|
-
continue;
|
|
902
|
-
const fieldValue = item[fieldName];
|
|
903
|
-
if (!fieldValue)
|
|
904
|
-
continue;
|
|
905
|
-
const fieldValues = _.isArray(fieldValue) ? fieldValue : [fieldValue];
|
|
906
|
-
for (const fv of fieldValues) {
|
|
907
|
-
const id = fv?.id;
|
|
908
|
-
if (!id)
|
|
909
|
-
continue;
|
|
910
|
-
idsToGet.add(id);
|
|
911
|
-
}
|
|
912
|
-
}
|
|
913
|
-
const driverInstance = getExistingDriverInstance(typeName2, drivers);
|
|
914
|
-
const relatedEntities = await driverInstance.getAll({ where: { id_in: [...idsToGet] } }, ctx);
|
|
915
|
-
const relatedEntitiesById = _.keyBy(relatedEntities, "id");
|
|
916
|
-
for (const item of result) {
|
|
917
|
-
if (!item)
|
|
918
|
-
continue;
|
|
919
|
-
const fieldValue = item[fieldName];
|
|
920
|
-
if (!fieldValue)
|
|
921
|
-
continue;
|
|
922
|
-
const fieldValues = _.isArray(fieldValue) ? fieldValue : [fieldValue];
|
|
923
|
-
for (const fv of fieldValues) {
|
|
924
|
-
const id = fv?.id;
|
|
925
|
-
if (!id)
|
|
926
|
-
continue;
|
|
927
|
-
const relatedEntity = relatedEntitiesById[id];
|
|
928
|
-
if (!relatedEntity)
|
|
929
|
-
console.warn(`Cannot find ${typeName2} with id "${id}" (for ${typeName2}.${fieldName} with id "${item.id}")`);
|
|
930
|
-
Object.assign(fv, relatedEntity);
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
});
|
|
934
|
-
await Promise.all(downloadRelationsPromises);
|
|
935
|
-
}
|
|
936
|
-
function getExistingDriverInstance(entityName, drivers) {
|
|
937
|
-
if (!drivers[entityName]) {
|
|
938
|
-
throw new Error(`Driver for entity ${entityName} was not found!`);
|
|
939
|
-
}
|
|
940
|
-
return drivers[entityName];
|
|
941
|
-
}
|
|
942
982
|
|
|
943
983
|
function generateMethods(schema, validators, options) {
|
|
944
984
|
const drivers = {};
|
|
@@ -968,7 +1008,7 @@ function generateMethods(schema, validators, options) {
|
|
|
968
1008
|
if (!opts.noComputed) {
|
|
969
1009
|
verifyComputedPresense(schema, opts.computed, effects);
|
|
970
1010
|
}
|
|
971
|
-
verifyRelationsSetup(schema, effects);
|
|
1011
|
+
radsDbRelations.verifyRelationsSetup(schema, effects);
|
|
972
1012
|
const computedContextGlobal = {
|
|
973
1013
|
schema,
|
|
974
1014
|
validators,
|
|
@@ -1136,12 +1176,14 @@ const restApi = (options) => (schema, entity) => {
|
|
|
1136
1176
|
}
|
|
1137
1177
|
return responseJson;
|
|
1138
1178
|
}
|
|
1139
|
-
const
|
|
1179
|
+
const { handlePlural } = schema[entity] || {};
|
|
1180
|
+
if (!handlePlural)
|
|
1181
|
+
throw new Error(`Entity ${entity} was not found in schema`);
|
|
1140
1182
|
const instance = {
|
|
1141
1183
|
driverName: "restApi",
|
|
1142
1184
|
async getMany(args) {
|
|
1143
1185
|
args = args || {};
|
|
1144
|
-
const url = `${opts.baseUrl}/${
|
|
1186
|
+
const url = `${opts.baseUrl}/${handlePlural}`;
|
|
1145
1187
|
const fetchOptions = {
|
|
1146
1188
|
method: "POST",
|
|
1147
1189
|
body: JSON.stringify(args),
|
|
@@ -1151,7 +1193,7 @@ const restApi = (options) => (schema, entity) => {
|
|
|
1151
1193
|
return await fetchInner(url, fetchOptions);
|
|
1152
1194
|
},
|
|
1153
1195
|
async putMany(item) {
|
|
1154
|
-
const url = `${opts.baseUrl}/${
|
|
1196
|
+
const url = `${opts.baseUrl}/${handlePlural}`;
|
|
1155
1197
|
const fetchOptions = {
|
|
1156
1198
|
method: "PUT",
|
|
1157
1199
|
body: JSON.stringify({ data: item }),
|
package/drivers/restApi.cjs
CHANGED
|
@@ -4,9 +4,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
module.exports = void 0;
|
|
7
|
-
var _lodash = _interopRequireDefault(require("lodash"));
|
|
8
|
-
var _pluralize = _interopRequireDefault(require("pluralize"));
|
|
9
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
10
7
|
var _default = options => (schema, entity) => {
|
|
11
8
|
const opts = {
|
|
12
9
|
baseUrl: "/api",
|
|
@@ -30,12 +27,15 @@ var _default = options => (schema, entity) => {
|
|
|
30
27
|
}
|
|
31
28
|
return responseJson;
|
|
32
29
|
}
|
|
33
|
-
const
|
|
30
|
+
const {
|
|
31
|
+
handlePlural
|
|
32
|
+
} = schema[entity] || {};
|
|
33
|
+
if (!handlePlural) throw new Error(`Entity ${entity} was not found in schema`);
|
|
34
34
|
const instance = {
|
|
35
35
|
driverName: "restApi",
|
|
36
36
|
async getMany(args) {
|
|
37
37
|
args = args || {};
|
|
38
|
-
const url = `${opts.baseUrl}/${
|
|
38
|
+
const url = `${opts.baseUrl}/${handlePlural}`;
|
|
39
39
|
const fetchOptions = {
|
|
40
40
|
method: "POST",
|
|
41
41
|
body: JSON.stringify(args),
|
|
@@ -50,7 +50,7 @@ var _default = options => (schema, entity) => {
|
|
|
50
50
|
return await fetchInner(url, fetchOptions);
|
|
51
51
|
},
|
|
52
52
|
async putMany(item) {
|
|
53
|
-
const url = `${opts.baseUrl}/${
|
|
53
|
+
const url = `${opts.baseUrl}/${handlePlural}`;
|
|
54
54
|
const fetchOptions = {
|
|
55
55
|
method: "PUT",
|
|
56
56
|
body: JSON.stringify({
|
package/drivers/restApi.mjs
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import _ from "lodash";
|
|
2
|
-
import pluralize from "pluralize";
|
|
3
1
|
export default (options) => (schema, entity) => {
|
|
4
2
|
const opts = {
|
|
5
3
|
baseUrl: "/api",
|
|
@@ -24,12 +22,14 @@ export default (options) => (schema, entity) => {
|
|
|
24
22
|
}
|
|
25
23
|
return responseJson;
|
|
26
24
|
}
|
|
27
|
-
const
|
|
25
|
+
const { handlePlural } = schema[entity] || {};
|
|
26
|
+
if (!handlePlural)
|
|
27
|
+
throw new Error(`Entity ${entity} was not found in schema`);
|
|
28
28
|
const instance = {
|
|
29
29
|
driverName: "restApi",
|
|
30
30
|
async getMany(args) {
|
|
31
31
|
args = args || {};
|
|
32
|
-
const url = `${opts.baseUrl}/${
|
|
32
|
+
const url = `${opts.baseUrl}/${handlePlural}`;
|
|
33
33
|
const fetchOptions = {
|
|
34
34
|
method: "POST",
|
|
35
35
|
body: JSON.stringify(args),
|
|
@@ -39,7 +39,7 @@ export default (options) => (schema, entity) => {
|
|
|
39
39
|
return await fetchInner(url, fetchOptions);
|
|
40
40
|
},
|
|
41
41
|
async putMany(item) {
|
|
42
|
-
const url = `${opts.baseUrl}/${
|
|
42
|
+
const url = `${opts.baseUrl}/${handlePlural}`;
|
|
43
43
|
const fetchOptions = {
|
|
44
44
|
method: "PUT",
|
|
45
45
|
body: JSON.stringify({ data: item }),
|
package/features/cache.cjs
CHANGED
|
@@ -22,8 +22,7 @@ var _default = options => {
|
|
|
22
22
|
async function preloadEntities(entitiesToPreload) {
|
|
23
23
|
for (const typeName in entitiesToPreload) {
|
|
24
24
|
const {
|
|
25
|
-
where
|
|
26
|
-
revalidateInterval
|
|
25
|
+
where
|
|
27
26
|
} = entitiesToPreload[typeName];
|
|
28
27
|
const handle = schema[typeName]?.handle;
|
|
29
28
|
if (!handle) throw new Error(`Entity ${typeName} was not found`);
|
|
@@ -54,7 +53,7 @@ var _default = options => {
|
|
|
54
53
|
...schema[typeName],
|
|
55
54
|
name: cacheEntityName,
|
|
56
55
|
handle: cacheEntityName,
|
|
57
|
-
handlePlural: `${cacheEntityName}
|
|
56
|
+
handlePlural: `${cacheEntityName}/list`
|
|
58
57
|
};
|
|
59
58
|
cacheDrivers[handle] = (0, _radsDb.getDriverInstance)(schema, cacheEntityName, normalizedOptions[typeName].driver, drivers);
|
|
60
59
|
cacheStatus[handle] = {
|
package/features/cache.mjs
CHANGED
|
@@ -11,7 +11,7 @@ export default (options) => {
|
|
|
11
11
|
const { schema, drivers } = context;
|
|
12
12
|
async function preloadEntities(entitiesToPreload) {
|
|
13
13
|
for (const typeName in entitiesToPreload) {
|
|
14
|
-
const { where
|
|
14
|
+
const { where } = entitiesToPreload[typeName];
|
|
15
15
|
const handle = schema[typeName]?.handle;
|
|
16
16
|
if (!handle)
|
|
17
17
|
throw new Error(`Entity ${typeName} was not found`);
|
|
@@ -36,7 +36,7 @@ export default (options) => {
|
|
|
36
36
|
...schema[typeName],
|
|
37
37
|
name: cacheEntityName,
|
|
38
38
|
handle: cacheEntityName,
|
|
39
|
-
handlePlural: `${cacheEntityName}
|
|
39
|
+
handlePlural: `${cacheEntityName}/list`
|
|
40
40
|
};
|
|
41
41
|
cacheDrivers[handle] = getDriverInstance(schema, cacheEntityName, normalizedOptions[typeName].driver, drivers);
|
|
42
42
|
cacheStatus[handle] = { preloadStatus: normalizedOptions[typeName].preload ? "neverLoaded" : void 0 };
|
package/integrations/lib.cjs
CHANGED
|
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.parseSchema = parseSchema;
|
|
7
7
|
var _typescript = require("typescript");
|
|
8
8
|
var _lodash = _interopRequireDefault(require("lodash"));
|
|
9
|
-
var _pluralize = _interopRequireDefault(require("pluralize"));
|
|
10
9
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
10
|
const supportedPrimitiveTypes = ["string", "number", "boolean", "Record<string, string>", "Record<string, any>"];
|
|
12
11
|
function parseSchema(typescriptFiles) {
|
|
@@ -186,7 +185,7 @@ function parseClassDeclaration(typeDeclaration, typeName, ctx) {
|
|
|
186
185
|
fields[field.name] = field;
|
|
187
186
|
}
|
|
188
187
|
const handle = _lodash.default.lowerFirst(name);
|
|
189
|
-
const handlePlural =
|
|
188
|
+
const handlePlural = `${handle}/list`;
|
|
190
189
|
const result = {
|
|
191
190
|
name,
|
|
192
191
|
handle,
|
|
@@ -268,23 +267,13 @@ function parseClassMember(node, parentFields, parentName, ctx) {
|
|
|
268
267
|
if (!parentField) throw new Error(`Cannot find field ${parentName}.${defaultValueCopyFrom}"`);
|
|
269
268
|
defaultValueType = parentField.type;
|
|
270
269
|
}
|
|
271
|
-
const
|
|
272
|
-
isArray,
|
|
273
|
-
isRelation,
|
|
274
|
-
isChange,
|
|
275
|
-
relationDenormFields,
|
|
276
|
-
type
|
|
277
|
-
} = parseFieldType(ctx, parentName, name, node, defaultValueType);
|
|
270
|
+
const parsedType = parseFieldType(ctx, parentName, name, node, defaultValueType);
|
|
278
271
|
const result = {
|
|
279
|
-
|
|
272
|
+
...parsedType,
|
|
280
273
|
defaultValue,
|
|
281
274
|
defaultValueCopyFrom,
|
|
282
275
|
name,
|
|
283
276
|
isRequired,
|
|
284
|
-
isArray,
|
|
285
|
-
isRelation,
|
|
286
|
-
isChange,
|
|
287
|
-
relationDenormFields,
|
|
288
277
|
comment
|
|
289
278
|
};
|
|
290
279
|
if (!_lodash.default.isEmpty(decorators)) result.decorators = decorators;
|
|
@@ -311,6 +300,7 @@ function parseFieldType(ctx, parentName, fieldName, node, defaultValueType) {
|
|
|
311
300
|
};
|
|
312
301
|
parseFieldTypeArray(parsedType, parentName);
|
|
313
302
|
parseFieldTypeRelation(parsedType, parentName, ctx);
|
|
303
|
+
parseFieldTypeInverseRelation(parsedType, parentName, ctx);
|
|
314
304
|
parseFieldTypeInlineEnum(parsedType, parentName, fieldName, ctx);
|
|
315
305
|
parseFieldTypeKeyofEnum(parsedType, parentName, fieldName, ctx);
|
|
316
306
|
parseFieldTypeRecordEnum(parsedType, parentName, fieldName, ctx);
|
|
@@ -328,8 +318,10 @@ function parseFieldType(ctx, parentName, fieldName, node, defaultValueType) {
|
|
|
328
318
|
return {
|
|
329
319
|
isArray: parsedType.isArray || void 0,
|
|
330
320
|
isRelation: parsedType.isRelation || void 0,
|
|
321
|
+
isInverseRelation: parsedType.isInverseRelation || void 0,
|
|
331
322
|
isChange: parsedType.isChange || void 0,
|
|
332
323
|
relationDenormFields: parsedType.relationDenormFields,
|
|
324
|
+
inverseRelationField: parsedType.inverseRelationField,
|
|
333
325
|
type: parsedType.type
|
|
334
326
|
};
|
|
335
327
|
}
|
|
@@ -382,6 +374,28 @@ function parseFieldTypeRelation(parsedType, parentName, ctx) {
|
|
|
382
374
|
}
|
|
383
375
|
}
|
|
384
376
|
}
|
|
377
|
+
function parseFieldTypeInverseRelation(parsedType, parentName, ctx) {
|
|
378
|
+
if (parsedType.nodeType?.kind !== _typescript.SyntaxKind.TypeReference) return;
|
|
379
|
+
const nt = parsedType.nodeType;
|
|
380
|
+
if (nt.typeName.getText(ctx.sourceFile) === "InverseRelation") {
|
|
381
|
+
if (!nt.typeArguments?.length) throw new Error(`Missing type argument for Relation<>: '${parentName}'`);
|
|
382
|
+
parsedType.type = parseStringField(ctx, nt.typeArguments[0]);
|
|
383
|
+
parsedType.isInverseRelation = true;
|
|
384
|
+
if (nt.typeArguments[1]) {
|
|
385
|
+
const taNode = nt.typeArguments[1];
|
|
386
|
+
parsedType.inverseRelationField = parseStringField(ctx, taNode);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function parseStringField(ctx, node) {
|
|
391
|
+
if (node.kind === _typescript.SyntaxKind.LiteralType) {
|
|
392
|
+
const literal = node.literal;
|
|
393
|
+
if (literal.kind === _typescript.SyntaxKind.StringLiteral) {
|
|
394
|
+
return literal.text;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
throw new Error(`Unexpected type - ${node.getText(ctx.sourceFile)}`);
|
|
398
|
+
}
|
|
385
399
|
function parseFieldTypeInlineEnum(parsedType, parentName, fieldName, ctx) {
|
|
386
400
|
if (parsedType.nodeType?.kind !== _typescript.SyntaxKind.UnionType) return;
|
|
387
401
|
const nt = parsedType.nodeType;
|
|
@@ -564,6 +578,14 @@ function verifyRelationFields(result) {
|
|
|
564
578
|
if (field.isRelation && !result[field.type]?.decorators?.entity) {
|
|
565
579
|
throw new Error(`${key}.${fieldName}: Relation must point to an entity. Got "${field.type}" instead.`);
|
|
566
580
|
}
|
|
581
|
+
if (field.isInverseRelation) {
|
|
582
|
+
if (!result[field.type]?.decorators?.entity) throw new Error(`${key}.${fieldName}: InverseRelation must point to an entity. Got "${field.type}" instead.`);
|
|
583
|
+
if (field.isRequired) throw new Error(`${key}.${fieldName}: InverseRelation cannot be required. Please, add "?" to the field name.`);
|
|
584
|
+
if (!field.isArray) throw new Error(`${key}.${fieldName}: InverseRelation must be an array.`);
|
|
585
|
+
if (!field.inverseRelationField) throw new Error(`${key}.${fieldName}: InverseRelation must have a field name.`);
|
|
586
|
+
if (!result[field.type]?.fields?.[field.inverseRelationField]?.isRelation) throw new Error(`${key}.${fieldName}: InverseRelation field must point to relation field. Got "${field.inverseRelationField}" instead.`);
|
|
587
|
+
if (result[field.type]?.fields?.[field.inverseRelationField]?.type !== key) throw new Error(`${key}.${fieldName}: InverseRelation field must point to the current entity. Got "${field.inverseRelationField}" instead.`);
|
|
588
|
+
}
|
|
567
589
|
}
|
|
568
590
|
}
|
|
569
591
|
}
|
package/integrations/lib.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ScriptTarget, SyntaxKind, createSourceFile } from "typescript";
|
|
2
2
|
import _ from "lodash";
|
|
3
|
-
import pluralize from "pluralize";
|
|
4
3
|
const supportedPrimitiveTypes = ["string", "number", "boolean", "Record<string, string>", "Record<string, any>"];
|
|
5
4
|
export function parseSchema(typescriptFiles) {
|
|
6
5
|
const typeNodesMap = getTypeNodesMap(typescriptFiles);
|
|
@@ -163,7 +162,7 @@ function parseClassDeclaration(typeDeclaration, typeName, ctx) {
|
|
|
163
162
|
fields[field.name] = field;
|
|
164
163
|
}
|
|
165
164
|
const handle = _.lowerFirst(name);
|
|
166
|
-
const handlePlural =
|
|
165
|
+
const handlePlural = `${handle}/list`;
|
|
167
166
|
const result = {
|
|
168
167
|
name,
|
|
169
168
|
handle,
|
|
@@ -232,23 +231,13 @@ function parseClassMember(node, parentFields, parentName, ctx) {
|
|
|
232
231
|
throw new Error(`Cannot find field ${parentName}.${defaultValueCopyFrom}"`);
|
|
233
232
|
defaultValueType = parentField.type;
|
|
234
233
|
}
|
|
235
|
-
const
|
|
236
|
-
ctx,
|
|
237
|
-
parentName,
|
|
238
|
-
name,
|
|
239
|
-
node,
|
|
240
|
-
defaultValueType
|
|
241
|
-
);
|
|
234
|
+
const parsedType = parseFieldType(ctx, parentName, name, node, defaultValueType);
|
|
242
235
|
const result = {
|
|
243
|
-
|
|
236
|
+
...parsedType,
|
|
244
237
|
defaultValue,
|
|
245
238
|
defaultValueCopyFrom,
|
|
246
239
|
name,
|
|
247
240
|
isRequired,
|
|
248
|
-
isArray,
|
|
249
|
-
isRelation,
|
|
250
|
-
isChange,
|
|
251
|
-
relationDenormFields,
|
|
252
241
|
comment
|
|
253
242
|
};
|
|
254
243
|
if (!_.isEmpty(decorators))
|
|
@@ -282,6 +271,7 @@ function parseFieldType(ctx, parentName, fieldName, node, defaultValueType) {
|
|
|
282
271
|
};
|
|
283
272
|
parseFieldTypeArray(parsedType, parentName);
|
|
284
273
|
parseFieldTypeRelation(parsedType, parentName, ctx);
|
|
274
|
+
parseFieldTypeInverseRelation(parsedType, parentName, ctx);
|
|
285
275
|
parseFieldTypeInlineEnum(parsedType, parentName, fieldName, ctx);
|
|
286
276
|
parseFieldTypeKeyofEnum(parsedType, parentName, fieldName, ctx);
|
|
287
277
|
parseFieldTypeRecordEnum(parsedType, parentName, fieldName, ctx);
|
|
@@ -301,8 +291,10 @@ function parseFieldType(ctx, parentName, fieldName, node, defaultValueType) {
|
|
|
301
291
|
return {
|
|
302
292
|
isArray: parsedType.isArray || void 0,
|
|
303
293
|
isRelation: parsedType.isRelation || void 0,
|
|
294
|
+
isInverseRelation: parsedType.isInverseRelation || void 0,
|
|
304
295
|
isChange: parsedType.isChange || void 0,
|
|
305
296
|
relationDenormFields: parsedType.relationDenormFields,
|
|
297
|
+
inverseRelationField: parsedType.inverseRelationField,
|
|
306
298
|
type: parsedType.type
|
|
307
299
|
};
|
|
308
300
|
}
|
|
@@ -360,6 +352,30 @@ function parseFieldTypeRelation(parsedType, parentName, ctx) {
|
|
|
360
352
|
}
|
|
361
353
|
}
|
|
362
354
|
}
|
|
355
|
+
function parseFieldTypeInverseRelation(parsedType, parentName, ctx) {
|
|
356
|
+
if (parsedType.nodeType?.kind !== SyntaxKind.TypeReference)
|
|
357
|
+
return;
|
|
358
|
+
const nt = parsedType.nodeType;
|
|
359
|
+
if (nt.typeName.getText(ctx.sourceFile) === "InverseRelation") {
|
|
360
|
+
if (!nt.typeArguments?.length)
|
|
361
|
+
throw new Error(`Missing type argument for Relation<>: '${parentName}'`);
|
|
362
|
+
parsedType.type = parseStringField(ctx, nt.typeArguments[0]);
|
|
363
|
+
parsedType.isInverseRelation = true;
|
|
364
|
+
if (nt.typeArguments[1]) {
|
|
365
|
+
const taNode = nt.typeArguments[1];
|
|
366
|
+
parsedType.inverseRelationField = parseStringField(ctx, taNode);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
function parseStringField(ctx, node) {
|
|
371
|
+
if (node.kind === SyntaxKind.LiteralType) {
|
|
372
|
+
const literal = node.literal;
|
|
373
|
+
if (literal.kind === SyntaxKind.StringLiteral) {
|
|
374
|
+
return literal.text;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
throw new Error(`Unexpected type - ${node.getText(ctx.sourceFile)}`);
|
|
378
|
+
}
|
|
363
379
|
function parseFieldTypeInlineEnum(parsedType, parentName, fieldName, ctx) {
|
|
364
380
|
if (parsedType.nodeType?.kind !== SyntaxKind.UnionType)
|
|
365
381
|
return;
|
|
@@ -537,6 +553,24 @@ function verifyRelationFields(result) {
|
|
|
537
553
|
if (field.isRelation && !result[field.type]?.decorators?.entity) {
|
|
538
554
|
throw new Error(`${key}.${fieldName}: Relation must point to an entity. Got "${field.type}" instead.`);
|
|
539
555
|
}
|
|
556
|
+
if (field.isInverseRelation) {
|
|
557
|
+
if (!result[field.type]?.decorators?.entity)
|
|
558
|
+
throw new Error(`${key}.${fieldName}: InverseRelation must point to an entity. Got "${field.type}" instead.`);
|
|
559
|
+
if (field.isRequired)
|
|
560
|
+
throw new Error(`${key}.${fieldName}: InverseRelation cannot be required. Please, add "?" to the field name.`);
|
|
561
|
+
if (!field.isArray)
|
|
562
|
+
throw new Error(`${key}.${fieldName}: InverseRelation must be an array.`);
|
|
563
|
+
if (!field.inverseRelationField)
|
|
564
|
+
throw new Error(`${key}.${fieldName}: InverseRelation must have a field name.`);
|
|
565
|
+
if (!result[field.type]?.fields?.[field.inverseRelationField]?.isRelation)
|
|
566
|
+
throw new Error(
|
|
567
|
+
`${key}.${fieldName}: InverseRelation field must point to relation field. Got "${field.inverseRelationField}" instead.`
|
|
568
|
+
);
|
|
569
|
+
if (result[field.type]?.fields?.[field.inverseRelationField]?.type !== key)
|
|
570
|
+
throw new Error(
|
|
571
|
+
`${key}.${fieldName}: InverseRelation field must point to the current entity. Got "${field.inverseRelationField}" instead.`
|
|
572
|
+
);
|
|
573
|
+
}
|
|
540
574
|
}
|
|
541
575
|
}
|
|
542
576
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rads-db",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.62",
|
|
4
4
|
"packageManager": "pnpm@8.6.1",
|
|
5
5
|
"description": "Say goodbye to boilerplate code and hello to efficient and elegant syntax.",
|
|
6
6
|
"author": "",
|
|
@@ -77,7 +77,6 @@
|
|
|
77
77
|
"ignore": "^5.2.4",
|
|
78
78
|
"klaw": "^4.1.0",
|
|
79
79
|
"lodash": "^4.17.21",
|
|
80
|
-
"pluralize": "^8.0.0",
|
|
81
80
|
"uuid": ">=8",
|
|
82
81
|
"zod": "^3.21.4"
|
|
83
82
|
},
|
|
@@ -87,7 +86,6 @@
|
|
|
87
86
|
"@types/eslint": "^8.44.8",
|
|
88
87
|
"@types/klaw": "^3.0.3",
|
|
89
88
|
"@types/lodash": "4.14.191",
|
|
90
|
-
"@types/pluralize": "^0.0.29",
|
|
91
89
|
"@types/uuid": "^9.0.2",
|
|
92
90
|
"@vitest/ui": "^1.6.0",
|
|
93
91
|
"cross-env": "^7.0.3",
|