proto.io 0.0.206 → 0.0.208

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.
Files changed (58) hide show
  1. package/dist/adapters/file/aliyun-oss.d.ts +3 -3
  2. package/dist/adapters/file/aliyun-oss.js +2 -2
  3. package/dist/adapters/file/aliyun-oss.js.map +1 -1
  4. package/dist/adapters/file/aliyun-oss.mjs +2 -2
  5. package/dist/adapters/file/aliyun-oss.mjs.map +1 -1
  6. package/dist/adapters/file/database.d.ts +2 -2
  7. package/dist/adapters/file/database.js +3 -3
  8. package/dist/adapters/file/database.js.map +1 -1
  9. package/dist/adapters/file/database.mjs +3 -3
  10. package/dist/adapters/file/database.mjs.map +1 -1
  11. package/dist/adapters/file/filesystem.d.ts +3 -3
  12. package/dist/adapters/file/google-cloud-storage.d.ts +3 -3
  13. package/dist/adapters/storage/progres.d.ts +9 -17
  14. package/dist/adapters/storage/progres.js +24 -101
  15. package/dist/adapters/storage/progres.js.map +1 -1
  16. package/dist/adapters/storage/progres.mjs +24 -101
  17. package/dist/adapters/storage/progres.mjs.map +1 -1
  18. package/dist/client.d.ts +3 -3
  19. package/dist/client.js +2 -2
  20. package/dist/client.mjs +3 -3
  21. package/dist/index.d.ts +3 -3
  22. package/dist/index.js +123 -144
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs +125 -146
  25. package/dist/index.mjs.map +1 -1
  26. package/dist/internals/{base-Be7PFKEC.d.ts → base-wSknxBv7.d.ts} +2 -2
  27. package/dist/internals/base-wSknxBv7.d.ts.map +1 -0
  28. package/dist/internals/{chunk-CEQtSsWb.d.ts → chunk-C_BXf1Er.d.ts} +3 -3
  29. package/dist/internals/chunk-C_BXf1Er.d.ts.map +1 -0
  30. package/dist/internals/{index-DnbbpIaO.d.ts → index-A-hoMfDO.d.ts} +48 -20
  31. package/dist/internals/index-A-hoMfDO.d.ts.map +1 -0
  32. package/dist/internals/{index-CsmAYB_t.js → index-B8TESzd9.js} +1 -2
  33. package/dist/internals/{index-CsmAYB_t.js.map → index-B8TESzd9.js.map} +1 -1
  34. package/dist/internals/{index-CSnRU_KQ.mjs → index-BvwYz4Yp.mjs} +60 -18
  35. package/dist/internals/index-BvwYz4Yp.mjs.map +1 -0
  36. package/dist/internals/{index-CyPxh5dl.js → index-CkAvNaAe.js} +2 -2
  37. package/dist/internals/{index-CyPxh5dl.js.map → index-CkAvNaAe.js.map} +1 -1
  38. package/dist/internals/{index-BFZlY3IO.js → index-DVUiXLfI.js} +60 -18
  39. package/dist/internals/index-DVUiXLfI.js.map +1 -0
  40. package/dist/internals/{index-iRVgw566.mjs → index-DlY33lfO.mjs} +2 -2
  41. package/dist/internals/{index-iRVgw566.mjs.map → index-DlY33lfO.mjs.map} +1 -1
  42. package/dist/internals/{index-DpN9tcbY.mjs → index-al1N-qi7.mjs} +2 -2
  43. package/dist/internals/{index-DpN9tcbY.mjs.map → index-al1N-qi7.mjs.map} +1 -1
  44. package/dist/internals/{index-CURcwPBG.d.ts → index-iqOd-Wdg.d.ts} +2 -2
  45. package/dist/internals/index-iqOd-Wdg.d.ts.map +1 -0
  46. package/dist/internals/{random-D7S1XD7F.js → random-B0V0EnjP.js} +3 -3
  47. package/dist/internals/random-B0V0EnjP.js.map +1 -0
  48. package/dist/internals/{random-BnGB_bVS.mjs → random-ZgzzM5v_.mjs} +3 -3
  49. package/dist/internals/random-ZgzzM5v_.mjs.map +1 -0
  50. package/package.json +1 -1
  51. package/dist/internals/base-Be7PFKEC.d.ts.map +0 -1
  52. package/dist/internals/chunk-CEQtSsWb.d.ts.map +0 -1
  53. package/dist/internals/index-BFZlY3IO.js.map +0 -1
  54. package/dist/internals/index-CSnRU_KQ.mjs.map +0 -1
  55. package/dist/internals/index-CURcwPBG.d.ts.map +0 -1
  56. package/dist/internals/index-DnbbpIaO.d.ts.map +0 -1
  57. package/dist/internals/random-BnGB_bVS.mjs.map +0 -1
  58. package/dist/internals/random-D7S1XD7F.js.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  import _ from 'lodash';
