rads-db 0.1.72 → 0.1.74

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 CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  const zod = require('zod');
4
4
  const _ = require('lodash');
5
- const createMerge = require('@fastify/deepmerge');
6
5
  const uuid = require('uuid');
6
+ const createMerge = require('@fastify/deepmerge');
7
7
  const _radsDb = require('_rads-db');
8
8
 
9
9
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
@@ -98,42 +98,6 @@ function getFieldZodSchemaBase(zodSchemas, schema, field, shouldBeLazy) {
98
98
  throw new Error(`Unknown type: ${field.type}`);
99
99
  }
100
100
 
101
- const mergeFn = createMerge__default({
102
- mergeArray(options) {
103
- const clone = options.clone;
104
- return function(target, source) {
105
- return clone(source);
106
- };
107
- }
108
- });
109
- function merge(target, source) {
110
- cleanUndefined(source);
111
- const result = mergeFn(target, source);
112
- return result;
113
- }
114
- function cleanUndefined(obj) {
115
- if (!obj || !___default.isPlainObject(obj))
116
- return;
117
- cleanUndefinedInner(obj);
118
- }
119
- function cleanUndefinedInner(obj) {
120
- for (const key in obj) {
121
- if (obj[key] === void 0)
122
- delete obj[key];
123
- if (___default.isPlainObject(obj[key]))
124
- cleanUndefinedInner(obj[key]);
125
- }
126
- }
127
-
128
- function cleanUndefinedAndNull(obj) {
129
- for (const key in obj) {
130
- if (obj[key] == null)
131
- delete obj[key];
132
- if (___default.isPlainObject(obj[key]))
133
- cleanUndefinedAndNull(obj[key]);
134
- }
135
- }
136
-
137
101
  const operatorFns = {
138
102
  eq: (x, w) => x === w,
139
103
  ieq: (x, w) => x?.toLowerCase() === w?.toLowerCase(),
@@ -446,6 +410,33 @@ async function handleEffectsAfterPut(context, docs, beforePutResults, ctx) {
446
410
  );
447
411
  }
448
412
 
