rads-db 3.0.24 → 3.0.25

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
@@ -543,12 +543,12 @@ async function fillDenormFieldsBeforePut(computedContext, docUpdates, ctx) {
543
543
  await Promise.all(promises);
544
544
  }
545
545
 
546
- function cleanUndefinedAndNull(obj) {
546
+ function cleanUndefinedAndNull(obj, isChange = false) {
547
547
  for (const key in obj) {
548
- if (obj[key] == null)
548
+ if (obj[key] == null && !isChange)
549
549
  delete obj[key];
550
550
  if (___default.isPlainObject(obj[key]))
551
- cleanUndefinedAndNull(obj[key]);
551
+ cleanUndefinedAndNull(obj[key], key === "change");
552
552
  }
553
553
  }
554
554
 
@@ -607,7 +607,6 @@ function mustUseDeepDiff(value, oldValue) {
607
607
 
608
608
  function getRadsDbMethods(key, computedContext, driverInstance) {
609
609
  const { schema, options, validators } = computedContext;
610
- const mustCleanNulls = driverInstance.driverName !== "restApi";
611
610
  const { precomputedFields } = schema[key];
612
611
  async function getMany(args, ctx) {
613
612
  args = { ...args, where: { ...args?.where } };
@@ -639,7 +638,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
639
638
  const docArgsToSave = docs.map((doc) => {
640
639
  const oldDoc = oldDocsById[doc.id];
641
640
  doc = merge(oldDoc, doc);
642
- if (mustCleanNulls)
641
+ if (!options.keepNulls)
643
642
  cleanUndefinedAndNull(doc);
644
643
  return { oldDoc, doc };
645
644
  });
@@ -717,7 +716,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
717
716
  const oldDoc = await driverInstance.get({ where: { id: doc.id } }, ctx);
718
717
  if (oldDoc)
719
718
  doc = merge(oldDoc, doc);
720
- if (mustCleanNulls)
719
+ if (!options.keepNulls)
721
720
  cleanUndefinedAndNull(doc);
722
721
  const docArgsToSave = [{ doc, oldDoc }];
723
722
  await handlePrecomputed(computedContext, docArgsToSave, ctx);
@@ -759,7 +758,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
759
758
  delete doc[p];
760
759
  }
761
760
  }
762
- if (mustCleanNulls)
761
+ if (!options.keepNulls)
763
762
  cleanUndefinedAndNull(doc);
764
763
  return { oldDoc, doc };
765
764
  });
@@ -1138,6 +1137,7 @@ function createRadsDbClient(args) {
1138
1137
  ...args,
1139
1138
  noComputed: true,
1140
1139
  noCustomDrivers: true,
1140
+ keepNulls: true,
1141
1141
  driver: restApi(args?.driver)
1142
1142
  };
1143
1143
  const s = radsDbArgs.schema || _radsDb.schema;
@@ -1145,6 +1145,7 @@ function createRadsDbClient(args) {
1145
1145
  return generateMethods(s, validators, radsDbArgs);
1146
1146
  }
1147
1147
 
1148
+ exports.cleanUndefinedAndNull = cleanUndefinedAndNull;
1148
1149
  exports.computed = computed;
1149
1150
  exports.createRadsDb = createRadsDb;
1150
1151
  exports.createRadsDbClient = createRadsDbClient;
package/dist/index.d.ts CHANGED
@@ -186,6 +186,7 @@ interface CreateRadsDbArgs {
186
186
  beforeGet?: (args: GetArgsAny, ctx: RadsRequestContext, context: ComputedContext) => MaybePromise<void>;
187
187
  context?: Record<string, any> & Omit<RadsRequestContext, 'dryRun' | 'silent'>;
188
188
  features?: RadsFeature[];
189
+ keepNulls?: boolean;
189
190
  }