2
2
  import { Server } from '@o2ter/server-js';
3
- import { Q as QueryValidator, r as resolveColumn, a as resolveDataType, g as generateId } from './internals/random-BnGB_bVS.mjs';
3
+ import { Q as QueryValidator, r as resolveColumn, a as resolveDataType, g as generateId } from './internals/random-ZgzzM5v_.mjs';
4
4
  import { P as PVK } from './internals/private-CNw40LZ7.mjs';
5
- import { asyncStream, prototypes, isBinaryData, base64ToBuffer } from '@o2ter/utils-js';
6
- import { T as TQuery, d as deserialize, P as PROTO_NOTY_MSG, M as MASTER_USER_HEADER_NAME, a as MASTER_PASS_HEADER_NAME, A as AUTH_COOKIE_KEY, b as TUser, c as ProtoType, s as serialize, U as UPLOAD_TOKEN_HEADER_NAME } from './internals/index-CSnRU_KQ.mjs';
7
- export { e as ProtoClient, f as classExtends, k as isFile, l as isJob, g as isObject, i as isQuery, j as isRole, h as isUser } from './internals/index-CSnRU_KQ.mjs';
8
- import { i as isPointer, a as isRelation, T as TObject, b as isShape, d as defaultObjectKeyTypes, c as isPrimitive } from './internals/index-DpN9tcbY.mjs';
5
+ import { prototypes, asyncStream, isBinaryData, base64ToBuffer } from '@o2ter/utils-js';
6
+ import { T as TQuery, d as deserialize, P as PROTO_NOTY_MSG, M as MASTER_USER_HEADER_NAME, a as MASTER_PASS_HEADER_NAME, A as AUTH_COOKIE_KEY, b as TUser, c as ProtoType, s as serialize, U as UPLOAD_TOKEN_HEADER_NAME } from './internals/index-BvwYz4Yp.mjs';
7
+ export { e as ProtoClient, f as classExtends, k as isFile, l as isJob, g as isObject, i as isQuery, j as isRole, h as isUser } from './internals/index-BvwYz4Yp.mjs';
8
+ import { i as isPointer, a as isRelation, T as TObject, b as isShape, d as defaultObjectKeyTypes, c as isPrimitive } from './internals/index-al1N-qi7.mjs';
9
9
  import jwt from 'jsonwebtoken';
10
10
  import { Blob } from 'node:buffer';
11
11
  import { Readable } from 'node:stream';
@@ -13,7 +13,7 @@ import { scrypt } from 'node:crypto';
13
13
  import { promisify } from 'util';
14
14
  import { randomBytes, randomUUID } from '@o2ter/crypto-js';
15
15
  export { Decimal } from 'decimal.js';
16
- import { Q as QuerySelector } from './internals/index-iRVgw566.mjs';
16
+ import { Q as QuerySelector } from './internals/index-DlY33lfO.mjs';
17
17
  import queryType from 'query-types';
18
18
  import busboy from 'busboy';
19
19
  import 'axios';
@@ -85,6 +85,7 @@ const dispatcher = (proto, options) => {
85
85
  master: options.master ?? false,
86
86
  disableSecurity: options.disableSecurity,
87
87
  });
