rads-db 3.1.5 → 3.1.7

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/config.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { g as RadsConfig, T as TypeDefinition } from './types-7e792d1f.js';
1
+ import { g as RadsConfig, T as TypeDefinition } from './types-1bda441b.js';
2
2
  import '_rads-db';
3
3
 
4
4
  declare function defineRadsConfig(config: RadsConfig): RadsConfig;
package/dist/index.cjs CHANGED
@@ -156,6 +156,20 @@ const operatorFns = {
156
156
  return true;
157
157
  },
158
158
  arrayContains: (x, w) => x?.includes(w),
159
+ isEmpty: (x, w) => {
160
+ if (!x) {
161
+ if (w === true)
162
+ return true;
163
+ if (w === false)
164
+ return false;
165
+ return true;
166
+ }
167
+ if (w === true)
168
+ return x?.length === 0;
169
+ if (w === false)
170
+ return x?.length > 0;
171
+ return true;
172
+ },
159
173
  isNull: (x, w) => {
160
174
  if (w === true)
161
175
  return x == null;
@@ -276,7 +290,18 @@ function getFilter(where, namePrefix = "") {
276
290
  }
277
291
  if (andClauses.length === 0)
278
292
  return null;
279
- return (x) => andClauses.every((ac) => ac(x));
293
+ return (x) => resolveAndClauses(andClauses, x);
294
+ }
295
+ function resolveAndClauses(andClauses, x) {
296
+ let result = true;
297
+ for (const ac of andClauses) {
298
+ const clauseResult = ac(x);
299
+ if (clauseResult === false)
300
+ return false;
301
+ if (clauseResult == null)
302
+ result = void 0;
303
+ }
304
+ return result;
280
305
  }
