@travetto/model 8.0.0-alpha.9 → 8.0.0
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/README.md +91 -102
- package/__index__.ts +7 -11
- package/package.json +18 -18
- package/src/error/exists.ts +1 -1
- package/src/error/invalid-index.ts +5 -3
- package/src/error/invalid-sub-type.ts +1 -1
- package/src/error/not-found.ts +1 -1
- package/src/error/unique.ts +13 -0
- package/src/registry/decorator.ts +38 -32
- package/src/registry/registry-adapter.ts +11 -8
- package/src/registry/registry-index.ts +12 -33
- package/src/registry/types.ts +24 -39
- package/src/types/basic.ts +3 -3
- package/src/types/blob.ts +6 -6
- package/src/types/bulk.ts +5 -9
- package/src/types/crud.ts +9 -6
- package/src/types/expiry.ts +2 -2
- package/src/types/model.ts +11 -2
- package/src/types/storage.ts +2 -3
- package/src/util/blob.ts +2 -2
- package/src/util/bulk.ts +6 -3
- package/src/util/crud.ts +105 -22
- package/src/util/expiry.ts +4 -5
- package/src/util/storage.ts +3 -4
- package/support/base-command.ts +3 -5
- package/support/bin/candidate.ts +8 -11
- package/support/bin/export.ts +2 -2
- package/support/bin/install.ts +2 -2
- package/support/cli.model_export.ts +9 -5
- package/support/cli.model_install.ts +9 -5
- package/support/doc.support.tsx +16 -20
- package/support/test/base.ts +14 -12
- package/support/test/basic.ts +44 -3
- package/support/test/blob.ts +8 -9
- package/support/test/bulk.ts +16 -5
- package/support/test/crud.ts +134 -82
- package/support/test/expiry.ts +39 -21
- package/support/test/polymorphism.ts +43 -143
- package/support/test/suite.ts +15 -12
- package/src/types/indexed.ts +0 -43
- package/src/util/indexed.ts +0 -144
- package/support/test/indexed.ts +0 -190
package/support/test/crud.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
2
|
import timers from 'node:timers/promises';
|
|
3
3
|
|
|
4
|
+
import { Model, type ModelCrudSupport, NotFoundError, PersistValue, PrePersist } from '@travetto/model';
|
|
5
|
+
import { Precision, Required, Schema, Text } from '@travetto/schema';
|
|
4
6
|
import { Suite, Test } from '@travetto/test';
|
|
5
|
-
import { Schema, Text, Precision, Required, } from '@travetto/schema';
|
|
6
|
-
import { type ModelCrudSupport, Model, NotFoundError, PersistValue } from '@travetto/model';
|
|
7
7
|
|
|
8
8
|
import { BaseModelSuite } from './base.ts';
|
|
9
9
|
|
|
@@ -42,14 +42,13 @@ class SimpleList {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
@Model()
|
|
45
|
+
@PrePersist(item => {
|
|
46
|
+
item.name = `${item.name}-suffix`;
|
|
47
|
+
})
|
|
45
48
|
class User2 {
|
|
46
49
|
id: string;
|
|
47
50
|
address?: Address;
|
|
48
51
|
name: string;
|
|
49
|
-
|
|
50
|
-
prePersist() {
|
|
51
|
-
this.name = `${this.name}-suffix`;
|
|
52
|
-
}
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
@Model()
|
|
@@ -76,26 +75,27 @@ class BigIntModel {
|
|
|
76
75
|
|
|
77
76
|
@Suite()
|
|
78
77
|
export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
78
|
+
indexLimitSkew = 0;
|
|
79
79
|
|
|
80
80
|
@Test('save it')
|
|
81
81
|
async save() {
|
|
82
82
|
const service = await this.service;
|
|
83
83
|
|
|
84
|
-
const people = [1, 2, 3, 8].map(x =>
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
await Promise.all(
|
|
96
|
-
people.map(el => service.upsert(Person, el))
|
|
84
|
+
const people = [1, 2, 3, 8].map(x =>
|
|
85
|
+
Person.from({
|
|
86
|
+
id: service.idSource.create(),
|
|
87
|
+
name: 'Bob',
|
|
88
|
+
age: 20 + x,
|
|
89
|
+
gender: 'm',
|
|
90
|
+
address: {
|
|
91
|
+
street1: 'a',
|
|
92
|
+
...(x === 1 ? { street2: 'b' } : {})
|
|
93
|
+
}
|
|
94
|
+
})
|
|
97
95
|
);
|
|
98
96
|
|
|
97
|
+
await Promise.all(people.map(el => service.upsert(Person, el)));
|
|
98
|
+
|
|
99
99
|
const single = await service.get(Person, people[2].id);
|
|
100
100
|
assert(single !== undefined);
|
|
101
101
|
assert(single.age === 23);
|
|
@@ -121,15 +121,18 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
121
121
|
@Test('Verify partial update with field removal')
|
|
122
122
|
async testPartialUpdate() {
|
|
123
123
|
const service = await this.service;
|
|
124
|
-
const o = await service.create(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
124
|
+
const o = await service.create(
|
|
125
|
+
Person,
|
|
126
|
+
Person.from({
|
|
127
|
+
name: 'bob',
|
|
128
|
+
age: 20,
|
|
129
|
+
gender: 'm',
|
|
130
|
+
address: {
|
|
131
|
+
street1: 'road',
|
|
132
|
+
street2: 'roader'
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
);
|
|
133
136
|
assert(o.id);
|
|
134
137
|
assert(o.name === 'bob');
|
|
135
138
|
|
|
@@ -146,7 +149,7 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
146
149
|
id: o2.id,
|
|
147
150
|
gender: 'f',
|
|
148
151
|
address: {
|
|
149
|
-
street1: 'changed\n'
|
|
152
|
+
street1: 'changed\n'
|
|
150
153
|
}
|
|
151
154
|
});
|
|
152
155
|
|
|
@@ -168,20 +171,23 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
168
171
|
@Test('Verify partial update with field removal and lists')
|
|
169
172
|
async testPartialUpdateList() {
|
|
170
173
|
const service = await this.service;
|
|
171
|
-
const o = await service.create(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
174
|
+
const o = await service.create(
|
|
175
|
+
SimpleList,
|
|
176
|
+
SimpleList.from({
|
|
177
|
+
names: ['a', 'b', 'c'],
|
|
178
|
+
simples: [
|
|
179
|
+
{
|
|
180
|
+
name: 'a'
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: 'b'
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: 'c'
|
|
187
|
+
}
|
|
188
|
+
]
|
|
189
|
+
})
|
|
190
|
+
);
|
|
185
191
|
|
|
186
192
|
const o2 = await service.updatePartial(SimpleList, {
|
|
187
193
|
id: o.id,
|
|
@@ -196,9 +202,12 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
196
202
|
@Test('Verify partial update with field removal and lists')
|
|
197
203
|
async testBlankPartialUpdate() {
|
|
198
204
|
const service = await this.service;
|
|
199
|
-
const o = await service.create(
|
|
200
|
-
|
|
201
|
-
|
|
205
|
+
const o = await service.create(
|
|
206
|
+
User2,
|
|
207
|
+
User2.from({
|
|
208
|
+
name: 'bob'
|
|
209
|
+
})
|
|
210
|
+
);
|
|
202
211
|
|
|
203
212
|
assert(o.address === undefined);
|
|
204
213
|
assert(o.name === 'bob-suffix');
|
|
@@ -246,21 +255,21 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
246
255
|
async list() {
|
|
247
256
|
const service = await this.service;
|
|
248
257
|
|
|
249
|
-
const people = [1, 2, 3].map(x =>
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
await Promise.all(
|
|
261
|
-
people.map(el => service.upsert(Person, el))
|
|
258
|
+
const people = [1, 2, 3].map(x =>
|
|
259
|
+
Person.from({
|
|
260
|
+
id: service.idSource.create(),
|
|
261
|
+
name: 'Bob',
|
|
262
|
+
age: 20 + x,
|
|
263
|
+
gender: 'm',
|
|
264
|
+
address: {
|
|
265
|
+
street1: 'a',
|
|
266
|
+
...(x === 1 ? { street2: 'b' } : {})
|
|
267
|
+
}
|
|
268
|
+
})
|
|
262
269
|
);
|
|
263
270
|
|
|
271
|
+
await Promise.all(people.map(el => service.upsert(Person, el)));
|
|
272
|
+
|
|
264
273
|
const found = (await this.toArray(service.list(Person))).toSorted((a, b) => a.age - b.age);
|
|
265
274
|
|
|
266
275
|
assert(found[0].age === people[0].age);
|
|
@@ -268,20 +277,60 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
268
277
|
assert(found[2].age === people[2].age);
|
|
269
278
|
}
|
|
270
279
|
|
|
280
|
+
@Test('verify list abort signal')
|
|
281
|
+
async listAbortSignal() {
|
|
282
|
+
const service = await this.service;
|
|
283
|
+
|
|
284
|
+
await Promise.all(
|
|
285
|
+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(x =>
|
|
286
|
+
service.upsert(
|
|
287
|
+
Person,
|
|
288
|
+
Person.from({
|
|
289
|
+
id: service.idSource.create(),
|
|
290
|
+
name: 'Bob',
|
|
291
|
+
age: 20 + x,
|
|
292
|
+
gender: 'm',
|
|
293
|
+
address: {
|
|
294
|
+
street1: 'a',
|
|
295
|
+
...(x === 1 ? { street2: 'b' } : {})
|
|
296
|
+
}
|
|
297
|
+
})
|
|
298
|
+
)
|
|
299
|
+
)
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
const controller = new AbortController();
|
|
303
|
+
const found: Person[] = [];
|
|
304
|
+
|
|
305
|
+
for await (const items of service.list(Person, { abort: controller.signal, batchSizeHint: 1 })) {
|
|
306
|
+
found.push(...items);
|
|
307
|
+
controller.abort();
|
|
308
|
+
await timers.setTimeout(10);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (this.indexLimitSkew) {
|
|
312
|
+
assert(found.length > 0 && found.length < this.indexLimitSkew);
|
|
313
|
+
} else {
|
|
314
|
+
assert(found.length === 1);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
271
318
|
@Test('save it')
|
|
272
319
|
async verifyRaw() {
|
|
273
320
|
const service = await this.service;
|
|
274
321
|
|
|
275
322
|
const people = await Promise.all(
|
|
276
|
-
[1, 2, 3, 8].map((x, i) =>
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
323
|
+
[1, 2, 3, 8].map((x, i) =>
|
|
324
|
+
service[i % 2 === 0 ? 'upsert' : 'create'](Person, {
|
|
325
|
+
name: 'Bob',
|
|
326
|
+
age: 20 + x,
|
|
327
|
+
gender: 'm',
|
|
328
|
+
address: {
|
|
329
|
+
street1: 'a',
|
|
330
|
+
...(x === 1 ? { street2: 'b' } : {})
|
|
331
|
+
}
|
|
332
|
+
})
|
|
333
|
+
)
|
|
285
334
|
);
|
|
286
335
|
|
|
287
336
|
const single = await service.get(Person, people[2].id);
|
|
@@ -331,10 +380,7 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
331
380
|
const service = await this.service;
|
|
332
381
|
const o = await service.create(SimpleList, {
|
|
333
382
|
names: ['rob', 'tom'],
|
|
334
|
-
simples: [
|
|
335
|
-
{ name: 'roger' },
|
|
336
|
-
{ name: 'dodger' }
|
|
337
|
-
]
|
|
383
|
+
simples: [{ name: 'roger' }, { name: 'dodger' }]
|
|
338
384
|
});
|
|
339
385
|
assert(o.names.length === 2);
|
|
340
386
|
assert(o.simples);
|
|
@@ -356,10 +402,13 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
356
402
|
const service = await this.service;
|
|
357
403
|
|
|
358
404
|
// Create with bigint values
|
|
359
|
-
const created = await service.create(
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
405
|
+
const created = await service.create(
|
|
406
|
+
BigIntModel,
|
|
407
|
+
BigIntModel.from({
|
|
408
|
+
largeNumber: 9007199254740991n, // Number.MAX_SAFE_INTEGER as bigint
|
|
409
|
+
optionalBigInt: 1234567890123456789n
|
|
410
|
+
})
|
|
411
|
+
);
|
|
363
412
|
|
|
364
413
|
assert(created.id);
|
|
365
414
|
assert.strictEqual(created.largeNumber, 9007199254740991n);
|
|
@@ -371,11 +420,14 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
371
420
|
assert.strictEqual(retrieved.optionalBigInt, 1234567890123456789n);
|
|
372
421
|
|
|
373
422
|
// Update with new bigint value
|
|
374
|
-
const updated = await service.update(
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
423
|
+
const updated = await service.update(
|
|
424
|
+
BigIntModel,
|
|
425
|
+
BigIntModel.from({
|
|
426
|
+
id: created.id,
|
|
427
|
+
largeNumber: 18014398509481982n,
|
|
428
|
+
optionalBigInt: undefined
|
|
429
|
+
})
|
|
430
|
+
);
|
|
379
431
|
|
|
380
432
|
assert.strictEqual(updated.largeNumber, 18014398509481982n);
|
|
381
433
|
assert.strictEqual(updated.optionalBigInt, undefined);
|
|
@@ -385,4 +437,4 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
385
437
|
assert.strictEqual(final.largeNumber, 18014398509481982n);
|
|
386
438
|
assert(!final.optionalBigInt);
|
|
387
439
|
}
|
|
388
|
-
}
|
|
440
|
+
}
|
package/support/test/expiry.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
2
|
import timers from 'node:timers/promises';
|
|
3
3
|
|
|
4
|
-
import { Suite, Test } from '@travetto/test';
|
|
5
4
|
import { type TimeSpan, TimeUtil } from '@travetto/runtime';
|
|
5
|
+
import { Suite, Test } from '@travetto/test';
|
|
6
6
|
|
|
7
|
+
import { NotFoundError } from '../../src/error/not-found.ts';
|
|
7
8
|
import { ExpiresAt, Model } from '../../src/registry/decorator.ts';
|
|
8
9
|
import type { ModelExpirySupport } from '../../src/types/expiry.ts';
|
|
9
10
|
import { ModelExpiryUtil } from '../../src/util/expiry.ts';
|
|
10
|
-
import { NotFoundError } from '../../src/error/not-found.ts';
|
|
11
11
|
import { BaseModelSuite } from './base.ts';
|
|
12
12
|
|
|
13
13
|
@Model('expiry-user')
|
|
@@ -20,7 +20,6 @@ export class ExpiryUser {
|
|
|
20
20
|
|
|
21
21
|
@Suite()
|
|
22
22
|
export abstract class ModelExpirySuite extends BaseModelSuite<ModelExpirySupport> {
|
|
23
|
-
|
|
24
23
|
delayFactor: number = 1;
|
|
25
24
|
|
|
26
25
|
async wait(input: TimeSpan | number | string) {
|
|
@@ -34,9 +33,12 @@ export abstract class ModelExpirySuite extends BaseModelSuite<ModelExpirySupport
|
|
|
34
33
|
@Test()
|
|
35
34
|
async basic() {
|
|
36
35
|
const service = await this.service;
|
|
37
|
-
const result = await service.upsert(
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
const result = await service.upsert(
|
|
37
|
+
ExpiryUser,
|
|
38
|
+
ExpiryUser.from({
|
|
39
|
+
expiresAt: this.timeFromNow('2s')
|
|
40
|
+
})
|
|
41
|
+
);
|
|
40
42
|
assert(result instanceof ExpiryUser);
|
|
41
43
|
|
|
42
44
|
const expiry = ModelExpiryUtil.getExpiryState(ExpiryUser, await service.get(ExpiryUser, result.id));
|
|
@@ -46,9 +48,12 @@ export abstract class ModelExpirySuite extends BaseModelSuite<ModelExpirySupport
|
|
|
46
48
|
@Test()
|
|
47
49
|
async aging() {
|
|
48
50
|
const service = await this.service;
|
|
49
|
-
const result = await service.upsert(
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
const result = await service.upsert(
|
|
52
|
+
ExpiryUser,
|
|
53
|
+
ExpiryUser.from({
|
|
54
|
+
expiresAt: this.timeFromNow(100)
|
|
55
|
+
})
|
|
56
|
+
);
|
|
52
57
|
|
|
53
58
|
assert(result instanceof ExpiryUser);
|
|
54
59
|
|
|
@@ -60,9 +65,12 @@ export abstract class ModelExpirySuite extends BaseModelSuite<ModelExpirySupport
|
|
|
60
65
|
@Test()
|
|
61
66
|
async updateExpired() {
|
|
62
67
|
const service = await this.service;
|
|
63
|
-
const result = await service.upsert(
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
const result = await service.upsert(
|
|
69
|
+
ExpiryUser,
|
|
70
|
+
ExpiryUser.from({
|
|
71
|
+
expiresAt: this.timeFromNow(100)
|
|
72
|
+
})
|
|
73
|
+
);
|
|
66
74
|
|
|
67
75
|
assert(result instanceof ExpiryUser);
|
|
68
76
|
|
|
@@ -74,14 +82,17 @@ export abstract class ModelExpirySuite extends BaseModelSuite<ModelExpirySupport
|
|
|
74
82
|
@Test()
|
|
75
83
|
async ageWithExtension() {
|
|
76
84
|
const service = await this.service;
|
|
77
|
-
const result = await service.upsert(
|
|
78
|
-
|
|
79
|
-
|
|
85
|
+
const result = await service.upsert(
|
|
86
|
+
ExpiryUser,
|
|
87
|
+
ExpiryUser.from({
|
|
88
|
+
expiresAt: this.timeFromNow('2s')
|
|
89
|
+
})
|
|
90
|
+
);
|
|
80
91
|
assert(result instanceof ExpiryUser);
|
|
81
92
|
|
|
82
93
|
await this.wait(50);
|
|
83
94
|
|
|
84
|
-
assert(!ModelExpiryUtil.getExpiryState(ExpiryUser,
|
|
95
|
+
assert(!ModelExpiryUtil.getExpiryState(ExpiryUser, await service.get(ExpiryUser, result.id)).expired);
|
|
85
96
|
|
|
86
97
|
await service.updatePartial(ExpiryUser, {
|
|
87
98
|
id: result.id,
|
|
@@ -97,16 +108,23 @@ export abstract class ModelExpirySuite extends BaseModelSuite<ModelExpirySupport
|
|
|
97
108
|
async culling() {
|
|
98
109
|
const service = await this.service;
|
|
99
110
|
|
|
100
|
-
let total;
|
|
111
|
+
let total: number;
|
|
101
112
|
|
|
102
113
|
total = await this.getSize(ExpiryUser);
|
|
103
114
|
assert(total === 0);
|
|
104
115
|
|
|
105
116
|
// Create
|
|
106
117
|
await Promise.all(
|
|
107
|
-
Array(10)
|
|
108
|
-
|
|
109
|
-
|
|
118
|
+
Array(10)
|
|
119
|
+
.fill(0)
|
|
120
|
+
.map((x, i) =>
|
|
121
|
+
service.upsert(
|
|
122
|
+
ExpiryUser,
|
|
123
|
+
ExpiryUser.from({
|
|
124
|
+
expiresAt: this.timeFromNow(300 + i * this.delayFactor)
|
|
125
|
+
})
|
|
126
|
+
)
|
|
127
|
+
)
|
|
110
128
|
);
|
|
111
129
|
|
|
112
130
|
// Let expire
|
|
@@ -124,4 +142,4 @@ export abstract class ModelExpirySuite extends BaseModelSuite<ModelExpirySupport
|
|
|
124
142
|
total = await this.getSize(ExpiryUser);
|
|
125
143
|
assert(total === 0);
|
|
126
144
|
}
|
|
127
|
-
}
|
|
145
|
+
}
|