88
+ const createFile = options.createFile;
88
89
  return {
89
90
  async explain(query) {
90
91
  QueryValidator.recursiveCheck(query);
@@ -131,35 +132,9 @@ const dispatcher = (proto, options) => {
131
132
  throw Error('No permission');
132
133
  return proto.storage.random(decoded, opts);
133
134
  },
134
- async insert(options, attrs) {
135
- QueryValidator.recursiveCheck(attrs);
136
- const _validator = await validator();
137
- _validator.validateCountMatches(options.className, options.countMatches ?? []);
138
- const _includes = _validator.decodeIncludes(options.className, options.includes ?? ['*']);
139
- const _matches = _validator.decodeMatches(options.className, options.matches ?? {}, _includes);
140
- if (!_validator.validateCLPs(options.className, 'create'))
141
- throw Error('No permission');
142
- const _attrs = normalize(_validator.validateFields(options.className, attrs, 'create', QueryValidator.patterns.path));
143
- while (true) {
144
- try {
145
- return await proto.storage.atomic((storage) => storage.insert({
146
- className: options.className,
147
- includes: _includes,
148
- matches: _matches,
149
- countMatches: options.countMatches ?? [],
150
- objectIdSize: proto[PVK].options.objectIdSize
151
- }, _attrs), { lockTable: options.className, retry: true });
152
- }
153
- catch (e) {
154
- if (proto.storage.isDuplicateIdError(e))
155
- continue;
156
- throw e;
157
- }
158
- }
159
- },
160
- async insertMany(options, values) {
161
- if (options.className === 'File')
162
- throw Error('File is not support insertMany');
135
+ async insert(options, values) {
136
+ if (!createFile && options.className === 'File')
137
+ throw Error('File is not support insert');
163
138
  QueryValidator.recursiveCheck(values);
164
139
  const _validator = await validator();
165
140
  _validator.validateCountMatches(options.className, options.countMatches ?? []);
@@ -170,7 +145,7 @@ const dispatcher = (proto, options) => {
170
145
  const _attrs = normalize(_.map(values, attr => _validator.validateFields(options.className, attr, 'create', QueryValidator.patterns.path)));
171
146
  while (true) {
172
147
  try {
173
- return await proto.storage.atomic((storage) => storage.insertMany({
148
+ return await proto.storage.atomic((storage) => storage.insert({
174
149
  className: options.className,
175
150
  includes: _includes,
176
151
  matches: _matches,
@@ -185,44 +160,16 @@ const dispatcher = (proto, options) => {
185
160
  }
186
161
  }
187
162
  },
188
- async updateOne(query, update) {
189
- QueryValidator.recursiveCheck(query, update);
190
- const _validator = await validator();
191
- if (!_validator.validateCLPs(query.className, 'update'))
192
- throw Error('No permission');
193
- return proto.storage.atomic((storage) => storage.updateOne(_validator.decodeQuery(normalize(query), 'update'), normalize(_validator.validateFields(query.className, update, 'update', QueryValidator.patterns.path))));
194
- },
195
- async updateMany(query, update) {
163
+ async update(query, update) {
196
164
  QueryValidator.recursiveCheck(query, update);
197
165
  const _validator = await validator();
198
166
  if (!_validator.validateCLPs(query.className, 'update'))
199
167
  throw Error('No permission');
200
- return proto.storage.atomic((storage) => storage.updateMany(_validator.decodeQuery(normalize(query), 'update'), normalize(_validator.validateFields(query.className, update, 'update', QueryValidator.patterns.path))));
201
- },
202
- async upsertOne(query, update, setOnInsert) {
203
- if (query.className === 'File')
204
- throw Error('File is not support upsertOne');
205
- QueryValidator.recursiveCheck(query, update, setOnInsert);
206
- const _validator = await validator();
207
- if (!_validator.validateCLPs(query.className, 'create', 'update'))
208
- throw Error('No permission');
209
- const _query = _validator.decodeQuery(normalize(query), 'update');
210
- const _update = normalize(_validator.validateFields(query.className, update, 'update', QueryValidator.patterns.path));
211
- const _setOnInsert = normalize(_validator.validateFields(query.className, setOnInsert, 'create', QueryValidator.patterns.path));
212
- while (true) {
213
- try {
214
- return await proto.storage.atomic((storage) => storage.upsertOne(_query, _update, _setOnInsert), { lockTable: query.className, retry: true });
215
- }
216
- catch (e) {
217
- if (proto.storage.isDuplicateIdError(e))
218
- continue;
219
- throw e;
220
- }
221
- }
168
+ return proto.storage.atomic((storage) => storage.update(_validator.decodeQuery(normalize(query), 'update'), normalize(_validator.validateFields(query.className, update, 'update', QueryValidator.patterns.path))));
222
169
  },
223
- async upsertMany(query, update, setOnInsert) {
170
+ async upsert(query, update, setOnInsert) {
224
171
  if (query.className === 'File')
225
- throw Error('File is not support upsertOne');
172
+ throw Error('File is not support upsert');
226
173
  QueryValidator.recursiveCheck(query, update, setOnInsert);
227
174
  const _validator = await validator();
228
175
  if (!_validator.validateCLPs(query.className, 'create', 'update'))
@@ -232,7 +179,7 @@ const dispatcher = (proto, options) => {
232
179
  const _setOnInsert = normalize(_validator.validateFields(query.className, setOnInsert, 'create', QueryValidator.patterns.path));
233
180
  while (true) {
234
181
  try {
235
- return await proto.storage.atomic((storage) => storage.upsertMany(_query, _update, _setOnInsert), { lockTable: query.className, retry: true });
182
+ return await proto.storage.atomic((storage) => storage.upsert(_query, _update, _setOnInsert), { lockTable: query.className, retry: true });
236
183
  }
237
184
  catch (e) {
238
185
  if (proto.storage.isDuplicateIdError(e))
@@ -241,23 +188,52 @@ const dispatcher = (proto, options) => {
241
188
  }
242
189
  }
243
190
  },
244
- async deleteOne(query) {
245
- QueryValidator.recursiveCheck(query);
246
- const _validator = await validator();
247
- if (!_validator.validateCLPs(query.className, 'delete'))
248
- throw Error('No permission');
249
- return proto.storage.atomic((storage) => storage.deleteOne(_validator.decodeQuery(normalize(query), 'update')));
250
- },
251
- async deleteMany(query) {
191
+ async delete(query) {
252
192
  QueryValidator.recursiveCheck(query);
253
193
  const _validator = await validator();
254
194
  if (!_validator.validateCLPs(query.className, 'delete'))
255
195
  throw Error('No permission');
256
- return proto.storage.atomic((storage) => storage.deleteMany(_validator.decodeQuery(normalize(query), 'update')));
196
+ return proto.storage.atomic((storage) => storage.delete(_validator.decodeQuery(normalize(query), 'update')));
257
197
  },
258
198
  };
259
199
  };
260
200
 
201
+ //
202
+ // proxy.ts
203
+ //
204
+ // The MIT License
205
+ // Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
206
+ //
207
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
208
+ // of this software and associated documentation files (the "Software"), to deal
209
+ // in the Software without restriction, including without limitation the rights
210
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
211
+ // copies of the Software, and to permit persons to whom the Software is
212
+ // furnished to do so, subject to the following conditions:
213
+ //
214
+ // The above copyright notice and this permission notice shall be included in
215
+ // all copies or substantial portions of the Software.
216
+ //
217
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
218
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
219
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
220
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
221
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
222
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
223
+ // THE SOFTWARE.
224
+ //
225
+ const proxy = (x) => {
226
+ const self = x;
227
+ const proxy = _.create(self);
228
+ for (const name of _.uniq(_.flatMap(prototypes(proxy), x => Object.getOwnPropertyNames(x)))) {
229
+ if (name === 'constructor')
230
+ continue;
231
+ const value = _.isFunction(self[name]) ? self[name].bind(self) : self[name];
232
+ Object.defineProperty(proxy, name, { get: () => value });
233
+ }
234
+ return proxy;
235
+ };
236
+
261
237
  //
262
238
  // methods.ts
263
239
  //
@@ -308,6 +284,7 @@ class _ProtoQuery extends TQuery {
308
284
  return dispatcher(_serviceOf(options) ?? this._proto, {
309
285
  ...options ?? {},
310
286
  disableSecurity: !!this._opts.insecure,
287
+ createFile: !!this._opts.createFile,
311
288
  });
312
289
  }
313
290
  explain(options) {
@@ -343,45 +320,65 @@ class _ProtoQuery extends TQuery {
343
320
  yield self._objectMethods(object);
344
321
  });
345
322
  }
346
- async insert(attrs, options) {
347
- const result = this._objectMethods(await this._dispatcher(options).insert({
348
- className: this.className,
349
- includes: this[PVK].options.includes,
350
- matches: this[PVK].options.matches,
351
- countMatches: this[PVK].options.countMatches,
352
- }, attrs));
353
- if (!result)
354
- throw Error('Unable to insert document');
355
- return result;
323
+ _on_upsert(objects) {
324
+ const createTraggers = this._proto[PVK].triggers[this.className]?.create ?? [];
325
+ const updateTraggers = this._proto[PVK].triggers[this.className]?.update ?? [];
326
+ for (const obj of objects) {
327
+ for (const tragger of obj.__v === 0 ? createTraggers : updateTraggers) {
328
+ (async () => {
329
+ try {
330
+ await tragger(proxy(Object.setPrototypeOf({ object: obj }, this._proto)));
331
+ }
332
+ catch (e) {
333
+ console.error(e);
334
+ }
335
+ })();
336
+ }
337
+ }
338
+ }
339
+ _on_delete(objects) {
340
+ const traggers = this._proto[PVK].triggers[this.className]?.delete ?? [];
341
+ for (const obj of objects) {
342
+ for (const tragger of traggers) {
343
+ (async () => {
344
+ try {
345
+ await tragger(proxy(Object.setPrototypeOf({ object: obj }, this._proto)));
346
+ }
347
+ catch (e) {
348
+ console.error(e);
349
+ }
350
+ })();
351
+ }
352
+ }
356
353
  }
357
354
  async insertMany(values, options) {
358
- return this._objectMethods(await this._dispatcher(options).insertMany({
355
+ const objs = this._objectMethods(await this._dispatcher(options).insert({
359
356
  className: this.className,
360
357
  includes: this[PVK].options.includes,
361
358
  matches: this[PVK].options.matches,
362
359
  countMatches: this[PVK].options.countMatches,
363
360
  }, values));
364
- }
365
- async updateOne(update, options) {
366
- return this._objectMethods(await this._dispatcher(options).updateOne(this._queryOptions, update));
361
+ if (!options?.silent)
362
+ this._on_upsert(objs);
363
+ return objs;
367
364
  }
368
365
  async updateMany(update, options) {
369
- return this._objectMethods(await this._dispatcher(options).updateMany(this._queryOptions, update));
370
- }
371
- async upsertOne(update, setOnInsert, options) {
372
- const result = this._objectMethods(await this._dispatcher(options).upsertOne(this._queryOptions, update, setOnInsert));
373
- if (!result)
374
- throw Error('Unable to upsert document');
375
- return result;
366
+ const objs = this._objectMethods(await this._dispatcher(options).update(this._queryOptions, update));
367
+ if (!options?.silent)
368
+ this._on_upsert(objs);
369
+ return objs;
376
370
  }
377
371
  async upsertMany(update, setOnInsert, options) {
378
- return this._objectMethods(await this._dispatcher(options).upsertMany(this._queryOptions, update, setOnInsert));
379
- }
380
- async deleteOne(options) {
381
- return this._objectMethods(await this._dispatcher(options).deleteOne(this._queryOptions));
372
+ const objs = this._objectMethods(await this._dispatcher(options).upsert(this._queryOptions, update, setOnInsert));
373
+ if (!options?.silent)
374
+ this._on_upsert(objs);
375
+ return objs;
382
376
  }
383
377
  async deleteMany(options) {
384
- return this._objectMethods(await this._dispatcher(options).deleteMany(this._queryOptions));
378
+ const objs = this._objectMethods(await this._dispatcher(options).delete(this._queryOptions));
379
+ if (!options?.silent)
380
+ this._on_delete(objs);
381
+ return objs;
385
382
  }
386
383
  }
387
384
  class ProtoQuery extends _ProtoQuery {
@@ -636,42 +633,6 @@ const varifyPassword = async (alg, password, options) => {
636
633
  }
637
634
  };
638
635
 
639
- //
640
- // proxy.ts
641
- //
642
- // The MIT License
643
- // Copyright (c) 2021 - 2025 O2ter Limited. All rights reserved.
644
- //
645
- // Permission is hereby granted, free of charge, to any person obtaining a copy
646
- // of this software and associated documentation files (the "Software"), to deal
647
- // in the Software without restriction, including without limitation the rights
648
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
649
- // copies of the Software, and to permit persons to whom the Software is
650
- // furnished to do so, subject to the following conditions:
651
- //
652
- // The above copyright notice and this permission notice shall be included in
653
- // all copies or substantial portions of the Software.
654
- //
655
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
656
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
657
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
658
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
659
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
660
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
661
- // THE SOFTWARE.
662
- //
663
- const proxy = (x) => {
664
- const self = x;
665
- const proxy = _.create(self);
666
- for (const name of _.uniq(_.flatMap(prototypes(proxy), x => Object.getOwnPropertyNames(x)))) {
667
- if (name === 'constructor')
668
- continue;
669
- const value = _.isFunction(self[name]) ? self[name].bind(self) : self[name];
670
- Object.defineProperty(proxy, name, { get: () => value });
671
- }
672
- return proxy;
673
- };
674
-
675
636
  //
676
637
  // internal.ts
677
638
  //
@@ -796,6 +757,7 @@ const mergeSchema = (...schemas) => _.reduce(schemas, (acc, schema) => ({
796
757
  class ProtoInternal {
797
758
  options;
798
759
  functions = {};
760
+ triggers = {};
799
761
  jobs = {};
800
762
  jobRunner = new JobRunner();
801
763
  constructor(options) {
@@ -958,9 +920,8 @@ class ProtoInternal {
958
920
  object.set('size', file.size);
959
921
  if (nonce)
960
922
  object.set('nonce', nonce);
961
- const created = await proto.Query('File')
962
- .includes(...object.keys())
963
- .insert(_.fromPairs([...object._set_entries()]), options);
923
+ const query = new ProtoQuery('File', proto, { createFile: true });
924
+ const created = await query.includes(...object.keys()).insert(_.fromPairs([...object._set_entries()]), options);
964
925
  if (created) {
965
926
  object[PVK].attributes = created.attributes;
966
927
  object[PVK].mutated = {};
@@ -1612,6 +1573,24 @@ class ProtoService extends ProtoType {
1612
1573
  define(name, callback, options) {
1613
1574
  this[PVK].functions[name] = options ? { callback, ...options } : callback;
1614
1575
  }
1576
+ afterCreate(className, callback) {
1577
+ if (_.isNil(this[PVK].triggers[className])) {
1578
+ this[PVK].triggers[className] = { create: [], update: [], delete: [] };
1579
+ }
1580
+ this[PVK].triggers[className].create.push(callback);
1581
+ }
1582
+ afterUpdate(className, callback) {
1583
+ if (_.isNil(this[PVK].triggers[className])) {
1584
+ this[PVK].triggers[className] = { create: [], update: [], delete: [] };
1585
+ }
1586
+ this[PVK].triggers[className].update.push(callback);
1587
+ }
1588
+ afterDelete(className, callback) {
1589
+ if (_.isNil(this[PVK].triggers[className])) {
1590
+ this[PVK].triggers[className] = { create: [], update: [], delete: [] };
1591
+ }
1592
+ this[PVK].triggers[className].delete.push(callback);
1593
+ }
1615
1594
  scheduleJob(name, params, options) {
1616
1595
  return this[PVK].scheduleJob(this, name, params, options);
1617
1596
  }
@@ -1676,7 +1655,7 @@ class ProtoService extends ProtoType {
1676
1655
  await this.fileStorage.destroy(this, token);
1677
1656
  }
1678
1657
  }
1679
- await this.storage.deleteMany({
1658
+ await this.storage.delete({
1680
1659
  className,
1681
1660
  filter: QuerySelector.decode({ _expired_at: { $lt: time } }),
1682
1661
  includes: ['_id', '_expired_at'],
@@ -1821,12 +1800,12 @@ const verifyRelatedBy = (relatedBy) => {
1821
1800
  var classesRoute = (router, proto) => {
1822
1801
  const defaultHandler = async (req) => {
1823
1802
  const { name } = req.params;
1824
- const { operation, random, attributes, update, setOnInsert, relatedBy, ...options } = deserialize(req.body);
1803
+ const { operation, random, attributes, update, setOnInsert, relatedBy, silent, ...options } = deserialize(req.body);
1825
1804
  verifyRelatedBy(relatedBy);
1826
1805
  const payload = proto.connect(req);
1827
1806
  const query = relatedBy ? payload.Relation(payload.Object(relatedBy.className, relatedBy.objectId), relatedBy.key) : payload.Query(name);
1828
1807
  query[PVK].options = options;
1829
- const opts = { master: payload.isMaster };
1808
+ const opts = { master: payload.isMaster, silent };
1830
1809
  if (_.includes(['File', '_Job', '_JobScope'], name) &&
1831
1810
  _.includes(['insert', 'insertMany', 'upsertOne', 'upsertMany'], operation))
1832
1811
  throw Error('Invaild operation');