281
306
  function getWhereNameOperatorPair(whereKey) {
282
307
  if (whereKey.startsWith("_type")) {
@@ -290,14 +315,14 @@ function getFilterInner(operator, whereVal, name, namePrefix) {
290
315
  return getFilter(whereVal, name);
291
316
  }
292
317
  if (operator === "some") {
293
- const f = getFilter(whereVal, namePrefix);
318
+ const f = getFilter(whereVal);
294
319
  return f && ((x) => {
295
320
  const val = ___default.get(x, name);
296
- return val === void 0 || val?.some(f);
321
+ return val?.some(f);
297
322
  });
298
323
  }
299
324
  if (operator === "none") {
300
- const f = getFilter(whereVal, namePrefix);
325
+ const f = getFilter(whereVal);
301
326
  return f && ((x) => !___default.get(x, name)?.some(f));
302
327
  }
303
328
  if (operator === "and") {
@@ -310,7 +335,7 @@ function getFilterInner(operator, whereVal, name, namePrefix) {
310
335
  }
311
336
  if (operator === "not") {
312
337
  const f = getFilter(whereVal, namePrefix);
313
- return f && ((x) => !f(x));
338
+ return f && ((x) => f(x) === false);
314
339
  }
315
340
  if (operator === "or") {
316
341
  if (!___default.isArray(whereVal))
@@ -387,8 +412,8 @@ async function handlePrecomputed(context, docs, ctx) {
387
412
  if (!handler)
388
413
  throw new Error(`Computed handler for ${typeName}.${fieldName} was not found`);
389
414
  }
390
- const precomputedResults = docs.map(async ({ doc, oldDoc, events }) => {
391
- doc[fieldName] = await handler({ fieldName, doc, oldDoc, events, db, ctx });
415
+ const precomputedResults = docs.map(async ({ doc, oldDoc, events, updatedEvents }) => {
416
+ doc[fieldName] = await handler({ fieldName, doc, oldDoc, events, updatedEvents, db, ctx });
392
417
  });
393
418
  await Promise.all(precomputedResults);
394
419
  }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { E as EntityDecoratorArgs, U as UiDecoratorArgs, a as UiFieldDecoratorArgs, V as ValidateEntityDecoratorArgs, b as ValidateFieldDecoratorArgs, F as FieldDecoratorArgs, C as ComputedDecoratorArgs, S as Schema, D as DriverConstructor, c as Driver, d as ComputedContext, R as RadsRequestContext, e as CreateRadsDbArgs, f as CreateRadsDbClientArgs } from './types-7e792d1f.js';
2
- export { N as Change, x as ComputedContextGlobal, k as CreateRadsArgsDrivers, m as CreateRadsDbArgsNormalized, aj as DeepKeys, ae as DeepPartial, ac as DeepPartialWithNulls, ad as DeepPartialWithNullsItem, ak as EntityMethods, w as EnumDefinition, v as FieldDefinition, I as FileSystemNode, u as FileUploadArgs, q as FileUploadDriver, p as FileUploadResult, B as GenerateClientNormalizedOptions, A as GenerateClientOptions, a8 as Get, _ as GetAggArgs, $ as GetAggArgsAgg, a2 as GetAggArgsAny, a5 as GetAggResponse, Z as GetArgs, a1 as GetArgsAny, a4 as GetArgsInclude, O as GetManyArgs, a0 as GetManyArgsAny, a6 as GetManyResponse, a7 as GetResponse, a9 as GetResponseInclude, aa as GetResponseIncludeSelect, ab as GetResponseNoInclude, s as GetRestRoutesArgs, G as GetRestRoutesOptions, t as GetRestRoutesResponse, ag as InverseRelation, M as MinimalDriver, ai as Put, ah as PutArgs, P as PutEffect, g as RadsConfig, h as RadsConfigDataSource, r as RadsDbInstance, L as RadsFeature, y as RadsHookDoc, K as RadsUiSlotDefinition, J as RadsUiSlotName, H as RadsVitePluginOptions, af as Relation, i as RequiredFields, l as RestDriverOptions, z as RestFileUploadDriverOptions, n as SchemaLoadResult, o as SchemaValidators, T as TypeDefinition, j as ValidateStringDecoratorArgs, Q as VerifyManyArgs, a3 as VerifyManyArgsAny, X as VerifyManyResponse, Y as Where, W as WhereJsonContains } from './types-7e792d1f.js';
1
+ import { E as EntityDecoratorArgs, U as UiDecoratorArgs, a as UiFieldDecoratorArgs, V as ValidateEntityDecoratorArgs, b as ValidateFieldDecoratorArgs, F as FieldDecoratorArgs, C as ComputedDecoratorArgs, S as Schema, D as DriverConstructor, c as Driver, d as ComputedContext, R as RadsRequestContext, e as CreateRadsDbArgs, f as CreateRadsDbClientArgs } from './types-1bda441b.js';
2
+ export { N as Change, x as ComputedContextGlobal, k as CreateRadsArgsDrivers, m as CreateRadsDbArgsNormalized, aj as DeepKeys, ae as DeepPartial, ac as DeepPartialWithNulls, ad as DeepPartialWithNullsItem, ak as EntityMethods, w as EnumDefinition, v as FieldDefinition, I as FileSystemNode, u as FileUploadArgs, q as FileUploadDriver, p as FileUploadResult, B as GenerateClientNormalizedOptions, A as GenerateClientOptions, a8 as Get, _ as GetAggArgs, $ as GetAggArgsAgg, a2 as GetAggArgsAny, a5 as GetAggResponse, Z as GetArgs, a1 as GetArgsAny, a4 as GetArgsInclude, O as GetManyArgs, a0 as GetManyArgsAny, a6 as GetManyResponse, a7 as GetResponse, a9 as GetResponseInclude, aa as GetResponseIncludeSelect, ab as GetResponseNoInclude, s as GetRestRoutesArgs, G as GetRestRoutesOptions, t as GetRestRoutesResponse, ag as InverseRelation, M as MinimalDriver, ai as Put, ah as PutArgs, P as PutEffect, g as RadsConfig, h as RadsConfigDataSource, r as RadsDbInstance, L as RadsFeature, y as RadsHookDoc, K as RadsUiSlotDefinition, J as RadsUiSlotName, H as RadsVitePluginOptions, af as Relation, i as RequiredFields, l as RestDriverOptions, z as RestFileUploadDriverOptions, n as SchemaLoadResult, o as SchemaValidators, T as TypeDefinition, j as ValidateStringDecoratorArgs, Q as VerifyManyArgs, a3 as VerifyManyArgsAny, X as VerifyManyResponse, Y as Where, W as WhereJsonContains } from './types-1bda441b.js';
3
3
  import { RadsDb } from '_rads-db';
4
4
  export { RadsDb } from '_rads-db';
5
5
 
package/dist/index.mjs CHANGED
@@ -149,6 +149,20 @@ const operatorFns = {
149
149
  return true;
150
150
  },
151
151
  arrayContains: (x, w) => x?.includes(w),
152
+ isEmpty: (x, w) => {
153
+ if (!x) {
154
+ if (w === true)
155
+ return true;
156
+ if (w === false)
157
+ return false;
158
+ return true;
159
+ }
160
+ if (w === true)
161
+ return x?.length === 0;
162
+ if (w === false)
163
+ return x?.length > 0;
164
+ return true;
165
+ },
152
166
  isNull: (x, w) => {
153
167
  if (w === true)
154
168
  return x == null;
@@ -269,7 +283,18 @@ function getFilter(where, namePrefix = "") {
269
283
  }
270
284
  if (andClauses.length === 0)
271
285
  return null;
272
- return (x) => andClauses.every((ac) => ac(x));
286
+ return (x) => resolveAndClauses(andClauses, x);
287
+ }
288
+ function resolveAndClauses(andClauses, x) {
289
+ let result = true;
290
+ for (const ac of andClauses) {
291
+ const clauseResult = ac(x);
292
+ if (clauseResult === false)
293
+ return false;
294
+ if (clauseResult == null)
295
+ result = void 0;
296
+ }
297
+ return result;
273
298
  }
274
299
  function getWhereNameOperatorPair(whereKey) {
275
300
  if (whereKey.startsWith("_type")) {
@@ -283,14 +308,14 @@ function getFilterInner(operator, whereVal, name, namePrefix) {
283
308
  return getFilter(whereVal, name);
284
309
  }
285
310
  if (operator === "some") {
286
- const f = getFilter(whereVal, namePrefix);
311
+ const f = getFilter(whereVal);
287
312
  return f && ((x) => {
288
313
  const val = _.get(x, name);
289
- return val === void 0 || val?.some(f);
314
+ return val?.some(f);
290
315
  });
291
316
  }
292
317
  if (operator === "none") {
293
- const f = getFilter(whereVal, namePrefix);
318
+ const f = getFilter(whereVal);
294
319
  return f && ((x) => !_.get(x, name)?.some(f));
295
320
  }
296
321
  if (operator === "and") {
@@ -303,7 +328,7 @@ function getFilterInner(operator, whereVal, name, namePrefix) {
303
328
  }
304
329
  if (operator === "not") {
305
330
  const f = getFilter(whereVal, namePrefix);
306
- return f && ((x) => !f(x));
331
+ return f && ((x) => f(x) === false);
307
332
  }
308
333
  if (operator === "or") {
309
334
  if (!_.isArray(whereVal))
@@ -380,8 +405,8 @@ async function handlePrecomputed(context, docs, ctx) {
380
405
  if (!handler)
381
406
  throw new Error(`Computed handler for ${typeName}.${fieldName} was not found`);
382
407
  }
383
- const precomputedResults = docs.map(async ({ doc, oldDoc, events }) => {
384
- doc[fieldName] = await handler({ fieldName, doc, oldDoc, events, db, ctx });
408
+ const precomputedResults = docs.map(async ({ doc, oldDoc, events, updatedEvents }) => {
409
+ doc[fieldName] = await handler({ fieldName, doc, oldDoc, events, updatedEvents, db, ctx });
385
410
  });
386
411
  await Promise.all(precomputedResults);
387
412
  }
@@ -355,7 +355,8 @@ interface RadsHookDoc {
355
355
  /** Previous version of document - i.e. one that is currently in the database (before saving) */
356
356
  oldDoc: any;
357
357
  /** If current entity is event sourcing aggregate, you can access all events in the chronological order */
358
- events?: any;
358
+ events?: any[];
359
+ updatedEvents?: any[];
359
360
  }
360
361
  interface PutEffect {
361
362
  beforePut?: (computedContext: ComputedContext, docs: RadsHookDoc[], ctx: RadsRequestContext) => MaybePromise<any>;
@@ -101,11 +101,16 @@ var _default = options => (schema, entity) => {
101
101
  nodes,
102
102
  cursor
103
103
  } = await getMany(args, ctx);
104
- for (const r of nodes) {
105
- const resp = await client.item(r.id, r._partition).delete();
104
+ for (const chunk of _lodash.default.chunk(nodes, 100)) {
105
+ const chunkToSend = chunk.map(x => ({
106
+ operationType: "Delete",
107
+ partitionKey: x._partition,
108
+ id: x.id
109
+ }));
110
+ const responses = await bulkSendWithRetry(client, chunkToSend);
106
111
  ctx?.log?.({
107
- charge: resp.requestCharge,
108
- request: `delete#${r._partition}|${r.id}`
112
+ charge: _lodash.default.sumBy(responses, r => r.requestCharge),
113
+ request: `delete#${chunk[0]._partition}[${chunk.length}]`
109
114
  });
110
115
  }
111
116
  return {
@@ -125,32 +130,11 @@ var _default = options => (schema, entity) => {
125
130
  });
126
131
  }
127
132
  for (const chunk of _lodash.default.chunk(itemsToPut, 100)) {
128
- let chunkToSend = chunk.map(x => ({
133
+ const chunkToSend = chunk.map(x => ({
129
134
  operationType: "Upsert",
130
135
  resourceBody: x
131
136
  }));
132
- const responses = [];
133
- for (let i = 0; i < 20; i++) {
134
- const response = await client.items.bulk(chunkToSend);
135
- const newChunkToSend = [];
136
- for (let i2 = 0; i2 < chunkToSend.length; i2++) {
137
- if (response[i2].statusCode !== 429) {
138
- responses.push(response[i2]);
139
- } else {
140
- newChunkToSend.push(chunkToSend[i2]);
141
- }
142
- }
143
- chunkToSend = newChunkToSend;
144
- if (chunkToSend.length === 0) break;
145
- const delay = 50 + i * 100;
146
- console.warn(`Db overloaded. Retrying after ${delay}ms...`);
147
- await new Promise(resolve => setTimeout(resolve, delay));
148
- }
149
- if (chunkToSend.length !== 0) {
150
- throw new _cosmos.RestError("Db oveloaded. Too many retries. Consider increasing database RU setting.", {
151
- code: "429"
152
- });
153
- }
137
+ const responses = await bulkSendWithRetry(client, chunkToSend);
154
138
  ctx?.log?.({
155
139
  charge: _lodash.default.sumBy(responses, r => r.requestCharge),
156
140
  request: `put#${chunk[0]._partition}[${chunk.length}]`
@@ -192,11 +176,7 @@ const operatorHandlers = {
192
176
  paramName,
193
177
  whereVal
194
178
  } = whereArgs;
195
- const newCtx = {
196
- ...ctx,
197
- entity: ctx.schema[ctx.entity].fields[ctx.field].type
198
- };
199
- const subClause = getCosmosQueryWhere(newCtx, parameters, whereVal, `${name}.`, `${paramNamePrefix}${paramName}_`);
179
+ const subClause = getCosmosQueryWhere(ctx, parameters, whereVal, `${name}.`, `${paramNamePrefix}${paramName}_`);
200
180
  if (subClause) {
201
181
  if (simpleSubclauseRegex.test(subClause)) {
202
182
  const parts = subClause.split(" ");
@@ -216,11 +196,7 @@ const operatorHandlers = {
216
196
  paramName,
217
197
  whereVal
218
198
  } = whereArgs;
219
- const newCtx = {
220
- ...ctx,
221
- entity: ctx.schema[ctx.entity].fields[ctx.field].type
222
- };
223
- const subClause = getCosmosQueryWhere(newCtx, parameters, whereVal, `${name}.`, `${paramNamePrefix}${paramName}_`);
199
+ const subClause = getCosmosQueryWhere(ctx, parameters, whereVal, `${name}.`, `${paramNamePrefix}${paramName}_`);
224
200
  if (subClause) return `not exists (select ${name} from ${name} in ${namePrefix}${name} where ${subClause})`;
225
201
  return `array_length(${namePrefix}${name}) = 0`;
226
202
  },
@@ -383,6 +359,16 @@ const operatorHandlers = {
383
359
  parameters[pn] = whereVal;
384
360
  return `array_contains(${namePrefix}${name}, @${pn})`;
385
361
  },
362
+ isEmpty: (ctx, parameters, whereArgs) => {
363
+ const {
364
+ name,
365
+ namePrefix,
366
+ whereVal
367
+ } = whereArgs;
368
+ const n = `${namePrefix}${name}`;
369
+ if (whereVal) return `(not (is_defined(${n})) or ${n} = null or array_length(${n}) = 0)`;
370
+ return `(is_defined(${n}) and ${n} != null and array_length(${n}) > 0)`;
371
+ },
386
372
  jsonContains: (ctx, parameters, whereArgs) => {
387
373
  const {
388
374
  name,
@@ -578,4 +564,29 @@ async function createDatabaseIfNotExists(options, client) {
578
564
  } catch (e) {
579
565
  console.error("Error initializing CosmosDb: ", e);
580
566
  }
567
+ }
568
+ async function bulkSendWithRetry(client, chunkToSend) {
569
+ const responses = [];
570
+ for (let i = 0; i < 20; i++) {
571
+ const response = await client.items.bulk(chunkToSend);
572
+ const newChunkToSend = [];
573
+ for (let i2 = 0; i2 < chunkToSend.length; i2++) {
574
+ if (response[i2].statusCode !== 429) {
575
+ responses.push(response[i2]);
576
+ } else {
577
+ newChunkToSend.push(chunkToSend[i2]);
578
+ }
579
+ }
580
+ chunkToSend = newChunkToSend;
581
+ if (chunkToSend.length === 0) break;
582
+ const delay = 50 + i * 100;
583
+ console.warn(`Db overloaded. Retrying after ${delay}ms...`);
584
+ await new Promise(resolve => setTimeout(resolve, delay));
585
+ }
586
+ if (chunkToSend.length !== 0) {
587
+ throw new _cosmos.RestError("Db oveloaded. Too many retries. Consider increasing database RU setting.", {
588
+ code: "429"
589
+ });
590
+ }
591
+ return responses;
581
592
  }
@@ -66,9 +66,17 @@ export default (options) => (schema, entity) => {
66
66
  getMany,
67
67
  async deleteMany(args, ctx) {
68
68
  const { nodes, cursor } = await getMany(args, ctx);
69
- for (const r of nodes) {
70
- const resp = await client.item(r.id, r._partition).delete();
71
- ctx?.log?.({ charge: resp.requestCharge, request: `delete#${r._partition}|${r.id}` });
69
+ for (const chunk of _.chunk(nodes, 100)) {
70
+ const chunkToSend = chunk.map((x) => ({
71
+ operationType: "Delete",
72
+ partitionKey: x._partition,
73
+ id: x.id
74
+ }));
75
+ const responses = await bulkSendWithRetry(client, chunkToSend);
76
+ ctx?.log?.({
77
+ charge: _.sumBy(responses, (r) => r.requestCharge),
78
+ request: `delete#${chunk[0]._partition}[${chunk.length}]`
79
+ });
72
80
  }
73
81
  return { nodes, cursor };
74
82
  },
@@ -81,33 +89,11 @@ export default (options) => (schema, entity) => {
81
89
  itemsToPut.push({ _partition: entity, id, ...item });
82
90
  }
83
91
  for (const chunk of _.chunk(itemsToPut, 100)) {
84
- let chunkToSend = chunk.map((x) => ({
92
+ const chunkToSend = chunk.map((x) => ({
85
93
  operationType: "Upsert",
86
94
  resourceBody: x
87
95
  }));
88
- const responses = [];
89
- for (let i = 0; i < 20; i++) {
90
- const response = await client.items.bulk(chunkToSend);
91
- const newChunkToSend = [];
92
- for (let i2 = 0; i2 < chunkToSend.length; i2++) {
93
- if (response[i2].statusCode !== 429) {
94
- responses.push(response[i2]);
95
- } else {
96
- newChunkToSend.push(chunkToSend[i2]);
97
- }
98
- }
99
- chunkToSend = newChunkToSend;
100
- if (chunkToSend.length === 0)
101
- break;
102
- const delay = 50 + i * 100;
103
- console.warn(`Db overloaded. Retrying after ${delay}ms...`);
104
- await new Promise((resolve) => setTimeout(resolve, delay));
105
- }
106
- if (chunkToSend.length !== 0) {
107
- throw new RestError("Db oveloaded. Too many retries. Consider increasing database RU setting.", {
108
- code: "429"
109
- });
110
- }
96
+ const responses = await bulkSendWithRetry(client, chunkToSend);
111
97
  ctx?.log?.({
112
98
  charge: _.sumBy(responses, (r) => r.requestCharge),
113
99
  request: `put#${chunk[0]._partition}[${chunk.length}]`
@@ -139,8 +125,7 @@ function getCosmosQuery(schema, entity, args) {
139
125
  const operatorHandlers = {
140
126
  some: (ctx, parameters, whereArgs) => {
141
127
  const { name, namePrefix, paramNamePrefix, paramName, whereVal } = whereArgs;
142
- const newCtx = { ...ctx, entity: ctx.schema[ctx.entity].fields[ctx.field].type };
143
- const subClause = getCosmosQueryWhere(newCtx, parameters, whereVal, `${name}.`, `${paramNamePrefix}${paramName}_`);
128
+ const subClause = getCosmosQueryWhere(ctx, parameters, whereVal, `${name}.`, `${paramNamePrefix}${paramName}_`);
144
129
  if (subClause) {
145
130
  if (simpleSubclauseRegex.test(subClause)) {
146
131
  const parts = subClause.split(" ");
@@ -154,8 +139,7 @@ const operatorHandlers = {
154
139
  },
155
140
  none: (ctx, parameters, whereArgs) => {
156
141
  const { name, namePrefix, paramNamePrefix, paramName, whereVal } = whereArgs;
157
- const newCtx = { ...ctx, entity: ctx.schema[ctx.entity].fields[ctx.field].type };
158
- const subClause = getCosmosQueryWhere(newCtx, parameters, whereVal, `${name}.`, `${paramNamePrefix}${paramName}_`);
142
+ const subClause = getCosmosQueryWhere(ctx, parameters, whereVal, `${name}.`, `${paramNamePrefix}${paramName}_`);
159
143
  if (subClause)
160
144
  return `not exists (select ${name} from ${name} in ${namePrefix}${name} where ${subClause})`;
161
145
  return `array_length(${namePrefix}${name}) = 0`;
@@ -254,6 +238,13 @@ const operatorHandlers = {
254
238
  parameters[pn] = whereVal;
255
239
  return `array_contains(${namePrefix}${name}, @${pn})`;
256
240
  },
241
+ isEmpty: (ctx, parameters, whereArgs) => {
242
+ const { name, namePrefix, whereVal } = whereArgs;
243
+ const n = `${namePrefix}${name}`;
244
+ if (whereVal)
245
+ return `(not (is_defined(${n})) or ${n} = null or array_length(${n}) = 0)`;
246
+ return `(is_defined(${n}) and ${n} != null and array_length(${n}) > 0)`;
247
+ },
257
248
  jsonContains: (ctx, parameters, whereArgs) => {
258
249
  const { name, namePrefix, paramName, paramNamePrefix, whereVal } = whereArgs;
259
250
  const { path, isNull, value } = whereVal;
@@ -385,3 +376,29 @@ async function createDatabaseIfNotExists(options, client) {
385
376
  console.error("Error initializing CosmosDb: ", e);
386
377
  }
387
378
  }
379
+ async function bulkSendWithRetry(client, chunkToSend) {
380
+ const responses = [];
381
+ for (let i = 0; i < 20; i++) {
382
+ const response = await client.items.bulk(chunkToSend);
383
+ const newChunkToSend = [];
384
+ for (let i2 = 0; i2 < chunkToSend.length; i2++) {
385
+ if (response[i2].statusCode !== 429) {
386
+ responses.push(response[i2]);
387
+ } else {
388
+ newChunkToSend.push(chunkToSend[i2]);
389
+ }
390
+ }
391
+ chunkToSend = newChunkToSend;
392
+ if (chunkToSend.length === 0)
393
+ break;
394
+ const delay = 50 + i * 100;
395
+ console.warn(`Db overloaded. Retrying after ${delay}ms...`);
396
+ await new Promise((resolve) => setTimeout(resolve, delay));
397
+ }
398
+ if (chunkToSend.length !== 0) {
399
+ throw new RestError("Db oveloaded. Too many retries. Consider increasing database RU setting.", {
400
+ code: "429"
401
+ });
402
+ }
403
+ return responses;
404
+ }
@@ -31,36 +31,50 @@ var _default = options => {
31
31
  const handle = schema[entity]?.handle;
32
32
  if (!handle) throw new Error(`Entity ${entity} not found`);
33
33
  dbWrapper.entities[handle] = true;
34
- return {
35
- driverName: "indexedDb",
36
- async getMany(args, ctx) {
37
- await initDbIfNeeded(dbName);
38
- const normalizedArgs = normalizeArgs(args);
39
- const dexieResult = applyArgsToDexieTable(dbWrapper.db.table(handle), normalizedArgs);
40
- let resultCursor;
41
- const newCursor = normalizedArgs.cursor + dexieResult.maxItemCount;
42
- let result = await dexieResult.collection.toArray();
43
- if (dexieResult.orderByProp) {
44
- result = _lodash.default.orderBy(result, [dexieResult.orderByProp], [dexieResult.orderByDirection.toLowerCase()]);
45
- if (dexieResult.maxItemCount) {
46
- if (result.length > newCursor) {
47
- resultCursor = newCursor;
48
- }
49
- result = result.slice(normalizedArgs.cursor, newCursor);
34
+ async function getMany(args, ctx) {
35
+ await initDbIfNeeded(dbName);
36
+ const normalizedArgs = normalizeArgs(args);
37
+ const dexieResult = applyArgsToDexieTable(dbWrapper.db.table(handle), normalizedArgs);
38
+ let resultCursor;
39
+ const newCursor = normalizedArgs.cursor + dexieResult.maxItemCount;
40
+ let result = await dexieResult.collection.toArray();
41
+ if (dexieResult.orderByProp) {
42
+ result = _lodash.default.orderBy(result, [dexieResult.orderByProp], [dexieResult.orderByDirection.toLowerCase()]);
43
+ if (dexieResult.maxItemCount) {
44
+ if (result.length > newCursor) {
45
+ resultCursor = newCursor;
50
46
  }
51
- return {
52
- nodes: result,
53
- cursor: resultCursor ? _lodash.default.toString(resultCursor) : null
54
- };
55
- }
56
- if (dexieResult.maxItemCount && result.length >= dexieResult.maxItemCount) {
57
- resultCursor = newCursor;
47
+ result = result.slice(normalizedArgs.cursor, newCursor);
58
48
  }
59
49
  return {
60
50
  nodes: result,
61
51
  cursor: resultCursor ? _lodash.default.toString(resultCursor) : null
62
52
  };
53
+ }
54
+ if (dexieResult.maxItemCount && result.length >= dexieResult.maxItemCount) {
55
+ resultCursor = newCursor;
56
+ }
57
+ return {
58
+ nodes: result,
59
+ cursor: resultCursor ? _lodash.default.toString(resultCursor) : null
60
+ };
61
+ }
62
+ return {
63
+ driverName: "indexedDb",
64
+ async deleteMany(args, ctx) {
65
+ await initDbIfNeeded(dbName);
66
+ const {
67
+ nodes,
68
+ cursor
69
+ } = await getMany(args, ctx);
70
+ const table = dbWrapper.db.table(handle);
71
+ await table.bulkDelete(nodes.map(x => x.id));
72
+ return {
73
+ nodes,
74
+ cursor
75
+ };
63
76
  },
77
+ getMany,
64
78
  async putMany(items, ctx) {
65
79
  await initDbIfNeeded(dbName);
66
80
  const table = dbWrapper.db.table(handle);
@@ -98,7 +112,7 @@ function applyArgsToDexieTable(table, args) {
98
112
  }
99
113
  const f = (0, _memory.getFilter)(where);
100
114
  if (f) {
101
- collection = collection.filter(f);
115
+ collection = collection.filter(x => f(x) || false);
102
116
  }
103
117
  if (maxItemCount && !orderByProp) {
104
118
  if (args.cursor) collection = collection.offset(_lodash.default.toNumber(args.cursor));
@@ -25,30 +25,38 @@ export default (options) => {
25
25
  if (!handle)
26
26
  throw new Error(`Entity ${entity} not found`);
27
27
  dbWrapper.entities[handle] = true;
28
- return {
29
- driverName: "indexedDb",
30
- async getMany(args, ctx) {
31
- await initDbIfNeeded(dbName);
32
- const normalizedArgs = normalizeArgs(args);
33
- const dexieResult = applyArgsToDexieTable(dbWrapper.db.table(handle), normalizedArgs);
34
- let resultCursor;
35
- const newCursor = normalizedArgs.cursor + dexieResult.maxItemCount;
36
- let result = await dexieResult.collection.toArray();
37
- if (dexieResult.orderByProp) {
38
- result = _.orderBy(result, [dexieResult.orderByProp], [dexieResult.orderByDirection.toLowerCase()]);
39
- if (dexieResult.maxItemCount) {
40
- if (result.length > newCursor) {
41
- resultCursor = newCursor;
42
- }
43
- result = result.slice(normalizedArgs.cursor, newCursor);
28
+ async function getMany(args, ctx) {
29
+ await initDbIfNeeded(dbName);
30
+ const normalizedArgs = normalizeArgs(args);
31
+ const dexieResult = applyArgsToDexieTable(dbWrapper.db.table(handle), normalizedArgs);
32
+ let resultCursor;
33
+ const newCursor = normalizedArgs.cursor + dexieResult.maxItemCount;
34
+ let result = await dexieResult.collection.toArray();
35
+ if (dexieResult.orderByProp) {
36
+ result = _.orderBy(result, [dexieResult.orderByProp], [dexieResult.orderByDirection.toLowerCase()]);
37
+ if (dexieResult.maxItemCount) {
38
+ if (result.length > newCursor) {
39
+ resultCursor = newCursor;
44
40
  }
45
- return { nodes: result, cursor: resultCursor ? _.toString(resultCursor) : null };
46
- }
47
- if (dexieResult.maxItemCount && result.length >= dexieResult.maxItemCount) {
48
- resultCursor = newCursor;
41
+ result = result.slice(normalizedArgs.cursor, newCursor);
49
42
  }
50
43
  return { nodes: result, cursor: resultCursor ? _.toString(resultCursor) : null };
44
+ }
45
+ if (dexieResult.maxItemCount && result.length >= dexieResult.maxItemCount) {
46
+ resultCursor = newCursor;
47
+ }
48
+ return { nodes: result, cursor: resultCursor ? _.toString(resultCursor) : null };
49
+ }
50
+ return {
51
+ driverName: "indexedDb",
52
+ async deleteMany(args, ctx) {
53
+ await initDbIfNeeded(dbName);
54
+ const { nodes, cursor } = await getMany(args, ctx);
55
+ const table = dbWrapper.db.table(handle);
56
+ await table.bulkDelete(nodes.map((x) => x.id));
57
+ return { nodes, cursor };
51
58
  },
59
+ getMany,
52
60
  async putMany(items, ctx) {
53
61
  await initDbIfNeeded(dbName);
54
62
  const table = dbWrapper.db.table(handle);
@@ -81,7 +89,7 @@ function applyArgsToDexieTable(table, args) {
81
89
  }
82
90
  const f = getFilter(where);
83
91
  if (f) {
84
- collection = collection.filter(f);
92
+ collection = collection.filter((x) => f(x) || false);
85
93
  }
86
94
  if (maxItemCount && !orderByProp) {
87
95
  if (args.cursor)
@@ -33,6 +33,16 @@ const operatorFns = {
33
33
  return true;
34
34
  },
35
35
  arrayContains: (x, w) => x?.includes(w),
36
+ isEmpty: (x, w) => {
37
+ if (!x) {
38
+ if (w === true) return true;
39
+ if (w === false) return false;
40
+ return true;
41
+ }
42
+ if (w === true) return x?.length === 0;
43
+ if (w === false) return x?.length > 0;
44
+ return true;
45
+ },
36
46
  isNull: (x, w) => {
37
47
  if (w === true) return x == null;
38
48
  if (w === false) return x != null;
@@ -154,7 +164,16 @@ function getFilter(where, namePrefix = "") {
154
164
  if (f) andClauses.push(f);
155
165
  }
156
166
  if (andClauses.length === 0) return null;
157
- return x => andClauses.every(ac => ac(x));
167
+ return x => resolveAndClauses(andClauses, x);
168
+ }
169
+ function resolveAndClauses(andClauses, x) {
170
+ let result = true;
171
+ for (const ac of andClauses) {
172
+ const clauseResult = ac(x);
173
+ if (clauseResult === false) return false;
174
+ if (clauseResult == null) result = void 0;
175
+ }
176
+ return result;
158
177
  }
159
178
  function getWhereNameOperatorPair(whereKey) {
160
179
  if (whereKey.startsWith("_type")) {
@@ -168,14 +187,14 @@ function getFilterInner(operator, whereVal, name, namePrefix) {
168
187
  return getFilter(whereVal, name);
169
188
  }
170
189
  if (operator === "some") {
171
- const f = getFilter(whereVal, namePrefix);
190
+ const f = getFilter(whereVal);
172
191
  return f && (x => {
173
192
  const val = _lodash.default.get(x, name);
174
- return val === void 0 || val?.some(f);
193
+ return val?.some(f);
175
194
  });
176
195
  }
177
196
  if (operator === "none") {
178
- const f = getFilter(whereVal, namePrefix);
197
+ const f = getFilter(whereVal);
179
198
  return f && (x => !_lodash.default.get(x, name)?.some(f));
180
199
  }
181
200
  if (operator === "and") {
@@ -186,7 +205,7 @@ function getFilterInner(operator, whereVal, name, namePrefix) {
186
205
  }
187
206
  if (operator === "not") {
188
207
  const f = getFilter(whereVal, namePrefix);
189
- return f && (x => !f(x));
208
+ return f && (x => f(x) === false);
190
209
  }
191
210
  if (operator === "or") {
192
211
  if (!_lodash.default.isArray(whereVal)) throw new Error(`Value for where._or must be an array`);
@@ -8,4 +8,4 @@ export declare function queryArray(array: any[], args: GetManyArgsAny): {
8
8
  nodes: any;
9
9
  cursor: any;
10
10
  };
11
- export declare function getFilter(where: Record<string, any>, namePrefix?: string): ((x: any) => boolean) | null;
11
+ export declare function getFilter(where: Record<string, any>, namePrefix?: string): ((x: any) => boolean | undefined) | null;
@@ -23,6 +23,20 @@ const operatorFns = {
23
23
  return true;
24
24
  },
25
25
  arrayContains: (x, w) => x?.includes(w),
26
+ isEmpty: (x, w) => {
27
+ if (!x) {
28
+ if (w === true)
29
+ return true;
30
+ if (w === false)
31
+ return false;
32
+ return true;
33
+ }
34
+ if (w === true)
35
+ return x?.length === 0;
36
+ if (w === false)
37
+ return x?.length > 0;
38
+ return true;
39
+ },
26
40
  isNull: (x, w) => {
27
41
  if (w === true)
28
42
  return x == null;
@@ -143,7 +157,18 @@ export function getFilter(where, namePrefix = "") {
143
157
  }
144
158
  if (andClauses.length === 0)
145
159
  return null;
146
- return (x) => andClauses.every((ac) => ac(x));
160
+ return (x) => resolveAndClauses(andClauses, x);
161
+ }
162
+ function resolveAndClauses(andClauses, x) {
163
+ let result = true;
164
+ for (const ac of andClauses) {
165
+ const clauseResult = ac(x);
166
+ if (clauseResult === false)
167
+ return false;
168
+ if (clauseResult == null)
169
+ result = void 0;
170
+ }
171
+ return result;
147
172
  }
148
173
  function getWhereNameOperatorPair(whereKey) {
149
174
  if (whereKey.startsWith("_type")) {
@@ -157,14 +182,14 @@ function getFilterInner(operator, whereVal, name, namePrefix) {
157
182
  return getFilter(whereVal, name);
158
183
  }
159
184
  if (operator === "some") {
160
- const f = getFilter(whereVal, namePrefix);
185
+ const f = getFilter(whereVal);
161
186
  return f && ((x) => {
162
187
  const val = _.get(x, name);
163
- return val === void 0 || val?.some(f);
188
+ return val?.some(f);
164
189
  });
165
190
  }
166
191
  if (operator === "none") {
167
- const f = getFilter(whereVal, namePrefix);
192
+ const f = getFilter(whereVal);
168
193
  return f && ((x) => !_.get(x, name)?.some(f));
169
194
  }
170
195
  if (operator === "and") {
@@ -177,7 +202,7 @@ function getFilterInner(operator, whereVal, name, namePrefix) {
177
202
  }
178
203
  if (operator === "not") {
179
204
  const f = getFilter(whereVal, namePrefix);
180
- return f && ((x) => !f(x));
205
+ return f && ((x) => f(x) === false);
181
206
  }
182
207
  if (operator === "or") {
183
208
  if (!_.isArray(whereVal))
@@ -82,6 +82,7 @@ function getEffectFor(entityName, aggregateRelationField, eventEntityName, schem
82
82
  result.push({
83
83
  aggDoc,
84
84
  events,
85
+ updatedEvents: events.filter(ev => docsById[ev.id]),
85
86
  oldAggDoc: existingAggregatesById[aggDoc.id]
86
87
  });
87
88
  }
@@ -93,6 +94,7 @@ function getEffectFor(entityName, aggregateRelationField, eventEntityName, schem
93
94
  }, result.map(v => ({
94
95
  doc: v.aggDoc,
95
96
  events: v.events,
97
+ updatedEvents: v.updatedEvents,
96
98
  oldDoc: v.oldAggDoc
97
99
  })), ctx);
98
100
  return result;
@@ -102,6 +104,7 @@ function getEffectFor(entityName, aggregateRelationField, eventEntityName, schem
102
104
  const hookItems = beforePutResult.map(v => ({
103
105
  doc: v.aggDoc,
104
106
  events: v.events,
107
+ updatedEvents: v.updatedEvents,
105
108
  oldDoc: v.oldAggDoc
106
109
  }));
107
110
  const aggregateEffectContext = {
@@ -74,19 +74,29 @@ function getEffectFor(entityName, aggregateRelationField, eventEntityName, schem
74
74
  if (!context.options.keepNulls)
75
75
  cleanUndefinedAndNull(aggDoc);
76
76
  }
77
- result.push({ aggDoc, events, oldAggDoc: existingAggregatesById[aggDoc.id] });
77
+ result.push({
78
+ aggDoc,
79
+ events,
80
+ updatedEvents: events.filter((ev) => docsById[ev.id]),
81
+ oldAggDoc: existingAggregatesById[aggDoc.id]
82
+ });
78
83
  }
79
84
  await handlePrecomputed(
80
85
  // @ts-expect-error TODO wrong types
81
86
  { ...context, typeName: entityName },
82
- result.map((v) => ({ doc: v.aggDoc, events: v.events, oldDoc: v.oldAggDoc })),
87
+ result.map((v) => ({ doc: v.aggDoc, events: v.events, updatedEvents: v.updatedEvents, oldDoc: v.oldAggDoc })),
83
88
  ctx
84
89
  );
85
90
  return result;
86
91
  },
87
92
  async afterPut(context, docs, beforePutResult, ctx) {
88
93
  const aggs = beforePutResult.map((d) => d.aggDoc);
89
- const hookItems = beforePutResult.map((v) => ({ doc: v.aggDoc, events: v.events, oldDoc: v.oldAggDoc }));
94
+ const hookItems = beforePutResult.map((v) => ({
95
+ doc: v.aggDoc,
96
+ events: v.events,
97
+ updatedEvents: v.updatedEvents,
98
+ oldDoc: v.oldAggDoc
99
+ }));
90
100
  const aggregateEffectContext = { ...context, typeName: entityName, handle: schema[entityName].handle };
91
101
  await beforePut(hookItems, ctx, aggregateEffectContext);
92
102
  await context.drivers[entityName].putMany(aggs, ctx);
@@ -205,9 +205,12 @@ function getWhereFieldsFor(schema, type, fieldKey) {
205
205
  const isPrimitive = !fieldType;
206
206
  const isEnum = !!fieldType?.enumValues;
207
207
  const isObject = !!fieldType?.fields;
208
- const isRelation = field.isRelation;
209
- const isRequired = field.isRequired;
210
- const isArray = field.isArray;
208
+ const {
209
+ isRelation,
210
+ isRequired,
211
+ isArray,
212
+ isChange
213
+ } = field;
211
214
  if (isArray) {
212
215
  const arrayWhereFields = [`${name}_isEmpty?: boolean`];
213
216
  if (!isRequired) arrayWhereFields.push(`${name}_isNull?: boolean`);
@@ -228,6 +231,9 @@ function getWhereFieldsFor(schema, type, fieldKey) {
228
231
  return [`${name}?: ${getRelationWhereType()}`, ...commonWhereFields];
229
232
  }
230
233
  if (isObject) {
234
+ if (isChange) {
235
+ return [`${name}?: Omit<${fieldTypeName}_Where, 'id'>`, `${name}_jsonContains?: WhereJsonContains`, ...commonWhereFields];
236
+ }
231
237
  return [`${name}?: ${fieldTypeName}_Where`, ...commonWhereFields];
232
238
  }
233
239
  if (isEnum) {
@@ -223,9 +223,7 @@ function getWhereFieldsFor(schema, type, fieldKey) {
223
223
  const isPrimitive = !fieldType;
224
224
  const isEnum = !!fieldType?.enumValues;
225
225
  const isObject = !!fieldType?.fields;
226
- const isRelation = field.isRelation;
227
- const isRequired = field.isRequired;
228
- const isArray = field.isArray;
226
+ const { isRelation, isRequired, isArray, isChange } = field;
229
227
  if (isArray) {
230
228
  const arrayWhereFields = [`${name}_isEmpty?: boolean`];
231
229
  if (!isRequired)
@@ -248,6 +246,13 @@ function getWhereFieldsFor(schema, type, fieldKey) {
248
246
  return [`${name}?: ${getRelationWhereType()}`, ...commonWhereFields];
249
247
  }
250
248
  if (isObject) {
249
+ if (isChange) {
250
+ return [
251
+ `${name}?: Omit<${fieldTypeName}_Where, 'id'>`,
252
+ `${name}_jsonContains?: WhereJsonContains`,
253
+ ...commonWhereFields
254
+ ];
255
+ }
251
256
  return [`${name}?: ${fieldTypeName}_Where`, ...commonWhereFields];
252
257
  }
253
258
  if (isEnum) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rads-db",
3
- "version": "3.1.5",
3
+ "version": "3.1.7",
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": "",