@sprucelabs/data-stores 25.7.6 → 25.7.8

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.
@@ -3,14 +3,16 @@ import AbstractStore from '../stores/AbstractStore';
3
3
  import { QueryOptions } from '../types/query.types';
4
4
  import { PrepareOptions } from '../types/stores.types';
5
5
  export default class BatchCursorImpl<ResponseRecord> implements BatchCursor<ResponseRecord> {
6
+ static Class?: new (...args: any[]) => BatchCursor<any>;
6
7
  private store;
7
8
  private options?;
8
- private query?;
9
+ protected query?: Record<string, any>;
9
10
  private nextHandler?;
10
- private constructor();
11
+ private lastId?;
12
+ protected constructor(store: AbstractStore<Schema>, query?: Record<string, any>, options?: FindBatchOptions);
11
13
  [Symbol.asyncIterator](): AsyncIterator<ResponseRecord, any, undefined>;
12
14
  static Cursor<Response>(store: AbstractStore<Schema>, query?: Record<string, any>, options?: FindBatchOptions): BatchCursor<Response>;
13
- setOnNextResults(cb: (results: ResponseRecord[]) => never[]): void;
15
+ setOnNextResults(cb: OnNextResultsHandler<ResponseRecord>): void;
14
16
  getTotalRecords(): Promise<number>;
15
17
  next(): Promise<ResponseRecord[] | null>;
16
18
  private bumpCursorPosition;
@@ -27,8 +27,9 @@ class BatchCursorImpl {
27
27
  };
28
28
  }
29
29
  static Cursor(store, query, options) {
30
+ var _a;
30
31
  (0, schema_1.assertOptions)({ store }, ['store'], 'You need to pass a store to BatchCursor.Cursor()');
31
- return new this(store, query, options);
32
+ return new ((_a = this.Class) !== null && _a !== void 0 ? _a : this)(store, query, options);
32
33
  }
