@travetto/model 4.1.0 → 4.1.3

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 ArcSine Technologies
3
+ Copyright (c) 2020 ArcSine Technologies
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/model",
3
- "version": "4.1.0",
3
+ "version": "4.1.3",
4
4
  "description": "Datastore abstraction for core operations.",
5
5
  "keywords": [
6
6
  "datastore",
@@ -26,14 +26,14 @@
26
26
  "directory": "module/model"
27
27
  },
28
28
  "dependencies": {
29
- "@travetto/config": "^4.1.0",
30
- "@travetto/di": "^4.1.0",
31
- "@travetto/registry": "^4.1.0",
32
- "@travetto/schema": "^4.1.0"
29
+ "@travetto/config": "^4.1.1",
30
+ "@travetto/di": "^4.1.1",
31
+ "@travetto/registry": "^4.1.1",
32
+ "@travetto/schema": "^4.1.1"
33
33
  },
34
34
  "peerDependencies": {
35
- "@travetto/cli": "^4.1.0",
36
- "@travetto/test": "^4.1.0"
35
+ "@travetto/cli": "^4.1.1",
36
+ "@travetto/test": "^4.1.1"
37
37
  },
38
38
  "peerDependenciesMeta": {
39
39
  "@travetto/cli": {
@@ -8,7 +8,7 @@ import { ModelIdSource, ModelType, OptionalId } from '../../types/model';
8
8
  import { NotFoundError } from '../../error/not-found';
9
9
  import { ExistsError } from '../../error/exists';
10
10
  import { SubTypeNotSupportedError } from '../../error/invalid-sub-type';
11
- import { DataHandler } from '../../registry/types';
11
+ import { DataHandler, PrePersistScope } from '../../registry/types';
12
12
 
13
13
  export type ModelCrudProvider = {
14
14
  idSource: ModelIdSource;
@@ -72,7 +72,7 @@ export class ModelCrudUtil {
72
72
  * @param cls Type to store for
73
73
  * @param item Item to store
74
74
  */
75
- static async preStore<T extends ModelType>(cls: Class<T>, item: Partial<OptionalId<T>>, provider: ModelCrudProvider): Promise<T> {
75
+ static async preStore<T extends ModelType>(cls: Class<T>, item: Partial<OptionalId<T>>, provider: ModelCrudProvider, scope: PrePersistScope = 'all'): Promise<T> {
76
76
  if (!item.id) {
77
77
  item.id = provider.idSource.create();
78
78
  }
@@ -88,7 +88,7 @@ export class ModelCrudUtil {
88
88
  SchemaRegistry.ensureInstanceTypeField(cls, item);
89
89
  }
90
90
 
91
- item = await this.prePersist(cls, item);
91
+ item = await this.prePersist(cls, item, scope);
92
92
 
93
93
  let errors: ValidationError[] = [];
94
94
  try {
@@ -137,7 +137,7 @@ export class ModelCrudUtil {
137
137
 
138
138
  item = Object.assign(existing, item);
139
139
 
140
- item = await this.prePersist(cls, item);
140
+ item = await this.prePersist(cls, item, 'partial');
141
141
 
142
142
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
143
143
  return item as T;
@@ -155,11 +155,14 @@ export class ModelCrudUtil {
155
155
  /**
156
156
  * Pre persist behavior
157
157
  */
158
- static async prePersist<T>(cls: Class<T>, item: T): Promise<T> {
158
+ static async prePersist<T>(cls: Class<T>, item: T, scope: PrePersistScope): Promise<T> {
159
159
  const config = ModelRegistry.get(cls);
160
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
161
- for (const handler of (config.prePersist ?? []) as unknown as DataHandler<T>[]) {
162
- item = await handler(item) ?? item;
160
+ for (const state of (config.prePersist ?? [])) {
161
+ if (state.scope === scope || scope === 'all' || state.scope === 'all') {
162
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
163
+ const handler = state.handler as unknown as DataHandler<T>;
164
+ item = await handler(item) ?? item;
165
+ }
163
166
  }
164
167
  if (typeof item === 'object' && item && 'prePersist' in item && typeof item['prePersist'] === 'function') {
165
168
  item = await item.prePersist() ?? item;
@@ -27,7 +27,7 @@ type StoreType = Map<string, Buffer>;
27
27
 
28
28
  @Config('model.memory')
29
29
  export class MemoryModelConfig {
30
- autoCreate?: boolean;
30
+ autoCreate?: boolean = true;
31
31
  namespace?: string;
32
32
  cullRate?: number | TimeSpan;
33
33
  }
@@ -105,6 +105,9 @@ export class MemoryModelService implements ModelCrudSupport, ModelStreamSupport,
105
105
  let index = this.#indices[idx.type].get(idxName)?.get(key);
106
106
 
107
107
  if (!index) {
108
+ if (!this.#indices[idx.type].has(idxName)) {
109
+ this.#indices[idx.type].set(idxName, new Map());
110
+ }
108
111
  if (idx.type === 'sorted') {
109
112
  this.#indices[idx.type].get(idxName)!.set(key, index = new Map());
110
113
  } else {
@@ -3,7 +3,7 @@ import { SchemaRegistry } from '@travetto/schema';
3
3
 
4
4
  import { ModelType } from '../types/model';
5
5
  import { ModelRegistry } from './model';
6
- import { DataHandler, IndexConfig, ModelOptions } from './types';
6
+ import { DataHandler, IndexConfig, ModelOptions, PrePersistScope } from './types';
7
7
 
8
8
  /**
9
9
  * Model decorator, extends `@Schema`
@@ -45,27 +45,33 @@ export function ExpiresAt() {
45
45
  /**
46
46
  * Model class decorator for pre-persist behavior
47
47
  */
48
- export function PrePersist<T>(handler: DataHandler<T>) {
48
+ export function PrePersist<T>(handler: DataHandler<T>, scope: PrePersistScope = 'all') {
49
49
  return function (tgt: Class<T>): void {
50
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
51
- ModelRegistry.registerDataHandlers(tgt, { prePersist: [handler as DataHandler] });
50
+ ModelRegistry.registerDataHandlers(tgt, {
51
+ prePersist: [{
52
+ scope,
53
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
54
+ handler: handler as DataHandler
55
+ }]
56
+ });
52
57
  };
53
58
  }
54
59
 
55
60
  /**
56
61
  * Model field decorator for pre-persist value setting
57
62
  */
58
- export function PersistValue<T>(handler: (curr: T | undefined) => T) {
63
+ export function PersistValue<T>(handler: (curr: T | undefined) => T, scope: PrePersistScope = 'all') {
59
64
  return function <K extends string, C extends Partial<Record<K, T>>>(tgt: C, prop: K): void {
60
65
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
61
66
  ModelRegistry.registerDataHandlers(tgt.constructor as Class<C>, {
62
- prePersist: [
63
- (inst): void => {
67
+ prePersist: [{
68
+ scope,
69
+ handler: (inst): void => {
64
70
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
65
71
  const cInst = (inst as unknown as Record<K, T>);
66
72
  cInst[prop] = handler(cInst[prop]);
67
73
  }
68
- ]
74
+ }]
69
75
  });
70
76
  };
71
77
  }
@@ -23,6 +23,8 @@ type IndexClauseRaw<T> = {
23
23
 
24
24
  export type DataHandler<T = unknown> = (inst: T) => (Promise<T | void> | T | void);
25
25
 
26
+ export type PrePersistScope = 'full' | 'partial' | 'all';
27
+
26
28
  /**
27
29
  * Model options
28
30
  */
@@ -62,7 +64,7 @@ export class ModelOptions<T extends ModelType = ModelType> {
62
64
  /**
63
65
  * Pre-persist handlers
64
66
  */
65
- prePersist?: DataHandler<unknown>[];
67
+ prePersist?: { scope: PrePersistScope, handler: DataHandler<unknown> }[];
66
68
  /**
67
69
  * Post-load handlers
68
70
  */
@@ -1,8 +1,8 @@
1
1
  import assert from 'node:assert';
2
2
 
3
3
  import { Suite, Test } from '@travetto/test';
4
- import { Schema, Text, Precision, } from '@travetto/schema';
5
- import { ModelCrudSupport, Model, NotFoundError } from '@travetto/model';
4
+ import { Schema, Text, Precision, Required, } from '@travetto/schema';
5
+ import { ModelCrudSupport, Model, NotFoundError, PersistValue } from '@travetto/model';
6
6
 
7
7
  import { BaseModelSuite } from './base';
8
8
 
@@ -55,7 +55,14 @@ class User2 {
55
55
  @Model()
56
56
  class Dated {
57
57
  id: string;
58
- time?: Date;
58
+
59
+ @PersistValue(v => v ?? new Date(), 'full')
60
+ @Required(false)
61
+ createdDate: Date;
62
+
63
+ @PersistValue(v => new Date())
64
+ @Required(false)
65
+ updatedDate: Date;
59
66
  }
60
67
 
61
68
  @Suite()
@@ -204,11 +211,29 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
204
211
  @Test('verify dates')
205
212
  async testDates() {
206
213
  const service = await this.service;
207
- const res = await service.create(Dated, Dated.from({ time: new Date() }));
214
+ const res = await service.create(Dated, Dated.from({ createdDate: new Date() }));
215
+
216
+ assert(res.createdDate instanceof Date);
217
+ }
208
218
 
209
- assert(res.time instanceof Date);
219
+ @Test('verify prepersist on create/update')
220
+ async testPrePersist() {
221
+ const service = await this.service;
222
+ const res = await service.create(Dated, Dated.from({}));
223
+ const created = res.createdDate;
224
+ assert(res.createdDate instanceof Date);
225
+ assert(res.updatedDate instanceof Date);
226
+
227
+ await new Promise(r => setTimeout(r, 100));
228
+
229
+ const final = await service.updatePartial(Dated, { id: res.id });
230
+ assert(final.createdDate instanceof Date);
231
+ assert(final.createdDate.getTime() === created?.getTime());
232
+ assert(final.updatedDate instanceof Date);
233
+ assert(final.createdDate.getTime() < final.updatedDate?.getTime());
210
234
  }
211
235
 
236
+
212
237
  @Test('verify list')
213
238
  async list() {
214
239
  const service = await this.service;