413
+ const mergeFn = createMerge__default({
414
+ mergeArray(options) {
415
+ const clone = options.clone;
416
+ return function(target, source) {
417
+ return clone(source);
418
+ };
419
+ }
420
+ });
421
+ function merge(target, source) {
422
+ cleanUndefined(source);
423
+ const result = mergeFn(target, source);
424
+ return result;
425
+ }
426
+ function cleanUndefined(obj) {
427
+ if (!obj || !___default.isPlainObject(obj))
428
+ return;
429
+ cleanUndefinedInner(obj);
430
+ }
431
+ function cleanUndefinedInner(obj) {
432
+ for (const key in obj) {
433
+ if (obj[key] === void 0)
434
+ delete obj[key];
435
+ if (___default.isPlainObject(obj[key]))
436
+ cleanUndefinedInner(obj[key]);
437
+ }
438
+ }
439
+
449
440
  function diff(object, base, includeDenormFields = false) {
450
441
  if (!base)
451
442
  return object;
@@ -640,154 +631,172 @@ async function fillDenormFieldsBeforePut(computedContext, docUpdates, ctx) {
640
631
  await Promise.all(promises);
641
632
  }
642
633
 
643
- function generateMethods(schema, validators, options) {
644
- const drivers = {};
645
- const opts = { computed: {}, driver: memory(), features: [], ...options };
646
- const db = {
647
- _schema: schema,
648
- uploadFile(args) {
649
- if (!opts.fileUploadDriver)
650
- throw new Error(`Missing configuration. Please specify "fileUploadDriver" argument in "createRads()".`);
651
- if (!args.containerName)
652
- args.containerName = "files";
653
- return opts.fileUploadDriver.uploadFile(args);
654
- }
655
- };
656
- const effects = {};
657
- for (const key in schema) {
658
- effects[key] = [];
634
+ function cleanUndefinedAndNull(obj) {
635
+ for (const key in obj) {
636
+ if (obj[key] == null)
637
+ delete obj[key];
638
+ if (___default.isPlainObject(obj[key]))
639
+ cleanUndefinedAndNull(obj[key]);
659
640
  }
660
- if (!opts.skipComputed) {
661
- verifyComputedPresense(schema, opts.computed);
641
+ }
642
+
643
+ function getRadsDbMethods(key, computedContext, driverInstance) {
644
+ const { schema, options, validators } = computedContext;
645
+ const mustCleanNulls = driverInstance.driverName !== "restApi";
646
+ const { precomputedFields } = schema[key];
647
+ async function getMany(args, ctx) {
648
+ args = { ...args, where: { ...args?.where } };
649
+ ctx = { ...options.context, ...ctx };
650
+ await beforeGet(args, ctx, computedContext);
651
+ const result = await driverInstance.getMany(args, ctx);
652
+ if (args.include)
653
+ await handleInclude(computedContext, args.include, result.nodes, ctx);
654
+ await handleComputed(computedContext, result.nodes, ctx);
655
+ await afterGet(result.nodes, args, ctx, computedContext);
656
+ return result;
662
657
  }
663
- verifyEventSourcingSetup(schema, effects);
664
- verifyRelationsSetup(schema, effects);
665
- for (const key in schema) {
666
- if (!schema[key].decorators.entity)
667
- continue;
668
- const { handle } = schema[key];
669
- if (!handle)
670
- throw new Error(`Missing handle for entity ${key}`);
671
- const computedContext = {
672
- schema,
673
- typeName: key,
674
- validators,
675
- options: opts,
676
- drivers,
677
- effects,
678
- db
679
- };
680
- const driverInstance = getDriverInstance(schema, key, opts.driver, drivers);
681
- const mustCleanNulls = driverInstance.driverName !== "restApi";
682
- const { precomputedFields } = schema[key];
683
- db[handle] = {
684
- getAgg: async (args, ctx) => {
685
- args = { ...args, where: { ...args?.where } };
686
- if (!args.agg)
687
- throw new Error(`Please provide 'agg' argument`);
688
- ctx = { ...opts.context, ...ctx };
689
- await beforeGet(args, ctx, computedContext);
690
- const result = await driverInstance.getAgg(args, ctx);
691
- return result;
692
- },
693
- get: async (args, ctx) => {
694
- args = { ...args, where: { ...args?.where } };
695
- ctx = { ...opts.context, ...ctx };
696
- await beforeGet(args, ctx, computedContext);
697
- const result = await driverInstance.get(args, ctx);
698
- const resultArray = [result];
699
- if (result && args.include)
700
- await handleInclude(computedContext, args.include, resultArray, ctx);
701
- if (result)
702
- await handleComputed(computedContext, resultArray, ctx);
703
- await afterGet(resultArray, args, ctx, computedContext);
704
- return result;
705
- },
706
- getMany: async (args, ctx) => {
707
- args = { ...args, where: { ...args?.where } };
708
- ctx = { ...opts.context, ...ctx };
709
- await beforeGet(args, ctx, computedContext);
710
- const result = await driverInstance.getMany(args, ctx);
711
- if (args.include)
712
- await handleInclude(computedContext, args.include, result.nodes, ctx);
713
- await handleComputed(computedContext, result.nodes, ctx);
714
- await afterGet(result.nodes, args, ctx, computedContext);
715
- return result;
716
- },
717
- getAll: async (args, ctx) => {
718
- args = { ...args, where: { ...args?.where } };
719
- ctx = { ...opts.context, ...ctx };
720
- await beforeGet(args, ctx, computedContext);
721
- const result = await driverInstance.getAll(args, ctx);
722
- if (args.include)
723
- await handleInclude(computedContext, args.include, result, ctx);
724
- await handleComputed(computedContext, result, ctx);
725
- await afterGet(result, args, ctx, computedContext);
726
- return result;
727
- },
728
- put: async (doc, ctx) => {
729
- if (schema[key].decorators.precomputed)
730
- throw new Error("Forbidden. Use events instead.");
731
- if (!doc?.id)
732
- throw new Error("Id is required");
733
- ctx = { ...opts.context, ...ctx };
734
- const oldDoc = await driverInstance.get({ where: { id: doc.id } }, ctx);
735
- if (oldDoc)
736
- doc = merge(oldDoc, doc);
658
+ async function putMany(docs, ctx) {
659
+ if (schema[key].decorators.precomputed)
660
+ throw new Error("Forbidden. Use events instead.");
661
+ const ids = docs.map((i) => {
662
+ if (!i?.id)
663
+ throw new Error("Id is required");
664
+ return i.id;
665
+ });
666
+ ctx = { ...options.context, ...ctx };
667
+ const oldDocs = await driverInstance.getAll({ where: { id_in: ids } }, ctx);
668
+ const oldDocsById = ___default.keyBy(oldDocs, "id");
669
+ const docArgsToSave = docs.map((doc) => {
670
+ const oldDoc = oldDocsById[doc.id];
671
+ doc = merge(oldDoc, doc);
672
+ if (mustCleanNulls)
673
+ cleanUndefinedAndNull(doc);
674
+ doc = validators[key](doc);
675
+ return { oldDoc, doc };
676
+ });
677
+ const docsToSave = docArgsToSave.map((x) => x.doc);
678
+ await fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
679
+ await handlePrecomputed(computedContext, docArgsToSave, ctx);
680
+ const beforePutResults = await handleEffectsBeforePut(computedContext, docArgsToSave, ctx);
681
+ await beforePut(docArgsToSave, ctx, computedContext);
682
+ await driverInstance.putMany(docsToSave, ctx);
683
+ await handleEffectsAfterPut(computedContext, docArgsToSave, beforePutResults, ctx);
684
+ await afterPut(docArgsToSave, ctx, computedContext);
685
+ return docsToSave;
686
+ }
687
+ return {
688
+ getMany,
689
+ putMany,
690
+ driver: driverInstance,
691
+ getAgg: async (args, ctx) => {
692
+ args = { ...args, where: { ...args?.where } };
693
+ if (!args.agg)
694
+ throw new Error(`Please provide 'agg' argument`);
695
+ ctx = { ...options.context, ...ctx };
696
+ await beforeGet(args, ctx, computedContext);
697
+ const result = await driverInstance.getAgg(args, ctx);
698
+ return result;
699
+ },
700
+ get: async (args, ctx) => {
701
+ args = { ...args, where: { ...args?.where } };
702
+ ctx = { ...options.context, ...ctx };
703
+ await beforeGet(args, ctx, computedContext);
704
+ const result = await driverInstance.get(args, ctx);
705
+ const resultArray = [result];
706
+ if (result && args.include)
707
+ await handleInclude(computedContext, args.include, resultArray, ctx);
708
+ if (result)
709
+ await handleComputed(computedContext, resultArray, ctx);
710
+ await afterGet(resultArray, args, ctx, computedContext);
711
+ return result;
712
+ },
713
+ getAll: async (args, ctx) => {
714
+ args = { ...args, where: { ...args?.where } };
715
+ ctx = { ...options.context, ...ctx };
716
+ await beforeGet(args, ctx, computedContext);
717
+ const result = await driverInstance.getAll(args, ctx);
718
+ if (args.include)
719
+ await handleInclude(computedContext, args.include, result, ctx);
720
+ await handleComputed(computedContext, result, ctx);
721
+ await afterGet(result, args, ctx, computedContext);
722
+ return result;
723
+ },
724
+ put: async (doc, ctx) => {
725
+ if (schema[key].decorators.precomputed)
726
+ throw new Error("Forbidden. Use events instead.");
727
+ if (!doc?.id)
728
+ throw new Error("Id is required");
729
+ ctx = { ...options.context, ...ctx };
730
+ const oldDoc = await driverInstance.get({ where: { id: doc.id } }, ctx);
731
+ if (oldDoc)
732
+ doc = merge(oldDoc, doc);
733
+ if (mustCleanNulls)
734
+ cleanUndefinedAndNull(doc);
735
+ doc = validators[key](doc);
736
+ if (oldDoc && precomputedFields) {
737
+ for (const f of precomputedFields) {
738
+ ___default.set(doc, f, ___default.get(oldDoc, f));
739
+ }
740
+ }
741
+ const docArgsToSave = [{ doc, oldDoc }];
742
+ await fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
743
+ await handlePrecomputed(computedContext, docArgsToSave, ctx);
744
+ const beforePutResults = await handleEffectsBeforePut(computedContext, docArgsToSave, ctx);
745
+ await beforePut(docArgsToSave, ctx, computedContext);
746
+ await driverInstance.put(doc, ctx);
747
+ await handleEffectsAfterPut(computedContext, docArgsToSave, beforePutResults, ctx);
748
+ await afterPut(docArgsToSave, ctx, computedContext);
749
+ return doc;
750
+ },
751
+ async verifyMany(args, ctx) {
752
+ ctx = { ...options.context, ...ctx };
753
+ if (args?.dryRun) {
754
+ ctx._logs = ctx._logs || [];
755
+ ctx.dryRun = true;
756
+ }
757
+ const { _logs } = ctx;
758
+ const { nodes, cursor } = await driverInstance.getMany(args, ctx);
759
+ let correctCount = 0;
760
+ let incorrectCount = 0;
761
+ const incorrectDocs = [];
762
+ const oldDocs = nodes;
763
+ const docArgsToSave = oldDocs.map((oldDoc) => {
764
+ let doc = merge(oldDoc, {});
765
+ if (args?.recompute) {
766
+ for (const p of args.recompute) {
767
+ delete doc[p];
768
+ }
769
+ }
737
770
  if (mustCleanNulls)
738
771
  cleanUndefinedAndNull(doc);
739
772
  doc = validators[key](doc);
740
- if (oldDoc && precomputedFields) {
741
- for (const f of precomputedFields) {
742
- ___default.set(doc, f, ___default.get(oldDoc, f));
743
- }
773
+ return { oldDoc, doc };
774
+ });
775
+ await fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
776
+ await handlePrecomputed(computedContext, docArgsToSave, ctx);
777
+ const beforePutResults = await handleEffectsBeforePut(computedContext, docArgsToSave, ctx);
778
+ await beforePut(docArgsToSave, ctx, computedContext);
779
+ for (const { oldDoc, doc } of docArgsToSave) {
780
+ const d = diff(doc, oldDoc);
781
+ if (___default.isEmpty(d))
782
+ correctCount++;
783
+ else {
784
+ incorrectCount++;
785
+ const objToAdd = { id: doc.id, diff: d };
786
+ incorrectDocs.push(objToAdd);
744
787
  }
745
- const docArgsToSave = [{ doc, oldDoc }];
746
- await fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
747
- await handlePrecomputed(computedContext, docArgsToSave, ctx);
748
- const beforePutResults = await handleEffectsBeforePut(computedContext, docArgsToSave, ctx);
749
- await beforePut(docArgsToSave, ctx, computedContext);
750
- await driverInstance.put(doc, ctx);
751
- await handleEffectsAfterPut(computedContext, docArgsToSave, beforePutResults, ctx);
752
- await afterPut(docArgsToSave, ctx, computedContext);
753
- return doc;
754
- },
755
- putMany: async (docs, ctx) => {
756
- if (schema[key].decorators.precomputed)
757
- throw new Error("Forbidden. Use events instead.");
758
- const ids = docs.map((i) => {
759
- if (!i?.id)
760
- throw new Error("Id is required");
761
- return i.id;
762
- });
763
- ctx = { ...opts.context, ...ctx };
764
- const oldDocs = await driverInstance.getAll({ where: { id_in: ids } }, ctx);
765
- const oldDocsById = ___default.keyBy(oldDocs, "id");
766
- const docArgsToSave = docs.map((doc) => {
767
- const oldDoc = oldDocsById[doc.id];
768
- doc = merge(oldDoc, doc);
769
- if (mustCleanNulls)
770
- cleanUndefinedAndNull(doc);
771
- doc = validators[key](doc);
772
- return { oldDoc, doc };
773
- });
774
- const docsToSave = docArgsToSave.map((x) => x.doc);
775
- await fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
776
- await handlePrecomputed(computedContext, docArgsToSave, ctx);
777
- const beforePutResults = await handleEffectsBeforePut(computedContext, docArgsToSave, ctx);
778
- await beforePut(docArgsToSave, ctx, computedContext);
779
- await driverInstance.putMany(docsToSave, ctx);
780
- await handleEffectsAfterPut(computedContext, docArgsToSave, beforePutResults, ctx);
781
- await afterPut(docArgsToSave, ctx, computedContext);
782
- return docsToSave;
783
- },
784
- clear: (ctx) => {
785
- ctx = { ...opts.context, ...ctx };
786
- return driverInstance.clear(ctx);
787
788
  }
788
- };
789
- }
790
- return db;
789
+ const docsToSave = docArgsToSave.map((x) => x.doc);
790
+ await driverInstance.putMany(docsToSave, ctx);
791
+ await handleEffectsAfterPut(computedContext, docArgsToSave, beforePutResults, ctx);
792
+ await afterPut(docArgsToSave, ctx, computedContext);
793
+ return { _logs, cursor, correctCount, incorrectCount, incorrectDocs };
794
+ },
795
+ clear: (ctx) => {
796
+ ctx = { ...options.context, ...ctx };
797
+ return driverInstance.clear(ctx);
798
+ }
799
+ };
791
800
  }
792
801
  async function beforeGet(args, ctx, computedContext) {
793
802
  for (const f of computedContext.options.features) {
@@ -864,6 +873,49 @@ function getExistingDriverInstance(entityName, drivers) {
864
873
  }
865
874
  return drivers[entityName];
866
875
  }
876
+
877
+ function generateMethods(schema, validators, options) {
878
+ const drivers = {};
879
+ const opts = { computed: {}, driver: memory(), features: [], ...options };
880
+ const db = {
881
+ _schema: schema,
882
+ uploadFile(args) {
883
+ if (!opts.fileUploadDriver)
884
+ throw new Error(`Missing configuration. Please specify "fileUploadDriver" argument in "createRads()".`);
885
+ if (!args.containerName)
886
+ args.containerName = "files";
887
+ return opts.fileUploadDriver.uploadFile(args);
888
+ }
889
+ };
890
+ const effects = {};
891
+ for (const key in schema) {
892
+ effects[key] = [];
893
+ }
894
+ if (!opts.skipComputed) {
895
+ verifyComputedPresense(schema, opts.computed);
896
+ }
897
+ verifyEventSourcingSetup(schema, effects);
898
+ verifyRelationsSetup(schema, effects);
899
+ for (const key in schema) {
900
+ if (!schema[key].decorators.entity)
901
+ continue;
902
+ const { handle } = schema[key];
903
+ if (!handle)
904
+ throw new Error(`Missing handle for entity ${key}`);
905
+ const computedContext = {
906
+ schema,
907
+ typeName: key,
908
+ validators,
909
+ options: opts,
910
+ drivers,
911
+ effects,
912
+ db
913
+ };
914
+ const driverInstance = getDriverInstance(schema, key, opts.driver, drivers);
915
+ db[handle] = getRadsDbMethods(key, computedContext, driverInstance);
916
+ }
917
+ return db;
918
+ }
867
919
  function getDriverInstance(schema, key, driver, drivers) {
868
920
  if (!drivers[key]) {
869
921
  drivers[key] = getDriverInstanceInner(schema, key, driver);
@@ -872,6 +924,7 @@ function getDriverInstance(schema, key, driver, drivers) {
872
924
  }
873
925
  function getDriverInstanceInner(schema, key, driverConstructor) {
874
926
  const driverInstance = driverConstructor(schema, key);
927
+ addDryRunSupport(driverInstance, key);
875
928
  async function getAll(args, ctx) {
876
929
  const result = [];
877
930
  let cursor = args.cursor;
@@ -914,6 +967,40 @@ function getDriverInstanceInner(schema, key, driverConstructor) {
914
967
  ...driverInstance
915
968
  };
916
969
  }
970
+ function addDryRunSupport(driverInstance, entity) {
971
+ const { clear, put, putMany } = driverInstance;
972
+ if (clear) {
973
+ driverInstance.clear = (ctx) => {
974
+ if (ctx?.dryRun) {
975
+ if (!ctx._logs)
976
+ ctx._logs = [];
977
+ ctx._logs.push({ method: "clear", entity });
978
+ return;
979
+ }
980
+ return clear(ctx);
981
+ };
982
+ }
983
+ if (put) {
984
+ driverInstance.put = (data, ctx) => {
985
+ if (ctx?.dryRun) {
986
+ if (!ctx._logs)
987
+ ctx._logs = [];
988
+ ctx._logs.push({ method: "put", entity, data });
989
+ return;
990
+ }
991
+ return put(data, ctx);
992
+ };
993
+ }
994
+ driverInstance.putMany = (data, ctx) => {
995
+ if (ctx?.dryRun) {
996
+ if (!ctx._logs)
997
+ ctx._logs = [];
998
+ ctx._logs.push({ method: "putMany", entity, data });
999
+ return;
1000
+ }
1001
+ return putMany(data, ctx);
1002
+ };
1003
+ }
917
1004
 
918
1005
  function entity(meta) {
919
1006
  return function(classConstructor, _ctx) {
package/dist/index.d.ts CHANGED
@@ -10,6 +10,20 @@ interface GetManyArgs<E, EN extends keyof EntityMeta, W> extends GetArgs<E, EN,
10
10
  maxItemCount?: number;
11
11
  orderBy?: string;
12
12
  }
13
+ interface VerifyManyArgs<E, EN extends keyof EntityMeta, W> extends GetManyArgs<E, EN, W> {
14
+ recompute?: string[];
15
+ dryRun?: boolean;
16
+ }
17
+ interface VerifyManyResponse {
18
+ cursor: string | null;
19
+ correctCount: number;
20
+ incorrectCount: number;
21
+ incorrectDocs: {
22
+ id: string;
23
+ diff: any;
24
+ toRemove?: any;
25
+ }[];
26
+ }
13
27
  interface GetArgs<E, EN extends keyof EntityMeta, W> {
14
28
  where?: W;
15
29
  include?: GetArgsInclude<E, EN>;
@@ -22,6 +36,7 @@ type GetAggArgsAgg<EN extends keyof EntityMeta, F extends string = EntityMeta[EN
22
36
  type GetManyArgsAny = GetManyArgs<any, any, any>;
23
37
  type GetArgsAny = GetArgs<any, any, any>;
24
38
  type GetAggArgsAny = GetAggArgs<any, any>;
39
+ type VerifyManyArgsAny = VerifyManyArgs<any, any, any>;
25
40
  type GetArgsInclude<E, EN extends keyof EntityMeta, R extends keyof E = keyof EntityMeta[EN]['relations'] & keyof E> = [R] extends [never] ? Record<string, never> : {
26
41
  _pick?: EntityMeta[EN]['primitives'][];
27
42
  } & {
@@ -61,12 +76,16 @@ type PutArgs<T> = {
61
76
  } & DeepPartialNullable<T>;
62
77
  interface EntityMethods<E, EN extends keyof EntityMeta, W> {
63
78
  createObject(defaultValues: Partial<E>): E;
79
+ /** Used to access underlying mechanism of storage directly.
80
+ * Warning: bypasses all rads features - schema won't be validated, default values won't be filled, etc. */
81
+ driver: Driver;
64
82
  get<A extends GetArgs<E, EN, W>>(args: A): MaybePromise$1<GetResponse<E, EN, A>>;
65
83
  getMany<A extends GetManyArgs<E, EN, W>>(args?: A): MaybePromise$1<GetManyResponse<E, EN, A>>;
66
84
  getAgg<A extends GetAggArgs<EN, W>>(args: A): MaybePromise$1<GetAggResponse<EN, A>>;
67
85
  getAll<A extends GetManyArgs<E, EN, W>>(args?: A): MaybePromise$1<GetManyResponse<E, EN, A>['nodes']>;
68
86
  put(data: PutArgs<E>): MaybePromise$1<GetResponseNoInclude<E, EN>>;
69
87
  putMany(data: PutArgs<E>[]): MaybePromise$1<GetResponseNoInclude<E, EN>[]>;
88
+ verifyMany<A extends VerifyManyArgs<E, EN, W>>(args?: A): MaybePromise$1<VerifyManyResponse>;
70
89
  }
71
90
 
72
91
  type MaybePromise<T> = Promise<T> | T;
@@ -142,7 +161,7 @@ interface CreateRadsArgs {
142
161
  skipComputed?: boolean;
143
162
  computed?: Record<string, Record<string, Function>>;
144
163
  beforeGet?: (args: GetArgsAny, ctx: RadsRequestContext, context: ComputedContext) => MaybePromise<void>;
145
- context?: Record<string, any> & RadsRequestContext;
164
+ context?: Record<string, any> & Omit<RadsRequestContext, 'dryRun' | 'silent'>;
146
165
  features?: RadsFeature[];
147
166
  }
148
167
  type Schema = Record<string, TypeDefinition>;
@@ -253,11 +272,16 @@ interface RadsVitePluginOptions extends GenerateClientOptions {
253
272
  exposeModulesInDev?: boolean | RegExp;
254
273
  }
255
274
  interface RadsRequestContext {
275
+ _logs?: any[];
256
276
  log?: (requestInfo: any) => void;
257
277
  getUser?: () => {
258
278
  id: string;
259
279
  role: string;
260
280
  } | undefined;
281
+ /** If true, no changes made to the database - instead, all changes are returned as logs */
282
+ dryRun?: boolean;
283
+ /** (Not supported yet) If true, updatedAt/updatedBy properties are not updated, and log records are not created */
284
+ silent?: boolean;
261
285
  }
262
286
  interface GithubTreeResponse {
263
287
  sha: string;
@@ -295,4 +319,4 @@ declare function computed(meta?: ComputedDecoratorArgs): (a: any, b?: ClassField
295
319
 
296
320
  declare function createRads(args?: CreateRadsArgs): RadsDb;
297
321
 
298
- export { Change, ComputedContext, ComputedDecoratorArgs, CreateRadsArgs, DeepPartial, DeepPartialNullable, Driver, DriverConstructor, EntityDecoratorArgs, EntityMethods, FieldDecoratorArgs, FieldDefinition, FileUploadArgs, FileUploadDriver, GenerateClientNormalizedOptions, GenerateClientOptions, GetAggArgs, GetAggArgsAgg, GetAggArgsAny, GetAggResponse, GetArgs, GetArgsAny, GetArgsInclude, GetManyArgs, GetManyArgsAny, GetManyResponse, GetResponse, GetResponseInclude, GetResponseIncludeSelect, GetResponseNoInclude, GetRestRoutesArgs, GetRestRoutesOptions, GetRestRoutesResponse, GithubTreeItem, GithubTreeResponse, MinimalDriver, PutArgs, PutEffect, RadsFeature, RadsRequestContext, RadsVitePluginOptions, Relation, RestFileUploadDriverOptions, Schema, SchemaValidators, TypeDefinition, UiDecoratorArgs, UiFieldDecoratorArgs, ValidateEntityDecoratorArgs, ValidateFieldDecoratorArgs, ValidateStringDecoratorArgs, computed, createRads, entity, field, precomputed, ui, validate };
322
+ export { Change, ComputedContext, ComputedDecoratorArgs, CreateRadsArgs, DeepPartial, DeepPartialNullable, Driver, DriverConstructor, EntityDecoratorArgs, EntityMethods, FieldDecoratorArgs, FieldDefinition, FileUploadArgs, FileUploadDriver, GenerateClientNormalizedOptions, GenerateClientOptions, GetAggArgs, GetAggArgsAgg, GetAggArgsAny, GetAggResponse, GetArgs, GetArgsAny, GetArgsInclude, GetManyArgs, GetManyArgsAny, GetManyResponse, GetResponse, GetResponseInclude, GetResponseIncludeSelect, GetResponseNoInclude, GetRestRoutesArgs, GetRestRoutesOptions, GetRestRoutesResponse, GithubTreeItem, GithubTreeResponse, MinimalDriver, PutArgs, PutEffect, RadsFeature, RadsRequestContext, RadsVitePluginOptions, Relation, RestFileUploadDriverOptions, Schema, SchemaValidators, TypeDefinition, UiDecoratorArgs, UiFieldDecoratorArgs, ValidateEntityDecoratorArgs, ValidateFieldDecoratorArgs, ValidateStringDecoratorArgs, VerifyManyArgs, VerifyManyArgsAny, VerifyManyResponse, computed, createRads, entity, field, precomputed, ui, validate };
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import _ from 'lodash';
3
- import createMerge from '@fastify/deepmerge';
4
3
  import { v4 } from 'uuid';
4
+ import createMerge from '@fastify/deepmerge';
5
5
  import { schema } from '_rads-db';
6
6
 
7
7
  function generateValidators(schema) {
@@ -91,42 +91,6 @@ function getFieldZodSchemaBase(zodSchemas, schema, field, shouldBeLazy) {
91
91
  throw new Error(`Unknown type: ${field.type}`);
92
92
  }
93
93
 
94
- const mergeFn = createMerge({
95
- mergeArray(options) {
96
- const clone = options.clone;
97
- return function(target, source) {
98
- return clone(source);
99
- };
100
- }
101
- });
102
- function merge(target, source) {
103
- cleanUndefined(source);
104
- const result = mergeFn(target, source);
105
- return result;
106
- }
107
- function cleanUndefined(obj) {
108
- if (!obj || !_.isPlainObject(obj))
109
- return;
110
- cleanUndefinedInner(obj);
111
- }
112
- function cleanUndefinedInner(obj) {
113
- for (const key in obj) {
114
- if (obj[key] === void 0)
115
- delete obj[key];
116
- if (_.isPlainObject(obj[key]))
117
- cleanUndefinedInner(obj[key]);
118
- }
119
- }
120
-
121
- function cleanUndefinedAndNull(obj) {
122
- for (const key in obj) {
123
- if (obj[key] == null)
124
- delete obj[key];
125
- if (_.isPlainObject(obj[key]))
126
- cleanUndefinedAndNull(obj[key]);
127
- }
128
- }
129
-
130
94
  const operatorFns = {
131
95
  eq: (x, w) => x === w,
132
96
  ieq: (x, w) => x?.toLowerCase() === w?.toLowerCase(),
@@ -439,6 +403,33 @@ async function handleEffectsAfterPut(context, docs, beforePutResults, ctx) {
439
403
  );
440
404
  }
441
405
 
406
+ const mergeFn = createMerge({
407
+ mergeArray(options) {
408
+ const clone = options.clone;
409
+ return function(target, source) {
410
+ return clone(source);
411
+ };
412
+ }
413
+ });
414
+ function merge(target, source) {
415
+ cleanUndefined(source);
416
+ const result = mergeFn(target, source);
417
+ return result;
418
+ }
419
+ function cleanUndefined(obj) {
420
+ if (!obj || !_.isPlainObject(obj))
421
+ return;
422
+ cleanUndefinedInner(obj);
423
+ }
424
+ function cleanUndefinedInner(obj) {
425
+ for (const key in obj) {
426
+ if (obj[key] === void 0)
427
+ delete obj[key];
428
+ if (_.isPlainObject(obj[key]))
429
+ cleanUndefinedInner(obj[key]);
430
+ }
431
+ }
432
+
442
433
  function diff(object, base, includeDenormFields = false) {
443
434
  if (!base)
444
435
  return object;
@@ -633,154 +624,172 @@ async function fillDenormFieldsBeforePut(computedContext, docUpdates, ctx) {
633
624
  await Promise.all(promises);
634
625
  }
635
626
 
636
- function generateMethods(schema, validators, options) {
637
- const drivers = {};
638
- const opts = { computed: {}, driver: memory(), features: [], ...options };
639
- const db = {
640
- _schema: schema,
641
- uploadFile(args) {
642
- if (!opts.fileUploadDriver)
643
- throw new Error(`Missing configuration. Please specify "fileUploadDriver" argument in "createRads()".`);
644
- if (!args.containerName)
645
- args.containerName = "files";
646
- return opts.fileUploadDriver.uploadFile(args);
647
- }
648
- };
649
- const effects = {};
650
- for (const key in schema) {
651
- effects[key] = [];
627
+ function cleanUndefinedAndNull(obj) {
628
+ for (const key in obj) {
629
+ if (obj[key] == null)
630
+ delete obj[key];
631
+ if (_.isPlainObject(obj[key]))
632
+ cleanUndefinedAndNull(obj[key]);
652
633
  }
653
- if (!opts.skipComputed) {
654
- verifyComputedPresense(schema, opts.computed);
634
+ }
635
+
636
+ function getRadsDbMethods(key, computedContext, driverInstance) {
637
+ const { schema, options, validators } = computedContext;
638
+ const mustCleanNulls = driverInstance.driverName !== "restApi";
639
+ const { precomputedFields } = schema[key];
640
+ async function getMany(args, ctx) {
641
+ args = { ...args, where: { ...args?.where } };
642
+ ctx = { ...options.context, ...ctx };
643
+ await beforeGet(args, ctx, computedContext);
644
+ const result = await driverInstance.getMany(args, ctx);
645
+ if (args.include)
646
+ await handleInclude(computedContext, args.include, result.nodes, ctx);
647
+ await handleComputed(computedContext, result.nodes, ctx);
648
+ await afterGet(result.nodes, args, ctx, computedContext);
649
+ return result;
655
650
  }
656
- verifyEventSourcingSetup(schema, effects);
657
- verifyRelationsSetup(schema, effects);
658
- for (const key in schema) {
659
- if (!schema[key].decorators.entity)
660
- continue;
661
- const { handle } = schema[key];
662
- if (!handle)
663
- throw new Error(`Missing handle for entity ${key}`);
664
- const computedContext = {
665
- schema,
666
- typeName: key,
667
- validators,
668
- options: opts,
669
- drivers,
670
- effects,
671
- db
672
- };
673
- const driverInstance = getDriverInstance(schema, key, opts.driver, drivers);
674
- const mustCleanNulls = driverInstance.driverName !== "restApi";
675
- const { precomputedFields } = schema[key];
676
- db[handle] = {
677
- getAgg: async (args, ctx) => {
678
- args = { ...args, where: { ...args?.where } };
679
- if (!args.agg)
680
- throw new Error(`Please provide 'agg' argument`);
681
- ctx = { ...opts.context, ...ctx };
682
- await beforeGet(args, ctx, computedContext);
683
- const result = await driverInstance.getAgg(args, ctx);
684
- return result;
685
- },
686
- get: async (args, ctx) => {
687
- args = { ...args, where: { ...args?.where } };
688
- ctx = { ...opts.context, ...ctx };
689
- await beforeGet(args, ctx, computedContext);
690
- const result = await driverInstance.get(args, ctx);
691
- const resultArray = [result];
692
- if (result && args.include)
693
- await handleInclude(computedContext, args.include, resultArray, ctx);
694
- if (result)
695
- await handleComputed(computedContext, resultArray, ctx);
696
- await afterGet(resultArray, args, ctx, computedContext);
697
- return result;
698
- },
699
- getMany: async (args, ctx) => {
700
- args = { ...args, where: { ...args?.where } };
701
- ctx = { ...opts.context, ...ctx };
702
- await beforeGet(args, ctx, computedContext);
703
- const result = await driverInstance.getMany(args, ctx);
704
- if (args.include)
705
- await handleInclude(computedContext, args.include, result.nodes, ctx);
706
- await handleComputed(computedContext, result.nodes, ctx);
707
- await afterGet(result.nodes, args, ctx, computedContext);
708
- return result;
709
- },
710
- getAll: async (args, ctx) => {
711
- args = { ...args, where: { ...args?.where } };
712
- ctx = { ...opts.context, ...ctx };
713
- await beforeGet(args, ctx, computedContext);
714
- const result = await driverInstance.getAll(args, ctx);
715
- if (args.include)
716
- await handleInclude(computedContext, args.include, result, ctx);
717
- await handleComputed(computedContext, result, ctx);
718
- await afterGet(result, args, ctx, computedContext);
719
- return result;
720
- },
721
- put: async (doc, ctx) => {
722
- if (schema[key].decorators.precomputed)
723
- throw new Error("Forbidden. Use events instead.");
724
- if (!doc?.id)
725
- throw new Error("Id is required");
726
- ctx = { ...opts.context, ...ctx };
727
- const oldDoc = await driverInstance.get({ where: { id: doc.id } }, ctx);
728
- if (oldDoc)
729
- doc = merge(oldDoc, doc);
651
+ async function putMany(docs, ctx) {
652
+ if (schema[key].decorators.precomputed)
653
+ throw new Error("Forbidden. Use events instead.");
654
+ const ids = docs.map((i) => {
655
+ if (!i?.id)
656
+ throw new Error("Id is required");
657
+ return i.id;
658
+ });
659
+ ctx = { ...options.context, ...ctx };
660
+ const oldDocs = await driverInstance.getAll({ where: { id_in: ids } }, ctx);
661
+ const oldDocsById = _.keyBy(oldDocs, "id");
662
+ const docArgsToSave = docs.map((doc) => {
663
+ const oldDoc = oldDocsById[doc.id];
664
+ doc = merge(oldDoc, doc);
665
+ if (mustCleanNulls)
666
+ cleanUndefinedAndNull(doc);
667
+ doc = validators[key](doc);
668
+ return { oldDoc, doc };
669
+ });
670
+ const docsToSave = docArgsToSave.map((x) => x.doc);
671
+ await fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
672
+ await handlePrecomputed(computedContext, docArgsToSave, ctx);
673
+ const beforePutResults = await handleEffectsBeforePut(computedContext, docArgsToSave, ctx);
674
+ await beforePut(docArgsToSave, ctx, computedContext);
675
+ await driverInstance.putMany(docsToSave, ctx);
676
+ await handleEffectsAfterPut(computedContext, docArgsToSave, beforePutResults, ctx);
677
+ await afterPut(docArgsToSave, ctx, computedContext);
678
+ return docsToSave;
679
+ }
680
+ return {
681
+ getMany,
682
+ putMany,
683
+ driver: driverInstance,
684
+ getAgg: async (args, ctx) => {
685
+ args = { ...args, where: { ...args?.where } };
686
+ if (!args.agg)
687
+ throw new Error(`Please provide 'agg' argument`);
688
+ ctx = { ...options.context, ...ctx };
689
+ await beforeGet(args, ctx, computedContext);
690
+ const result = await driverInstance.getAgg(args, ctx);
691
+ return result;
692
+ },
693
+ get: async (args, ctx) => {
694
+ args = { ...args, where: { ...args?.where } };
695
+ ctx = { ...options.context, ...ctx };
696
+ await beforeGet(args, ctx, computedContext);
697
+ const result = await driverInstance.get(args, ctx);
698
+ const resultArray = [result];
699
+ if (result && args.include)
700
+ await handleInclude(computedContext, args.include, resultArray, ctx);
701
+ if (result)
702
+ await handleComputed(computedContext, resultArray, ctx);
703
+ await afterGet(resultArray, args, ctx, computedContext);
704
+ return result;
705
+ },
706
+ getAll: async (args, ctx) => {
707
+ args = { ...args, where: { ...args?.where } };
708
+ ctx = { ...options.context, ...ctx };
709
+ await beforeGet(args, ctx, computedContext);
710
+ const result = await driverInstance.getAll(args, ctx);
711
+ if (args.include)
712
+ await handleInclude(computedContext, args.include, result, ctx);
713
+ await handleComputed(computedContext, result, ctx);
714
+ await afterGet(result, args, ctx, computedContext);
715
+ return result;
716
+ },
717
+ put: async (doc, ctx) => {
718
+ if (schema[key].decorators.precomputed)
719
+ throw new Error("Forbidden. Use events instead.");
720
+ if (!doc?.id)
721
+ throw new Error("Id is required");
722
+ ctx = { ...options.context, ...ctx };
723
+ const oldDoc = await driverInstance.get({ where: { id: doc.id } }, ctx);
724
+ if (oldDoc)
725
+ doc = merge(oldDoc, doc);
726
+ if (mustCleanNulls)
727
+ cleanUndefinedAndNull(doc);
728
+ doc = validators[key](doc);
729
+ if (oldDoc && precomputedFields) {
730
+ for (const f of precomputedFields) {
731
+ _.set(doc, f, _.get(oldDoc, f));
732
+ }
733
+ }
734
+ const docArgsToSave = [{ doc, oldDoc }];
735
+ await fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
736
+ await handlePrecomputed(computedContext, docArgsToSave, ctx);
737
+ const beforePutResults = await handleEffectsBeforePut(computedContext, docArgsToSave, ctx);
738
+ await beforePut(docArgsToSave, ctx, computedContext);
739
+ await driverInstance.put(doc, ctx);
740
+ await handleEffectsAfterPut(computedContext, docArgsToSave, beforePutResults, ctx);
741
+ await afterPut(docArgsToSave, ctx, computedContext);
742
+ return doc;
743
+ },
744
+ async verifyMany(args, ctx) {
745
+ ctx = { ...options.context, ...ctx };
746
+ if (args?.dryRun) {
747
+ ctx._logs = ctx._logs || [];
748
+ ctx.dryRun = true;
749
+ }
750
+ const { _logs } = ctx;
751
+ const { nodes, cursor } = await driverInstance.getMany(args, ctx);
752
+ let correctCount = 0;
753
+ let incorrectCount = 0;
754
+ const incorrectDocs = [];
755
+ const oldDocs = nodes;
756
+ const docArgsToSave = oldDocs.map((oldDoc) => {
757
+ let doc = merge(oldDoc, {});
758
+ if (args?.recompute) {
759
+ for (const p of args.recompute) {
760
+ delete doc[p];
761
+ }
762
+ }
730
763
  if (mustCleanNulls)
731
764
  cleanUndefinedAndNull(doc);
732
765
  doc = validators[key](doc);
733
- if (oldDoc && precomputedFields) {
734
- for (const f of precomputedFields) {
735
- _.set(doc, f, _.get(oldDoc, f));
736
- }
766
+ return { oldDoc, doc };
767
+ });
768
+ await fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
769
+ await handlePrecomputed(computedContext, docArgsToSave, ctx);
770
+ const beforePutResults = await handleEffectsBeforePut(computedContext, docArgsToSave, ctx);
771
+ await beforePut(docArgsToSave, ctx, computedContext);
772
+ for (const { oldDoc, doc } of docArgsToSave) {
773
+ const d = diff(doc, oldDoc);
774
+ if (_.isEmpty(d))
775
+ correctCount++;
776
+ else {
777
+ incorrectCount++;
778
+ const objToAdd = { id: doc.id, diff: d };
779
+ incorrectDocs.push(objToAdd);
737
780
  }
738
- const docArgsToSave = [{ doc, oldDoc }];
739
- await fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
740
- await handlePrecomputed(computedContext, docArgsToSave, ctx);
741
- const beforePutResults = await handleEffectsBeforePut(computedContext, docArgsToSave, ctx);
742
- await beforePut(docArgsToSave, ctx, computedContext);
743
- await driverInstance.put(doc, ctx);
744
- await handleEffectsAfterPut(computedContext, docArgsToSave, beforePutResults, ctx);
745
- await afterPut(docArgsToSave, ctx, computedContext);
746
- return doc;
747
- },
748
- putMany: async (docs, ctx) => {
749
- if (schema[key].decorators.precomputed)
750
- throw new Error("Forbidden. Use events instead.");
751
- const ids = docs.map((i) => {
752
- if (!i?.id)
753
- throw new Error("Id is required");
754
- return i.id;
755
- });
756
- ctx = { ...opts.context, ...ctx };
757
- const oldDocs = await driverInstance.getAll({ where: { id_in: ids } }, ctx);
758
- const oldDocsById = _.keyBy(oldDocs, "id");
759
- const docArgsToSave = docs.map((doc) => {
760
- const oldDoc = oldDocsById[doc.id];
761
- doc = merge(oldDoc, doc);
762
- if (mustCleanNulls)
763
- cleanUndefinedAndNull(doc);
764
- doc = validators[key](doc);
765
- return { oldDoc, doc };
766
- });
767
- const docsToSave = docArgsToSave.map((x) => x.doc);
768
- await fillDenormFieldsBeforePut(computedContext, docArgsToSave, ctx);
769
- await handlePrecomputed(computedContext, docArgsToSave, ctx);
770
- const beforePutResults = await handleEffectsBeforePut(computedContext, docArgsToSave, ctx);
771
- await beforePut(docArgsToSave, ctx, computedContext);
772
- await driverInstance.putMany(docsToSave, ctx);
773
- await handleEffectsAfterPut(computedContext, docArgsToSave, beforePutResults, ctx);
774
- await afterPut(docArgsToSave, ctx, computedContext);
775
- return docsToSave;
776
- },
777
- clear: (ctx) => {
778
- ctx = { ...opts.context, ...ctx };
779
- return driverInstance.clear(ctx);
780
781
  }
781
- };
782
- }
783
- return db;
782
+ const docsToSave = docArgsToSave.map((x) => x.doc);
783
+ await driverInstance.putMany(docsToSave, ctx);
784
+ await handleEffectsAfterPut(computedContext, docArgsToSave, beforePutResults, ctx);
785
+ await afterPut(docArgsToSave, ctx, computedContext);
786
+ return { _logs, cursor, correctCount, incorrectCount, incorrectDocs };
787
+ },
788
+ clear: (ctx) => {
789
+ ctx = { ...options.context, ...ctx };
790
+ return driverInstance.clear(ctx);
791
+ }
792
+ };
784
793
  }
785
794
  async function beforeGet(args, ctx, computedContext) {
786
795
  for (const f of computedContext.options.features) {
@@ -857,6 +866,49 @@ function getExistingDriverInstance(entityName, drivers) {
857
866
  }
858
867
  return drivers[entityName];
859
868
  }
869
+
870
+ function generateMethods(schema, validators, options) {
871
+ const drivers = {};
872
+ const opts = { computed: {}, driver: memory(), features: [], ...options };
873
+ const db = {
874
+ _schema: schema,
875
+ uploadFile(args) {
876
+ if (!opts.fileUploadDriver)
877
+ throw new Error(`Missing configuration. Please specify "fileUploadDriver" argument in "createRads()".`);
878
+ if (!args.containerName)
879
+ args.containerName = "files";
880
+ return opts.fileUploadDriver.uploadFile(args);
881
+ }
882
+ };
883
+ const effects = {};
884
+ for (const key in schema) {
885
+ effects[key] = [];
886
+ }
887
+ if (!opts.skipComputed) {
888
+ verifyComputedPresense(schema, opts.computed);
889
+ }
890
+ verifyEventSourcingSetup(schema, effects);
891
+ verifyRelationsSetup(schema, effects);
892
+ for (const key in schema) {
893
+ if (!schema[key].decorators.entity)
894
+ continue;
895
+ const { handle } = schema[key];
896
+ if (!handle)
897
+ throw new Error(`Missing handle for entity ${key}`);
898
+ const computedContext = {
899
+ schema,
900
+ typeName: key,
901
+ validators,
902
+ options: opts,
903
+ drivers,
904
+ effects,
905
+ db
906
+ };
907
+ const driverInstance = getDriverInstance(schema, key, opts.driver, drivers);
908
+ db[handle] = getRadsDbMethods(key, computedContext, driverInstance);
909
+ }
910
+ return db;
911
+ }
860
912
  function getDriverInstance(schema, key, driver, drivers) {
861
913
  if (!drivers[key]) {
862
914
  drivers[key] = getDriverInstanceInner(schema, key, driver);
@@ -865,6 +917,7 @@ function getDriverInstance(schema, key, driver, drivers) {
865
917
  }
866
918
  function getDriverInstanceInner(schema, key, driverConstructor) {
867
919
  const driverInstance = driverConstructor(schema, key);
920
+ addDryRunSupport(driverInstance, key);
868
921
  async function getAll(args, ctx) {
869
922
  const result = [];
870
923
  let cursor = args.cursor;
@@ -907,6 +960,40 @@ function getDriverInstanceInner(schema, key, driverConstructor) {
907
960
  ...driverInstance
908
961
  };
909
962
  }
963
+ function addDryRunSupport(driverInstance, entity) {
964
+ const { clear, put, putMany } = driverInstance;
965
+ if (clear) {
966
+ driverInstance.clear = (ctx) => {
967
+ if (ctx?.dryRun) {
968
+ if (!ctx._logs)
969
+ ctx._logs = [];
970
+ ctx._logs.push({ method: "clear", entity });
971
+ return;
972
+ }
973
+ return clear(ctx);
974
+ };
975
+ }
976
+ if (put) {
977
+ driverInstance.put = (data, ctx) => {
978
+ if (ctx?.dryRun) {
979
+ if (!ctx._logs)
980
+ ctx._logs = [];
981
+ ctx._logs.push({ method: "put", entity, data });
982
+ return;
983
+ }
984
+ return put(data, ctx);
985
+ };
986
+ }
987
+ driverInstance.putMany = (data, ctx) => {
988
+ if (ctx?.dryRun) {
989
+ if (!ctx._logs)
990
+ ctx._logs = [];
991
+ ctx._logs.push({ method: "putMany", entity, data });
992
+ return;
993
+ }
994
+ return putMany(data, ctx);
995
+ };
996
+ }
910
997
 
911
998
  function entity(meta) {
912
999
  return function(classConstructor, _ctx) {
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ module.exports = void 0;
7
+ var _default = () => {
8
+ return {
9
+ name: "softDelete",
10
+ beforeGet(args, ctx, context) {
11
+ if (!context.schema[context.typeName]?.fields?.isDeleted) return;
12
+ if (args.where.isDeleted || args.where.isDeleted_in) return;
13
+ args.where._and = [...(args.where._and || []), {
14
+ isDeleted: false
15
+ }];
16
+ }
17
+ };
18
+ };
19
+ module.exports = _default;
@@ -0,0 +1,2 @@
1
+ declare const _default: () => RadsFeature;
2
+ export default _default;
@@ -0,0 +1,12 @@
1
+ export default () => {
2
+ return {
3
+ name: "softDelete",
4
+ beforeGet(args, ctx, context) {
5
+ if (!context.schema[context.typeName]?.fields?.isDeleted)
6
+ return;
7
+ if (args.where.isDeleted || args.where.isDeleted_in)
8
+ return;
9
+ args.where._and = [...args.where._and || [], { isDeleted: false }];
10
+ }
11
+ };
12
+ };
package/package.json CHANGED
@@ -4,7 +4,8 @@
4
4
  "dist",
5
5
  "drivers",
6
6
  "fileUploadDrivers",
7
- "integrations"
7
+ "integrations",
8
+ "features"
8
9
  ],
9
10
  "bin": {
10
11
  "rads-db": "integrations/cli.cjs"
@@ -32,9 +33,14 @@
32
33
  "types": "./integrations/*.d.ts",
33
34
  "import": "./integrations/*.mjs",
34
35
  "require": "./integrations/*.cjs"
36
+ },
37
+ "./features/*": {
38
+ "types": "./features/*.d.ts",
39
+ "import": "./features/*.mjs",
40
+ "require": "./features/*.cjs"
35
41
  }
36
42
  },
37
- "version": "0.1.72",
43
+ "version": "0.1.74",
38
44
  "description": "Say goodbye to boilerplate code and hello to efficient and elegant syntax.",
39
45
  "keywords": [],
40
46
  "author": "",