190
191
  type CreateRadsDbArgsNormalized = RequiredFields<CreateRadsDbArgs, 'computed' | 'features'> & {
191
192
  driver: CreateRadsArgsDrivers;
@@ -393,6 +394,8 @@ declare function merge<T extends Record<string, any>>(target: T, source: any): T
393
394
 
394
395
  declare function diff(object: any, base: any, includeDenormFields?: boolean): any;
395
396
 
397
+ declare function cleanUndefinedAndNull(obj: Record<string, any>, isChange?: boolean): void;
398
+
396
399
  /**
397
400
  * Creates instance of rads db - object that provides access to all entities.
398
401
  * Intended to be used on the backend. On the client you may want to opt for "createRadsDbClient" instead.
@@ -405,4 +408,4 @@ declare function createRadsDb(args?: CreateRadsDbArgs): RadsDb;
405
408
  */
406
409
  declare function createRadsDbClient(args?: CreateRadsDbClientArgs): RadsDb;
407
410
 
408
- export { Change, ComputedContext, ComputedContextGlobal, ComputedDecoratorArgs, CreateRadsArgsDrivers, CreateRadsDbArgs, CreateRadsDbArgsNormalized, CreateRadsDbClientArgs, DeepPartial, Driver, DriverConstructor, EntityDecoratorArgs, EntityMethods, EnumDefinition, FieldDecoratorArgs, FieldDefinition, FileSystemNode, FileUploadArgs, FileUploadDriver, FileUploadResult, GenerateClientNormalizedOptions, GenerateClientOptions, GetAggArgs, GetAggArgsAgg, GetAggArgsAny, GetAggResponse, GetArgs, GetArgsAny, GetArgsInclude, GetManyArgs, GetManyArgsAny, GetManyResponse, GetResponse, GetResponseInclude, GetResponseIncludeSelect, GetResponseNoInclude, GetRestRoutesArgs, GetRestRoutesOptions, GetRestRoutesResponse, MinimalDriver, PutArgs, PutEffect, RadsFeature, RadsRequestContext, RadsUiSlotDefinition, RadsUiSlotName, RadsVitePluginOptions, Relation, RequiredFields, RestDriverOptions, RestFileUploadDriverOptions, Schema, SchemaValidators, TypeDefinition, UiDecoratorArgs, UiFieldDecoratorArgs, ValidateEntityDecoratorArgs, ValidateFieldDecoratorArgs, ValidateStringDecoratorArgs, VerifyManyArgs, VerifyManyArgsAny, VerifyManyResponse, computed, createRadsDb, createRadsDbClient, diff, entity, field, getDriverInstance, handlePrecomputed, merge, precomputed, ui, validate };
411
+ export { Change, ComputedContext, ComputedContextGlobal, ComputedDecoratorArgs, CreateRadsArgsDrivers, CreateRadsDbArgs, CreateRadsDbArgsNormalized, CreateRadsDbClientArgs, DeepPartial, Driver, DriverConstructor, EntityDecoratorArgs, EntityMethods, EnumDefinition, FieldDecoratorArgs, FieldDefinition, FileSystemNode, FileUploadArgs, FileUploadDriver, FileUploadResult, GenerateClientNormalizedOptions, GenerateClientOptions, GetAggArgs, GetAggArgsAgg, GetAggArgsAny, GetAggResponse, GetArgs, GetArgsAny, GetArgsInclude, GetManyArgs, GetManyArgsAny, GetManyResponse, GetResponse, GetResponseInclude, GetResponseIncludeSelect, GetResponseNoInclude, GetRestRoutesArgs, GetRestRoutesOptions, GetRestRoutesResponse, MinimalDriver, PutArgs, PutEffect, RadsFeature, RadsRequestContext, RadsUiSlotDefinition, RadsUiSlotName, RadsVitePluginOptions, Relation, RequiredFields, RestDriverOptions, RestFileUploadDriverOptions, Schema, SchemaValidators, TypeDefinition, UiDecoratorArgs, UiFieldDecoratorArgs, ValidateEntityDecoratorArgs, ValidateFieldDecoratorArgs, ValidateStringDecoratorArgs, VerifyManyArgs, VerifyManyArgsAny, VerifyManyResponse, cleanUndefinedAndNull, computed, createRadsDb, createRadsDbClient, diff, entity, field, getDriverInstance, handlePrecomputed, merge, precomputed, ui, validate };
package/dist/index.mjs CHANGED
@@ -535,12 +535,12 @@ async function fillDenormFieldsBeforePut(computedContext, docUpdates, ctx) {
535
535
  await Promise.all(promises);
536
536
  }
537
537
 
538
- function cleanUndefinedAndNull(obj) {
538
+ function cleanUndefinedAndNull(obj, isChange = false) {
539
539
  for (const key in obj) {
540
- if (obj[key] == null)
540
+ if (obj[key] == null && !isChange)
541
541
  delete obj[key];
542
542
  if (_.isPlainObject(obj[key]))
543
- cleanUndefinedAndNull(obj[key]);
543
+ cleanUndefinedAndNull(obj[key], key === "change");
544
544
  }
545
545
  }
546
546
 
@@ -599,7 +599,6 @@ function mustUseDeepDiff(value, oldValue) {
599
599
 
600
600
  function getRadsDbMethods(key, computedContext, driverInstance) {
601
601
  const { schema, options, validators } = computedContext;
602
- const mustCleanNulls = driverInstance.driverName !== "restApi";
603
602
  const { precomputedFields } = schema[key];
604
603
  async function getMany(args, ctx) {
605
604
  args = { ...args, where: { ...args?.where } };
@@ -631,7 +630,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
631
630
  const docArgsToSave = docs.map((doc) => {
632
631
  const oldDoc = oldDocsById[doc.id];
633
632
  doc = merge(oldDoc, doc);
634
- if (mustCleanNulls)
633
+ if (!options.keepNulls)
635
634
  cleanUndefinedAndNull(doc);
636
635
  return { oldDoc, doc };
637
636
  });
@@ -709,7 +708,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
709
708
  const oldDoc = await driverInstance.get({ where: { id: doc.id } }, ctx);
710
709
  if (oldDoc)
711
710
  doc = merge(oldDoc, doc);
712
- if (mustCleanNulls)
711
+ if (!options.keepNulls)
713
712
  cleanUndefinedAndNull(doc);
714
713
  const docArgsToSave = [{ doc, oldDoc }];
715
714
  await handlePrecomputed(computedContext, docArgsToSave, ctx);
@@ -751,7 +750,7 @@ function getRadsDbMethods(key, computedContext, driverInstance) {
751
750
  delete doc[p];
752
751
  }
753
752
  }
754
- if (mustCleanNulls)
753
+ if (!options.keepNulls)
755
754
  cleanUndefinedAndNull(doc);
756
755
  return { oldDoc, doc };
757
756
  });
@@ -1130,6 +1129,7 @@ function createRadsDbClient(args) {
1130
1129
  ...args,
1131
1130
  noComputed: true,
1132
1131
  noCustomDrivers: true,
1132
+ keepNulls: true,
1133
1133
  driver: restApi(args?.driver)
1134
1134
  };
1135
1135
  const s = radsDbArgs.schema || schema;
@@ -1137,4 +1137,4 @@ function createRadsDbClient(args) {
1137
1137
  return generateMethods(s, validators, radsDbArgs);
1138
1138
  }
1139
1139
 
1140
- export { computed, createRadsDb, createRadsDbClient, diff, entity, field, getDriverInstance, handlePrecomputed, merge, precomputed, ui, validate };
1140
+ export { cleanUndefinedAndNull, computed, createRadsDb, createRadsDbClient, diff, entity, field, getDriverInstance, handlePrecomputed, merge, precomputed, ui, validate };
@@ -62,13 +62,15 @@ function verifyEventSourcingSetup(schema, effects) {
62
62
  if (events.slice(1).some(ev => ev.type === "creation")) {
63
63
  throw new Error(`Only first event may have type = "creation"`);
64
64
  }
65
- let aggDoc = {
66
- id: aggId
67
- };
65
+ let aggDoc;
68
66
  for (const ev of events) {
69
- const newAggDoc = context.validators[entityName]((0, _radsDb.merge)(aggDoc, ev.change));
67
+ const newAggDoc = context.validators[entityName]((0, _radsDb.merge)(aggDoc || {
68
+ id: aggId
69
+ }, ev.change));
70
+ ev.beforeChange = aggDoc;
70
71
  ev.change = (0, _radsDb.diff)(newAggDoc, aggDoc);
71
72
  aggDoc = newAggDoc;
73
+ if (!context.options.keepNulls) (0, _radsDb.cleanUndefinedAndNull)(aggDoc);
72
74
  }
73
75
  result.push({
74
76
  aggDoc,
@@ -97,22 +99,55 @@ function validateEventSourcingSetup(schema, entityName, eventEntityName, aggrega
97
99
  if (!eventEntity) throw new Error(`You must create entity with name "${eventEntityName}" to use eventSourcing feature with "${entityName}"`);
98
100
  if (!eventEntity.decorators.entity) throw new Error(`"${eventEntityName}" must have "@entity()" decorator.`);
99
101
  const expectedFields = {
100
- id: "string",
101
- type: "enum",
102
- change: entityName,
103
- [aggregateRelationField]: entityName,
104
- date: "string"
102
+ id: {
103
+ type: "string"
104
+ },
105
+ change: {
106
+ type: entityName,
107
+ isChange: true
108
+ },
109
+ type: {
110
+ isEnum: true
111
+ },
112
+ beforeChange: {
113
+ type: entityName,
114
+ isChange: false,
115
+ isRelation: false,
116
+ isRequired: false
117
+ },
118
+ [aggregateRelationField]: {
119
+ type: entityName,
120
+ isRelation: true
121
+ },
122
+ date: {
123
+ type: "string"
124
+ }
105
125
  };
106
126
  for (const f in expectedFields) {
107
- const field = eventEntity.fields?.[f];
108
- if (!field) throw new Error(`"${eventEntityName}" must have field "${f}"`);
109
- if (expectedFields[f] === "enum") {
110
- if (!schema[field.type]?.enumValues?.creation) throw new Error(`Field "${eventEntityName}.${f}" must be enum that accepts value 'creation'`);
111
- } else {
112
- if (field.type !== expectedFields[f]) throw new Error(`"${eventEntityName}.${f}" must have type "${expectedFields[f]}"`);
113
- if (f === "change" && !field.isChange) {
114
- throw new Error(`"${eventEntityName}.${f}" must have type "Change<${expectedFields[f]}>"`);
115
- }
116
- }
127
+ validateSchemaField(f, eventEntityName, expectedFields, schema);
128
+ }
129
+ }
130
+ function validateSchemaField(fieldName, eventEntityName, expectedFields, schema) {
131
+ const eventEntity = schema[eventEntityName];
132
+ const field = eventEntity.fields?.[fieldName];
133
+ if (!field) throw new Error(`"${eventEntityName}" must have field "${fieldName}"`);
134
+ const expectedField = expectedFields[fieldName];
135
+ if (expectedField.type && expectedField.type !== field.type) {
136
+ throw new Error(`"${eventEntityName}.${fieldName}" must have type "${expectedField.type}"`);
137
+ }
138
+ if (expectedField.isEnum) {
139
+ if (!schema[field.type]?.enumValues?.creation) throw new Error(`Field "${eventEntityName}.${fieldName}" must be enum that accepts value 'creation'`);
140
+ }
141
+ if (expectedField.isChange != null && expectedField.isChange !== (field.isChange || false)) {
142
+ const expectedType = expectedField.isChange ? `Change<${expectedField.type}>` : expectedField.type;
143
+ throw new Error(`"${eventEntityName}.${fieldName}" must have type "${expectedType}"`);
144
+ }
145
+ if (expectedField.isRelation != null && expectedField.isRelation !== (field.isRelation || false)) {
146
+ const expectedType = expectedField.isRelation ? `Relation<${expectedField.type}>` : expectedField.type;
147
+ throw new Error(`"${eventEntityName}.${fieldName}" must have type "${expectedType}"`);
148
+ }
149
+ if (expectedField.isRequired != null && expectedField.isRequired !== (field.isRequired || false)) {
150
+ const expectedRequired = expectedField.isRequired ? `required` : "optional";
151
+ throw new Error(`"${eventEntityName}.${fieldName}" must be ${expectedRequired}`);
117
152
  }
118
153
  }
@@ -1,5 +1,5 @@
1
1
  import _ from "lodash";
2
- import { diff, handlePrecomputed, merge } from "rads-db";
2
+ import { cleanUndefinedAndNull, diff, handlePrecomputed, merge } from "rads-db";
3
3
  export default () => {
4
4
  return {
5
5
  name: "eventSourcing",
@@ -58,11 +58,14 @@ function verifyEventSourcingSetup(schema, effects) {
58
58
  if (events.slice(1).some((ev) => ev.type === "creation")) {
59
59
  throw new Error(`Only first event may have type = "creation"`);
60
60
  }
61
- let aggDoc = { id: aggId };
61
+ let aggDoc;
62
62
  for (const ev of events) {
63
- const newAggDoc = context.validators[entityName](merge(aggDoc, ev.change));
63
+ const newAggDoc = context.validators[entityName](merge(aggDoc || { id: aggId }, ev.change));
64
+ ev.beforeChange = aggDoc;
64
65
  ev.change = diff(newAggDoc, aggDoc);
65
66
  aggDoc = newAggDoc;
67
+ if (!context.options.keepNulls)
68
+ cleanUndefinedAndNull(aggDoc);
66
69
  }
67
70
  result.push({ aggDoc, events });
68
71
  }
@@ -89,25 +92,40 @@ function validateEventSourcingSetup(schema, entityName, eventEntityName, aggrega
89
92
  if (!eventEntity.decorators.entity)
90
93
  throw new Error(`"${eventEntityName}" must have "@entity()" decorator.`);
91
94
  const expectedFields = {
92
- id: "string",
93
- type: "enum",
94
- change: entityName,
95
- [aggregateRelationField]: entityName,
96
- date: "string"
95
+ id: { type: "string" },
96
+ change: { type: entityName, isChange: true },
97
+ type: { isEnum: true },
98
+ beforeChange: { type: entityName, isChange: false, isRelation: false, isRequired: false },
99
+ [aggregateRelationField]: { type: entityName, isRelation: true },
100
+ date: { type: "string" }
97
101
  };
98
102
  for (const f in expectedFields) {
99
- const field = eventEntity.fields?.[f];
100
- if (!field)
101
- throw new Error(`"${eventEntityName}" must have field "${f}"`);
102
- if (expectedFields[f] === "enum") {
103
- if (!schema[field.type]?.enumValues?.creation)
104
- throw new Error(`Field "${eventEntityName}.${f}" must be enum that accepts value 'creation'`);
105
- } else {
106
- if (field.type !== expectedFields[f])
107
- throw new Error(`"${eventEntityName}.${f}" must have type "${expectedFields[f]}"`);
108
- if (f === "change" && !field.isChange) {
109
- throw new Error(`"${eventEntityName}.${f}" must have type "Change<${expectedFields[f]}>"`);
110
- }
111
- }
103
+ validateSchemaField(f, eventEntityName, expectedFields, schema);
104
+ }
105
+ }
106
+ function validateSchemaField(fieldName, eventEntityName, expectedFields, schema) {
107
+ const eventEntity = schema[eventEntityName];
108
+ const field = eventEntity.fields?.[fieldName];
109
+ if (!field)
110
+ throw new Error(`"${eventEntityName}" must have field "${fieldName}"`);
111
+ const expectedField = expectedFields[fieldName];
112
+ if (expectedField.type && expectedField.type !== field.type) {
113
+ throw new Error(`"${eventEntityName}.${fieldName}" must have type "${expectedField.type}"`);
114
+ }
115
+ if (expectedField.isEnum) {
116
+ if (!schema[field.type]?.enumValues?.creation)
117
+ throw new Error(`Field "${eventEntityName}.${fieldName}" must be enum that accepts value 'creation'`);
118
+ }
119
+ if (expectedField.isChange != null && expectedField.isChange !== (field.isChange || false)) {
120
+ const expectedType = expectedField.isChange ? `Change<${expectedField.type}>` : expectedField.type;
121
+ throw new Error(`"${eventEntityName}.${fieldName}" must have type "${expectedType}"`);
122
+ }
123
+ if (expectedField.isRelation != null && expectedField.isRelation !== (field.isRelation || false)) {
124
+ const expectedType = expectedField.isRelation ? `Relation<${expectedField.type}>` : expectedField.type;
125
+ throw new Error(`"${eventEntityName}.${fieldName}" must have type "${expectedType}"`);
126
+ }
127
+ if (expectedField.isRequired != null && expectedField.isRequired !== (field.isRequired || false)) {
128
+ const expectedRequired = expectedField.isRequired ? `required` : "optional";
129
+ throw new Error(`"${eventEntityName}.${fieldName}" must be ${expectedRequired}`);
112
130
  }
113
131
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rads-db",
3
- "version": "3.0.24",
3
+ "version": "3.0.25",
4
4
  "files": [
5
5
  "dist",
6
6
  "drivers",
@@ -107,7 +107,7 @@
107
107
  "test": "vitest --run && vitest typecheck --run",
108
108
  "link-rads-db": "symlink-dir ./test/_rads-db ./node_modules/_rads-db",
109
109
  "generate-test-schema": "rads-db",
110
- "dev": "pnpm install && pnpm build && pnpm generate-test-schema && pnpm link-rads-db && vitest --ui --test-timeout 300000",
110
+ "dev": "pnpm install && pnpm link-rads-db && pnpm build && pnpm generate-test-schema && vitest --ui --test-timeout 300000",
111
111
  "lint": "cross-env NODE_ENV=production eslint src/**/*.* test/**/*.*",
112
112
  "dev-typecheck": "pnpm install && pnpm build && pnpm generate-test-schema && pnpm link-rads-db && vitest typecheck --ui",
113
113
  "build": "unbuild"