33
34
  setOnNextResults(cb) {
34
35
  this.nextHandler = cb;
@@ -37,9 +38,15 @@ class BatchCursorImpl {
37
38
  return this.store.count(Object.assign({}, this.query));
38
39
  }
39
40
  async next() {
40
- var _a;
41
- const _b = (_a = this.options) !== null && _a !== void 0 ? _a : {}, { batchSize = 10 } = _b, rest = __rest(_b, ["batchSize"]);
42
- const matches = await this.store.find(Object.assign({}, this.query), {
41
+ var _a, _b;
42
+ const _c = (_a = this.options) !== null && _a !== void 0 ? _a : {}, { batchSize = 10 } = _c, rest = __rest(_c, ["batchSize"]);
43
+ let query = Object.assign({}, this.query);
44
+ if (this.lastId) {
45
+ query = {
46
+ $and: [(_b = this.query) !== null && _b !== void 0 ? _b : {}, { id: { $gt: this.lastId } }],
47
+ };
48
+ }
49
+ const matches = await this.store.find(query, {
43
50
  limit: batchSize,
44
51
  }, Object.assign({}, rest));
45
52
  if (matches.length === 0) {
@@ -57,7 +64,7 @@ class BatchCursorImpl {
57
64
  if (!this.query) {
58
65
  this.query = {};
59
66
  }
60
- this.query.id = { $gt: last.id };
67
+ this.lastId = last.id;
61
68
  }
62
69
  }
63
70
  }
@@ -3,14 +3,16 @@ import AbstractStore from '../stores/AbstractStore';
3
3
  import { QueryOptions } from '../types/query.types';
4
4
  import { PrepareOptions } from '../types/stores.types';
5
5
  export default class BatchCursorImpl<ResponseRecord> implements BatchCursor<ResponseRecord> {
6
+ static Class?: new (...args: any[]) => BatchCursor<any>;
6
7
  private store;
7
8
  private options?;
8
- private query?;
9
+ protected query?: Record<string, any>;
9
10
  private nextHandler?;
10
- private constructor();
11
+ private lastId?;
12
+ protected constructor(store: AbstractStore<Schema>, query?: Record<string, any>, options?: FindBatchOptions);
11
13
  [Symbol.asyncIterator](): AsyncIterator<ResponseRecord, any, undefined>;
12
14
  static Cursor<Response>(store: AbstractStore<Schema>, query?: Record<string, any>, options?: FindBatchOptions): BatchCursor<Response>;
13
- setOnNextResults(cb: (results: ResponseRecord[]) => never[]): void;
15
+ setOnNextResults(cb: OnNextResultsHandler<ResponseRecord>): void;
14
16
  getTotalRecords(): Promise<number>;
15
17
  next(): Promise<ResponseRecord[] | null>;
16
18
  private bumpCursorPosition;
@@ -34,8 +34,9 @@ export default class BatchCursorImpl {
34
34
  };
35
35
  }
36
36
  static Cursor(store, query, options) {
37
+ var _a;
37
38
  assertOptions({ store }, ['store'], 'You need to pass a store to BatchCursor.Cursor()');
38
- return new this(store, query, options);
39
+ return new ((_a = this.Class) !== null && _a !== void 0 ? _a : this)(store, query, options);
39
40
  }
40
41
  setOnNextResults(cb) {
41
42
  this.nextHandler = cb;
@@ -46,10 +47,16 @@ export default class BatchCursorImpl {
46
47
  });
47
48
  }
48
49
  next() {
49
- var _a;
50
+ var _a, _b;
50
51
  return __awaiter(this, void 0, void 0, function* () {
51
- const _b = (_a = this.options) !== null && _a !== void 0 ? _a : {}, { batchSize = 10 } = _b, rest = __rest(_b, ["batchSize"]);
52
- const matches = yield this.store.find(Object.assign({}, this.query), {
52
+ const _c = (_a = this.options) !== null && _a !== void 0 ? _a : {}, { batchSize = 10 } = _c, rest = __rest(_c, ["batchSize"]);
53
+ let query = Object.assign({}, this.query);
54
+ if (this.lastId) {
55
+ query = {
56
+ $and: [(_b = this.query) !== null && _b !== void 0 ? _b : {}, { id: { $gt: this.lastId } }],
57
+ };
58
+ }
59
+ const matches = yield this.store.find(query, {
53
60
  limit: batchSize,
54
61
  }, Object.assign({}, rest));
55
62
  if (matches.length === 0) {
@@ -68,7 +75,7 @@ export default class BatchCursorImpl {
68
75
  if (!this.query) {
69
76
  this.query = {};
70
77
  }
71
- this.query.id = { $gt: last.id };
78
+ this.lastId = last.id;
72
79
  }
73
80
  }
74
81
  }
@@ -1,7 +1,7 @@
1
1
  import { Database } from '../types/database.types';
2
- import { Store, StoreMap, StoreName, StoreOptions } from '../types/stores.types';
2
+ import { DataStore, StoreMap, StoreName, StoreOptions } from '../types/stores.types';
3
3
  interface StoreContructor {
4
- Store(o: any): Promise<Store> | Store;
4
+ Store(o: any): Promise<DataStore> | DataStore;
5
5
  }
6
6
  export default class StoreFactory {
7
7
  private storeMap;
@@ -18,7 +18,7 @@ export default class StoreFactory {
18
18
  Store<Name extends StoreName, Options extends StoreOptions<Name>>(name: Name, options?: Options): Promise<StoreMap[Name]>;
19
19
  getStoreNames(): StoreName[];
20
20
  setStoreClass(name: string, Class: StoreContructor): void;
21
- setStore(name: StoreName, store: Store | null): void;
21
+ setStore(name: StoreName, store: DataStore | null): void;
22
22
  getStore<Name extends StoreName>(name: Name): Promise<StoreMap[Name]>;
23
23
  static reset(): void;
24
24
  }
@@ -1,12 +1,12 @@
1
1
  import { Schema, SchemaFieldNames, SchemaPartialValues, SchemaPublicFieldNames, SchemaValues } from '@sprucelabs/schema';
2
2
  import { FindBatchOptions } from '../cursors/BatchCursor';
3
3
  import AbstractMutexer from '../mutexers/AbstractMutexer';
4
- import { Database } from '../types/database.types';
4
+ import { DataStorePlugin, Database } from '../types/database.types';
5
5
  import { QueryBuilder, QueryOptions } from '../types/query.types';
6
- import { PrepareOptions, PrepareResults, SaveOperations, Store } from '../types/stores.types';
6
+ import { PrepareOptions, PrepareResults, SaveOperations, DataStore } from '../types/stores.types';
7
7
  export default abstract class AbstractStore<FullSchema extends Schema, CreateSchema extends Schema = FullSchema, UpdateSchema extends Schema = CreateSchema, DatabaseSchema extends Schema = FullSchema, DatabaseRecord = SchemaValues<DatabaseSchema> & {
8
8
  id: string;
9
- }, QueryRecord = SchemaPartialValues<FullSchema>, FullRecord = SchemaValues<FullSchema>, CreateRecord = SchemaValues<CreateSchema>, UpdateRecord = SchemaValues<UpdateSchema> & SaveOperations> extends AbstractMutexer implements Store {
9
+ }, QueryRecord = SchemaPartialValues<FullSchema>, FullRecord = SchemaValues<FullSchema>, CreateRecord = SchemaValues<CreateSchema>, UpdateRecord = SchemaValues<UpdateSchema> & SaveOperations> extends AbstractMutexer implements DataStore {
10
10
  abstract readonly name: string;
11
11
  protected abstract collectionName: string;
12
12
  protected abstract createSchema: CreateSchema;
@@ -17,6 +17,7 @@ export default abstract class AbstractStore<FullSchema extends Schema, CreateSch
17
17
  protected db: Database;
18
18
  protected primaryFieldNames: string[];
19
19
  protected shouldMapLowerCaseToCamelCase: boolean;
20
+ protected plugins: DataStorePlugin[];
20
21
  initialize?(): Promise<void>;
21
22
  protected prepareRecord?<IncludePrivateFields extends boolean, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>>(record: DatabaseRecord, options?: PrepareOptions<IncludePrivateFields, FullSchema, F>): Promise<PrepareResults<FullSchema, IncludePrivateFields>>;
22
23
  protected willCreate?(values: CreateRecord): Promise<Omit<DatabaseRecord, 'id'>>;
@@ -33,7 +34,7 @@ export default abstract class AbstractStore<FullSchema extends Schema, CreateSch
33
34
  protected prepareAndNormalizeRecord<IncludePrivateFields extends boolean = true, CreateEntityInstances extends boolean = false, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>, PF extends SchemaPublicFieldNames<FullSchema> = SchemaPublicFieldNames<FullSchema>>(record: any, options?: PrepareOptions<IncludePrivateFields, FullSchema, F>): Promise<Response<FullSchema, CreateEntityInstances, IncludePrivateFields, PF, F>>;
34
35
  private mapCasing;
35
36
  create<IncludePrivateFields extends boolean = true, CreateEntityInstances extends boolean = false, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>, PF extends SchemaPublicFieldNames<FullSchema> = SchemaPublicFieldNames<FullSchema>>(values: CreateRecord[], options?: PrepareOptions<CreateEntityInstances, FullSchema, F>): Promise<Response<FullSchema, CreateEntityInstances, IncludePrivateFields, PF, F>[]>;
36
- createOne<CreateEntityInstances extends boolean, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>>(values: CreateRecord, options?: PrepareOptions<CreateEntityInstances, FullSchema, F>): Promise<SchemaValues<FullSchema, false, CreateEntityInstances, false, F, SchemaPublicFieldNames<FullSchema>>>;
37
+ createOne<CreateEntityInstances extends boolean, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>>(values: CreateRecord, options?: PrepareOptions<CreateEntityInstances, FullSchema, F>): Promise<SchemaValues<FullSchema, false, CreateEntityInstances, false, F, SchemaPublicFieldNames<FullSchema>> & {}>;
37
38
  private get primaryFieldName();
38
39
  findOne<CreateEntityInstances extends boolean, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>>(query: QueryBuilder<QueryRecord>, options?: PrepareOptions<CreateEntityInstances, FullSchema, F>): Promise<SchemaValues<FullSchema, false, CreateEntityInstances, false, F, SchemaPublicFieldNames<FullSchema>> | null>;
39
40
  count(query?: QueryBuilder<QueryRecord>): Promise<number>;
@@ -30,6 +30,7 @@ export default class AbstractStore extends AbstractMutexer {
30
30
  super();
31
31
  this.primaryFieldNames = ['id'];
32
32
  this.shouldMapLowerCaseToCamelCase = false;
33
+ this.plugins = [];
33
34
  this.db = db;
34
35
  if (collectionName) {
35
36
  this.setCollectionName(collectionName);
@@ -100,7 +101,7 @@ export default class AbstractStore extends AbstractMutexer {
100
101
  });
101
102
  }
102
103
  createOne(values, options) {
103
- var _a, _b;
104
+ var _a, _b, _c;
104
105
  return __awaiter(this, void 0, void 0, function* () {
105
106
  try {
106
107
  //@ts-ignore
@@ -114,10 +115,15 @@ export default class AbstractStore extends AbstractMutexer {
114
115
  const toSave = normalizeSchemaValues(this.databaseSchema,
115
116
  //@ts-ignore
116
117
  databaseRecord, { shouldCreateEntityInstances: false });
118
+ let mixinValuesOnReturn = {};
119
+ for (const plugin of this.plugins) {
120
+ const r = yield ((_b = plugin.willCreateOne) === null || _b === void 0 ? void 0 : _b.call(plugin, toSave));
121
+ mixinValuesOnReturn = Object.assign(Object.assign({}, mixinValuesOnReturn), r === null || r === void 0 ? void 0 : r.valuesToMixinBeforeReturning);
122
+ }
117
123
  const record = yield this.db.createOne(this.collectionName, toSave);
118
- yield ((_b = this.didCreate) === null || _b === void 0 ? void 0 : _b.call(this, record));
124
+ yield ((_c = this.didCreate) === null || _c === void 0 ? void 0 : _c.call(this, record));
119
125
  const normalized = yield this.prepareAndNormalizeRecord(record, options);
120
- return normalized;
126
+ return Object.assign(Object.assign({}, normalized), mixinValuesOnReturn);
121
127
  }
122
128
  catch (err) {
123
129
  const coded = errorUtil.transformToSpruceErrors(err, new SpruceError({
@@ -213,7 +219,7 @@ export default class AbstractStore extends AbstractMutexer {
213
219
  });
214
220
  }
215
221
  findOneAndUpdate(query, updates, notFoundHandler, options = {}) {
216
- var _a;
222
+ var _a, _b;
217
223
  return __awaiter(this, void 0, void 0, function* () {
218
224
  const { ops, updates: initialUpdates } = this.pluckOperations(updates);
219
225
  try {
@@ -245,8 +251,11 @@ export default class AbstractStore extends AbstractMutexer {
245
251
  for (const { name, value } of ops) {
246
252
  normalizedValues[name] = value;
247
253
  }
254
+ for (const plugin of this.plugins) {
255
+ yield ((_a = plugin.willUpdateOne) === null || _a === void 0 ? void 0 : _a.call(plugin, query, normalizedValues));
256
+ }
248
257
  const results = yield this.db.updateOne(this.collectionName, query, normalizedValues);
249
- yield ((_a = this.didUpdate) === null || _a === void 0 ? void 0 : _a.call(this, current, results));
258
+ yield ((_b = this.didUpdate) === null || _b === void 0 ? void 0 : _b.call(this, current, results));
250
259
  return this.prepareAndNormalizeRecord(results, options);
251
260
  }
252
261
  catch (err) {
@@ -1,5 +1,5 @@
1
1
  import { Database, Index, TestConnect, UniqueIndex } from '../types/database.types';
2
- import { Store } from '../types/stores.types';
2
+ import { DataStore } from '../types/stores.types';
3
3
  declare const databaseAssertUtil: {
4
4
  collectionName: string;
5
5
  runSuite(connect: TestConnect, tests?: string[]): Promise<void>;
@@ -79,6 +79,6 @@ declare const databaseAssertUtil: {
79
79
  _assertThrowsExpectedNotFoundOnUpdateOne(db: Database, query: Record<string, any>): Promise<void>;
80
80
  _assert$orReturnsExpectedTotalRecords(db: Database, $or: Record<string, any>[], expected: number): Promise<void>;
81
81
  _assertCanCreateMultiFieldIndex(connect: TestConnect, fields: string[]): Promise<void>;
82
- assertHasLowerCaseToCamelCaseMappingEnabled(store: Store): void;
82
+ assertHasLowerCaseToCamelCaseMappingEnabled(store: DataStore): void;
83
83
  };
84
84
  export default databaseAssertUtil;
@@ -39,3 +39,14 @@ export type TestConnect = (connectionString?: string, dbName?: string) => Promis
39
39
  connectionStringWithRandomBadDatabaseName: string;
40
40
  badDatabaseName: string;
41
41
  }>;
42
+ export interface DataStorePlugin {
43
+ willCreateOne?: (values: Record<string, any>) => Promise<void | DataStorePluginHookResponse>;
44
+ willUpdateOne?: (query: Record<string, any>, updates: Record<string, any>) => Promise<void | DataStorePluginWillUpdateOneResponse>;
45
+ getName(): string;
46
+ }
47
+ export interface DataStorePluginHookResponse {
48
+ valuesToMixinBeforeReturning?: Record<string, any>;
49
+ }
50
+ export interface DataStorePluginWillUpdateOneResponse {
51
+ query?: Record<string, any>;
52
+ }
@@ -8,8 +8,9 @@ export interface UniversalStoreOptions {
8
8
  db: Database;
9
9
  storeFactory: StoreFactory;
10
10
  }
11
- export interface Store {
11
+ export interface DataStore {
12
12
  initialize?(): Promise<void>;
13
+ getCollectionName?(): string;
13
14
  }
14
15
  /**
15
16
  * @deprecated SimplifiedStoreFactory -> SimpleStoreFactory
@@ -15,7 +15,10 @@ const mongoUtil = {
15
15
  mapQuery(query, options = {}) {
16
16
  const q = query || {};
17
17
  const opts = options;
18
- if (Object.prototype.hasOwnProperty.call(q, 'id') && q.id === undefined) {
18
+ const shouldTransformIdToObjectId = opts.shouldTransformToObjectId !== false;
19
+ if (Object.prototype.hasOwnProperty.call(q, 'id') &&
20
+ q.id === undefined &&
21
+ shouldTransformIdToObjectId) {
19
22
  throw new SpruceError({
20
23
  code: 'MONGO_ID_MAPPING_ERROR',
21
24
  friendlyMessage: '`id` cannot be undefined',
@@ -30,8 +33,9 @@ const mongoUtil = {
30
33
  normalizedValues.$or = $or.map((o) => this.mapQuery(o, options));
31
34
  }
32
35
  else if (typeof id === 'string') {
33
- normalizedValues._id =
34
- opts.shouldTransformToObjectId === false ? id : new ObjectId(id);
36
+ normalizedValues._id = !shouldTransformIdToObjectId
37
+ ? id
38
+ : new ObjectId(id);
35
39
  }
36
40
  else if (id) {
37
41
  normalizedValues._id = mapNestedIdValues(id, options);
@@ -1,7 +1,7 @@
1
1
  import { Database } from '../types/database.types';
2
- import { Store, StoreMap, StoreName, StoreOptions } from '../types/stores.types';
2
+ import { DataStore, StoreMap, StoreName, StoreOptions } from '../types/stores.types';
3
3
  interface StoreContructor {
4
- Store(o: any): Promise<Store> | Store;
4
+ Store(o: any): Promise<DataStore> | DataStore;
5
5
  }
6
6
  export default class StoreFactory {
7
7
  private storeMap;
@@ -18,7 +18,7 @@ export default class StoreFactory {
18
18
  Store<Name extends StoreName, Options extends StoreOptions<Name>>(name: Name, options?: Options): Promise<StoreMap[Name]>;
19
19
  getStoreNames(): StoreName[];
20
20
  setStoreClass(name: string, Class: StoreContructor): void;
21
- setStore(name: StoreName, store: Store | null): void;
21
+ setStore(name: StoreName, store: DataStore | null): void;
22
22
  getStore<Name extends StoreName>(name: Name): Promise<StoreMap[Name]>;
23
23
  static reset(): void;
24
24
  }
@@ -1,12 +1,12 @@
1
1
  import { Schema, SchemaFieldNames, SchemaPartialValues, SchemaPublicFieldNames, SchemaValues } from '@sprucelabs/schema';
2
2
  import { FindBatchOptions } from '../cursors/BatchCursor';
3
3
  import AbstractMutexer from '../mutexers/AbstractMutexer';
4
- import { Database } from '../types/database.types';
4
+ import { DataStorePlugin, Database } from '../types/database.types';
5
5
  import { QueryBuilder, QueryOptions } from '../types/query.types';
6
- import { PrepareOptions, PrepareResults, SaveOperations, Store } from '../types/stores.types';
6
+ import { PrepareOptions, PrepareResults, SaveOperations, DataStore } from '../types/stores.types';
7
7
  export default abstract class AbstractStore<FullSchema extends Schema, CreateSchema extends Schema = FullSchema, UpdateSchema extends Schema = CreateSchema, DatabaseSchema extends Schema = FullSchema, DatabaseRecord = SchemaValues<DatabaseSchema> & {
8
8
  id: string;
9
- }, QueryRecord = SchemaPartialValues<FullSchema>, FullRecord = SchemaValues<FullSchema>, CreateRecord = SchemaValues<CreateSchema>, UpdateRecord = SchemaValues<UpdateSchema> & SaveOperations> extends AbstractMutexer implements Store {
9
+ }, QueryRecord = SchemaPartialValues<FullSchema>, FullRecord = SchemaValues<FullSchema>, CreateRecord = SchemaValues<CreateSchema>, UpdateRecord = SchemaValues<UpdateSchema> & SaveOperations> extends AbstractMutexer implements DataStore {
10
10
  abstract readonly name: string;
11
11
  protected abstract collectionName: string;
12
12
  protected abstract createSchema: CreateSchema;
@@ -17,6 +17,7 @@ export default abstract class AbstractStore<FullSchema extends Schema, CreateSch
17
17
  protected db: Database;
18
18
  protected primaryFieldNames: string[];
19
19
  protected shouldMapLowerCaseToCamelCase: boolean;
20
+ protected plugins: DataStorePlugin[];
20
21
  initialize?(): Promise<void>;
21
22
  protected prepareRecord?<IncludePrivateFields extends boolean, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>>(record: DatabaseRecord, options?: PrepareOptions<IncludePrivateFields, FullSchema, F>): Promise<PrepareResults<FullSchema, IncludePrivateFields>>;
22
23
  protected willCreate?(values: CreateRecord): Promise<Omit<DatabaseRecord, 'id'>>;
@@ -33,7 +34,7 @@ export default abstract class AbstractStore<FullSchema extends Schema, CreateSch
33
34
  protected prepareAndNormalizeRecord<IncludePrivateFields extends boolean = true, CreateEntityInstances extends boolean = false, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>, PF extends SchemaPublicFieldNames<FullSchema> = SchemaPublicFieldNames<FullSchema>>(record: any, options?: PrepareOptions<IncludePrivateFields, FullSchema, F>): Promise<Response<FullSchema, CreateEntityInstances, IncludePrivateFields, PF, F>>;
34
35
  private mapCasing;
35
36
  create<IncludePrivateFields extends boolean = true, CreateEntityInstances extends boolean = false, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>, PF extends SchemaPublicFieldNames<FullSchema> = SchemaPublicFieldNames<FullSchema>>(values: CreateRecord[], options?: PrepareOptions<CreateEntityInstances, FullSchema, F>): Promise<Response<FullSchema, CreateEntityInstances, IncludePrivateFields, PF, F>[]>;
36
- createOne<CreateEntityInstances extends boolean, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>>(values: CreateRecord, options?: PrepareOptions<CreateEntityInstances, FullSchema, F>): Promise<SchemaValues<FullSchema, false, CreateEntityInstances, false, F, SchemaPublicFieldNames<FullSchema>>>;
37
+ createOne<CreateEntityInstances extends boolean, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>>(values: CreateRecord, options?: PrepareOptions<CreateEntityInstances, FullSchema, F>): Promise<SchemaValues<FullSchema, false, CreateEntityInstances, false, F, SchemaPublicFieldNames<FullSchema>> & {}>;
37
38
  private get primaryFieldName();
38
39
  findOne<CreateEntityInstances extends boolean, F extends SchemaFieldNames<FullSchema> = SchemaFieldNames<FullSchema>>(query: QueryBuilder<QueryRecord>, options?: PrepareOptions<CreateEntityInstances, FullSchema, F>): Promise<SchemaValues<FullSchema, false, CreateEntityInstances, false, F, SchemaPublicFieldNames<FullSchema>> | null>;
39
40
  count(query?: QueryBuilder<QueryRecord>): Promise<number>;
@@ -49,6 +49,7 @@ class AbstractStore extends AbstractMutexer_1.default {
49
49
  super();
50
50
  this.primaryFieldNames = ['id'];
51
51
  this.shouldMapLowerCaseToCamelCase = false;
52
+ this.plugins = [];
52
53
  this.db = db;
53
54
  if (collectionName) {
54
55
  this.setCollectionName(collectionName);
@@ -115,7 +116,7 @@ class AbstractStore extends AbstractMutexer_1.default {
115
116
  }
116
117
  }
117
118
  async createOne(values, options) {
118
- var _a, _b;
119
+ var _a, _b, _c;
119
120
  try {
120
121
  //@ts-ignore
121
122
  (0, schema_1.validateSchemaValues)(this.createSchema, values);
@@ -128,10 +129,15 @@ class AbstractStore extends AbstractMutexer_1.default {
128
129
  const toSave = (0, schema_1.normalizeSchemaValues)(this.databaseSchema,
129
130
  //@ts-ignore
130
131
  databaseRecord, { shouldCreateEntityInstances: false });
132
+ let mixinValuesOnReturn = {};
133
+ for (const plugin of this.plugins) {
134
+ const r = await ((_b = plugin.willCreateOne) === null || _b === void 0 ? void 0 : _b.call(plugin, toSave));
135
+ mixinValuesOnReturn = Object.assign(Object.assign({}, mixinValuesOnReturn), r === null || r === void 0 ? void 0 : r.valuesToMixinBeforeReturning);
136
+ }
131
137
  const record = await this.db.createOne(this.collectionName, toSave);
132
- await ((_b = this.didCreate) === null || _b === void 0 ? void 0 : _b.call(this, record));
138
+ await ((_c = this.didCreate) === null || _c === void 0 ? void 0 : _c.call(this, record));
133
139
  const normalized = await this.prepareAndNormalizeRecord(record, options);
134
- return normalized;
140
+ return Object.assign(Object.assign({}, normalized), mixinValuesOnReturn);
135
141
  }
136
142
  catch (err) {
137
143
  const coded = error_utility_1.default.transformToSpruceErrors(err, new SpruceError_1.default({
@@ -212,7 +218,7 @@ class AbstractStore extends AbstractMutexer_1.default {
212
218
  return this.db.update(this.collectionName, query, updates);
213
219
  }
214
220
  async findOneAndUpdate(query, updates, notFoundHandler, options = {}) {
215
- var _a;
221
+ var _a, _b;
216
222
  const { ops, updates: initialUpdates } = this.pluckOperations(updates);
217
223
  try {
218
224
  const isScrambled = this.isScrambled(initialUpdates);
@@ -243,8 +249,11 @@ class AbstractStore extends AbstractMutexer_1.default {
243
249
  for (const { name, value } of ops) {
244
250
  normalizedValues[name] = value;
245
251
  }
252
+ for (const plugin of this.plugins) {
253
+ await ((_a = plugin.willUpdateOne) === null || _a === void 0 ? void 0 : _a.call(plugin, query, normalizedValues));
254
+ }
246
255
  const results = await this.db.updateOne(this.collectionName, query, normalizedValues);
247
- await ((_a = this.didUpdate) === null || _a === void 0 ? void 0 : _a.call(this, current, results));
256
+ await ((_b = this.didUpdate) === null || _b === void 0 ? void 0 : _b.call(this, current, results));
248
257
  return this.prepareAndNormalizeRecord(results, options);
249
258
  }
250
259
  catch (err) {
@@ -1,5 +1,5 @@
1
1
  import { Database, Index, TestConnect, UniqueIndex } from '../types/database.types';
2
- import { Store } from '../types/stores.types';
2
+ import { DataStore } from '../types/stores.types';
3
3
  declare const databaseAssertUtil: {
4
4
  collectionName: string;
5
5
  runSuite(connect: TestConnect, tests?: string[]): Promise<void>;
@@ -79,6 +79,6 @@ declare const databaseAssertUtil: {
79
79
  _assertThrowsExpectedNotFoundOnUpdateOne(db: Database, query: Record<string, any>): Promise<void>;
80
80
  _assert$orReturnsExpectedTotalRecords(db: Database, $or: Record<string, any>[], expected: number): Promise<void>;
81
81
  _assertCanCreateMultiFieldIndex(connect: TestConnect, fields: string[]): Promise<void>;
82
- assertHasLowerCaseToCamelCaseMappingEnabled(store: Store): void;
82
+ assertHasLowerCaseToCamelCaseMappingEnabled(store: DataStore): void;
83
83
  };
84
84
  export default databaseAssertUtil;
@@ -39,3 +39,14 @@ export type TestConnect = (connectionString?: string, dbName?: string) => Promis
39
39
  connectionStringWithRandomBadDatabaseName: string;
40
40
  badDatabaseName: string;
41
41
  }>;
42
+ export interface DataStorePlugin {
43
+ willCreateOne?: (values: Record<string, any>) => Promise<void | DataStorePluginHookResponse>;
44
+ willUpdateOne?: (query: Record<string, any>, updates: Record<string, any>) => Promise<void | DataStorePluginWillUpdateOneResponse>;
45
+ getName(): string;
46
+ }
47
+ export interface DataStorePluginHookResponse {
48
+ valuesToMixinBeforeReturning?: Record<string, any>;
49
+ }
50
+ export interface DataStorePluginWillUpdateOneResponse {
51
+ query?: Record<string, any>;
52
+ }
@@ -8,8 +8,9 @@ export interface UniversalStoreOptions {
8
8
  db: Database;
9
9
  storeFactory: StoreFactory;
10
10
  }
11
- export interface Store {
11
+ export interface DataStore {
12
12
  initialize?(): Promise<void>;
13
+ getCollectionName?(): string;
13
14
  }
14
15
  /**
15
16
  * @deprecated SimplifiedStoreFactory -> SimpleStoreFactory
@@ -20,7 +20,10 @@ const mongoUtil = {
20
20
  mapQuery(query, options = {}) {
21
21
  const q = query || {};
22
22
  const opts = options;
23
- if (Object.prototype.hasOwnProperty.call(q, 'id') && q.id === undefined) {
23
+ const shouldTransformIdToObjectId = opts.shouldTransformToObjectId !== false;
24
+ if (Object.prototype.hasOwnProperty.call(q, 'id') &&
25
+ q.id === undefined &&
26
+ shouldTransformIdToObjectId) {
24
27
  throw new SpruceError_1.default({
25
28
  code: 'MONGO_ID_MAPPING_ERROR',
26
29
  friendlyMessage: '`id` cannot be undefined',
@@ -35,8 +38,9 @@ const mongoUtil = {
35
38
  normalizedValues.$or = $or.map((o) => this.mapQuery(o, options));
36
39
  }
37
40
  else if (typeof id === 'string') {
38
- normalizedValues._id =
39
- opts.shouldTransformToObjectId === false ? id : new mongodb_1.ObjectId(id);
41
+ normalizedValues._id = !shouldTransformIdToObjectId
42
+ ? id
43
+ : new mongodb_1.ObjectId(id);
40
44
  }
41
45
  else if (id) {
42
46
  normalizedValues._id = mapNestedIdValues(id, options);
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "25.7.6",
6
+ "version": "25.7.8",
7
7
  "files": [
8
8
  "build/**/*",
9
9
  "!build/__tests__",