@travetto/model 5.0.0-rc.1 → 5.0.0-rc.11
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 +85 -147
- package/__index__.ts +2 -4
- package/package.json +7 -7
- package/src/error/exists.ts +1 -1
- package/src/error/invalid-index.ts +1 -1
- package/src/error/invalid-sub-type.ts +1 -1
- package/src/error/not-found.ts +1 -1
- package/src/internal/service/blob.ts +5 -0
- package/src/internal/service/bulk.ts +1 -1
- package/src/internal/service/common.ts +20 -26
- package/src/internal/service/crud.ts +15 -21
- package/src/internal/service/expiry.ts +2 -5
- package/src/internal/service/indexed.ts +13 -26
- package/src/internal/service/storage.ts +3 -7
- package/src/registry/decorator.ts +7 -13
- package/src/registry/model.ts +13 -8
- package/src/registry/types.ts +1 -2
- package/src/service/basic.ts +1 -1
- package/src/service/blob.ts +41 -0
- package/src/service/bulk.ts +1 -1
- package/src/service/crud.ts +1 -1
- package/src/service/expiry.ts +1 -1
- package/src/service/indexed.ts +2 -2
- package/src/service/storage.ts +1 -1
- package/src/util/blob.ts +60 -0
- package/support/base-command.ts +1 -1
- package/support/bin/candidate.ts +2 -3
- package/support/bin/export.ts +1 -1
- package/support/bin/install.ts +1 -1
- package/support/cli.model_export.ts +1 -1
- package/support/cli.model_install.ts +1 -1
- package/support/doc.support.tsx +5 -11
- package/support/fixtures/alpha.txt +1 -0
- package/support/fixtures/empty +0 -0
- package/support/fixtures/empty.m4a +0 -0
- package/support/fixtures/logo.gif +0 -0
- package/support/fixtures/logo.png +0 -0
- package/support/fixtures/small-audio +0 -0
- package/support/fixtures/small-audio.mp3 +0 -0
- package/support/test/base.ts +3 -3
- package/support/test/blob.ts +117 -0
- package/support/test/crud.ts +5 -4
- package/support/test/expiry.ts +1 -1
- package/support/test/indexed.ts +1 -1
- package/support/test/polymorphism.ts +7 -6
- package/support/test/suite.ts +6 -6
- package/src/internal/service/stream.ts +0 -22
- package/src/provider/file.ts +0 -233
- package/src/provider/memory.ts +0 -341
- package/src/service/stream.ts +0 -72
- package/support/test/stream.ts +0 -110
package/support/test/crud.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
|
+
import timers from 'node:timers/promises';
|
|
2
3
|
|
|
3
4
|
import { Suite, Test } from '@travetto/test';
|
|
4
5
|
import { Schema, Text, Precision, Required, } from '@travetto/schema';
|
|
@@ -48,7 +49,7 @@ class User2 {
|
|
|
48
49
|
name: string;
|
|
49
50
|
|
|
50
51
|
prePersist() {
|
|
51
|
-
this.name = `${this.name}-
|
|
52
|
+
this.name = `${this.name}-suffix`;
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
|
|
@@ -192,7 +193,7 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
192
193
|
}));
|
|
193
194
|
|
|
194
195
|
assert(o.address === undefined);
|
|
195
|
-
assert(o.name === 'bob-
|
|
196
|
+
assert(o.name === 'bob-suffix');
|
|
196
197
|
|
|
197
198
|
await service.updatePartial(User2, User2.from({
|
|
198
199
|
id: o.id,
|
|
@@ -216,7 +217,7 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
216
217
|
assert(res.createdDate instanceof Date);
|
|
217
218
|
}
|
|
218
219
|
|
|
219
|
-
@Test('verify
|
|
220
|
+
@Test('verify pre-persist on create/update')
|
|
220
221
|
async testPrePersist() {
|
|
221
222
|
const service = await this.service;
|
|
222
223
|
const res = await service.create(Dated, Dated.from({}));
|
|
@@ -224,7 +225,7 @@ export abstract class ModelCrudSuite extends BaseModelSuite<ModelCrudSupport> {
|
|
|
224
225
|
assert(res.createdDate instanceof Date);
|
|
225
226
|
assert(res.updatedDate instanceof Date);
|
|
226
227
|
|
|
227
|
-
await
|
|
228
|
+
await timers.setTimeout(100);
|
|
228
229
|
|
|
229
230
|
const final = await service.updatePartial(Dated, { id: res.id });
|
|
230
231
|
assert(final.createdDate instanceof Date);
|
package/support/test/expiry.ts
CHANGED
|
@@ -2,7 +2,7 @@ import assert from 'node:assert';
|
|
|
2
2
|
import timers from 'node:timers/promises';
|
|
3
3
|
|
|
4
4
|
import { Suite, Test } from '@travetto/test';
|
|
5
|
-
import { TimeSpan, TimeUnit, TimeUtil } from '@travetto/
|
|
5
|
+
import { TimeSpan, TimeUnit, TimeUtil } from '@travetto/runtime';
|
|
6
6
|
|
|
7
7
|
import { ExpiresAt, Model } from '../../src/registry/decorator';
|
|
8
8
|
import { ModelExpirySupport } from '../../src/service/expiry';
|
package/support/test/indexed.ts
CHANGED
|
@@ -2,7 +2,7 @@ import assert from 'node:assert';
|
|
|
2
2
|
|
|
3
3
|
import { Suite, Test } from '@travetto/test';
|
|
4
4
|
import { Schema } from '@travetto/schema';
|
|
5
|
-
import { TimeUtil } from '@travetto/
|
|
5
|
+
import { TimeUtil } from '@travetto/runtime';
|
|
6
6
|
|
|
7
7
|
import { Index, Model } from '../../src/registry/decorator';
|
|
8
8
|
import { ModelIndexedSupport } from '../../src/service/indexed';
|
|
@@ -12,6 +12,7 @@ import { isIndexedSupported } from '../../src/internal/service/common';
|
|
|
12
12
|
import { ExistsError } from '../../src/error/exists';
|
|
13
13
|
|
|
14
14
|
import { BaseModelSuite } from './base';
|
|
15
|
+
import { castTo } from '@travetto/runtime';
|
|
15
16
|
|
|
16
17
|
@Model({ baseType: true })
|
|
17
18
|
export class Worker {
|
|
@@ -70,7 +71,7 @@ export class IndexedEngineer extends IndexedWorker {
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
async function collect<T>(iterable: AsyncIterable<T>): Promise<T[]> {
|
|
73
|
-
const out
|
|
74
|
+
const out: T[] = [];
|
|
74
75
|
for await (const el of iterable) {
|
|
75
76
|
out.push(el);
|
|
76
77
|
}
|
|
@@ -116,12 +117,12 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
|
|
|
116
117
|
|
|
117
118
|
const fire3 = all.find(x => x instanceof Firefighter);
|
|
118
119
|
assert(fire3 instanceof Firefighter);
|
|
119
|
-
assert(
|
|
120
|
+
assert(fire3.firehouse === 20);
|
|
120
121
|
assert(fire3.name === 'rob');
|
|
121
122
|
|
|
122
123
|
const eng3 = all.find(x => x instanceof Engineer);
|
|
123
124
|
assert(eng3 instanceof Engineer);
|
|
124
|
-
assert(
|
|
125
|
+
assert(eng3.major === 'oranges');
|
|
125
126
|
assert(eng3.name === 'cob');
|
|
126
127
|
|
|
127
128
|
const engineers = await collect(service.list(Engineer));
|
|
@@ -161,7 +162,7 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
|
|
|
161
162
|
);
|
|
162
163
|
|
|
163
164
|
await assert.rejects(
|
|
164
|
-
() => service.update(Engineer, Doctor.from({ ...doc })
|
|
165
|
+
() => service.update(Engineer, castTo(Doctor.from({ ...doc }))),
|
|
165
166
|
(e: Error) => (e instanceof NotFoundError || e instanceof SubTypeNotSupportedError || e instanceof TypeMismatchError) ? undefined : e);
|
|
166
167
|
|
|
167
168
|
await timers.setTimeout(15);
|
|
@@ -205,7 +206,7 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
|
|
|
205
206
|
|
|
206
207
|
@Test('Polymorphic index', { skip: BaseModelSuite.ifNot(isIndexedSupported) })
|
|
207
208
|
async polymorphicIndexGet() {
|
|
208
|
-
const service = (await this.service)
|
|
209
|
+
const service: ModelIndexedSupport = castTo(await this.service);
|
|
209
210
|
const now = 30;
|
|
210
211
|
const [doc, fire, eng] = [
|
|
211
212
|
IndexedDoctor.from({ name: 'bob', specialty: 'feet', age: now }),
|
|
@@ -235,7 +236,7 @@ export abstract class ModelPolymorphismSuite extends BaseModelSuite<ModelCrudSup
|
|
|
235
236
|
|
|
236
237
|
@Test('Polymorphic index', { skip: BaseModelSuite.ifNot(isIndexedSupported) })
|
|
237
238
|
async polymorphicIndexDelete() {
|
|
238
|
-
const service = (await this.service)
|
|
239
|
+
const service: ModelIndexedSupport = castTo(await this.service);
|
|
239
240
|
const now = 30;
|
|
240
241
|
const [doc, fire, eng] = [
|
|
241
242
|
IndexedDoctor.from({ name: 'bob', specialty: 'feet', age: now }),
|
package/support/test/suite.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { Class } from '@travetto/
|
|
1
|
+
import { Class } from '@travetto/runtime';
|
|
2
2
|
import { DependencyRegistry } from '@travetto/di';
|
|
3
3
|
import { RootRegistry } from '@travetto/registry';
|
|
4
4
|
import { SuiteRegistry, TestFixtures } from '@travetto/test';
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { isBlobSupported, isStorageSupported } from '../../src/internal/service/common';
|
|
7
|
+
import { MODEL_BLOB } from '../../src/internal/service/blob';
|
|
8
8
|
import { ModelRegistry } from '../../src/registry/model';
|
|
9
9
|
|
|
10
10
|
const Loaded = Symbol();
|
|
@@ -61,11 +61,11 @@ export function ModelSuite<T extends { configClass: Class<{ autoCreate?: boolean
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
-
if (
|
|
64
|
+
if (isBlobSupported(service)) {
|
|
65
65
|
if (service.truncateModel) {
|
|
66
|
-
await service.truncateModel(
|
|
66
|
+
await service.truncateModel(MODEL_BLOB);
|
|
67
67
|
} else if (service.deleteModel) {
|
|
68
|
-
await service.deleteModel(
|
|
68
|
+
await service.deleteModel(MODEL_BLOB);
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
} else {
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { AppError, Class } from '@travetto/base';
|
|
2
|
-
|
|
3
|
-
import { ModelType } from '../../types/model';
|
|
4
|
-
import { StreamRange } from '../../service/stream';
|
|
5
|
-
|
|
6
|
-
class Cls { id: string; }
|
|
7
|
-
export const StreamModel: Class<ModelType> = Cls;
|
|
8
|
-
export const STREAMS = '_streams';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Enforce byte range for stream stream/file of a certain size
|
|
13
|
-
*/
|
|
14
|
-
export function enforceRange({ start, end }: StreamRange, size: number): Required<StreamRange> {
|
|
15
|
-
end = Math.min(end ?? size - 1, size - 1);
|
|
16
|
-
|
|
17
|
-
if (Number.isNaN(start) || Number.isNaN(end) || !Number.isFinite(start) || start >= size || start < 0 || start > end) {
|
|
18
|
-
throw new AppError('Invalid position, out of range', 'data');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return { start, end };
|
|
22
|
-
}
|
package/src/provider/file.ts
DELETED
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import { createReadStream, createWriteStream } from 'node:fs';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import { Readable } from 'node:stream';
|
|
5
|
-
import { pipeline } from 'node:stream/promises';
|
|
6
|
-
import path from 'node:path';
|
|
7
|
-
|
|
8
|
-
import { Class, TimeSpan, RuntimeContext } from '@travetto/base';
|
|
9
|
-
import { Injectable } from '@travetto/di';
|
|
10
|
-
import { Config } from '@travetto/config';
|
|
11
|
-
import { Required } from '@travetto/schema';
|
|
12
|
-
|
|
13
|
-
import { ModelCrudSupport } from '../service/crud';
|
|
14
|
-
import { ModelStreamSupport, StreamMeta, StreamRange } from '../service/stream';
|
|
15
|
-
import { ModelType, OptionalId } from '../types/model';
|
|
16
|
-
import { ModelExpirySupport } from '../service/expiry';
|
|
17
|
-
import { ModelRegistry } from '../registry/model';
|
|
18
|
-
import { ModelStorageSupport } from '../service/storage';
|
|
19
|
-
import { ModelCrudUtil } from '../internal/service/crud';
|
|
20
|
-
import { ModelExpiryUtil } from '../internal/service/expiry';
|
|
21
|
-
import { NotFoundError } from '../error/not-found';
|
|
22
|
-
import { ExistsError } from '../error/exists';
|
|
23
|
-
import { enforceRange, StreamModel, STREAMS } from '../internal/service/stream';
|
|
24
|
-
|
|
25
|
-
type Suffix = '.bin' | '.meta' | '.json' | '.expires';
|
|
26
|
-
|
|
27
|
-
const BIN = '.bin';
|
|
28
|
-
const META = '.meta';
|
|
29
|
-
|
|
30
|
-
@Config('model.file')
|
|
31
|
-
export class FileModelConfig {
|
|
32
|
-
@Required(false)
|
|
33
|
-
folder: string;
|
|
34
|
-
namespace: string = '.';
|
|
35
|
-
autoCreate?: boolean;
|
|
36
|
-
cullRate?: number | TimeSpan;
|
|
37
|
-
|
|
38
|
-
async postConstruct(): Promise<void> {
|
|
39
|
-
this.folder ??= path.resolve(os.tmpdir(), `trv_file_${RuntimeContext.main.name.replace(/[^a-z]/ig, '_')}`);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const exists = (f: string): Promise<boolean> => fs.stat(f).then(() => true, () => false);
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Standard file support
|
|
47
|
-
*/
|
|
48
|
-
@Injectable()
|
|
49
|
-
export class FileModelService implements ModelCrudSupport, ModelStreamSupport, ModelExpirySupport, ModelStorageSupport {
|
|
50
|
-
|
|
51
|
-
private static async * scanFolder(folder: string, suffix: string): AsyncGenerator<[id: string, field: string]> {
|
|
52
|
-
for (const sub of await fs.readdir(folder)) {
|
|
53
|
-
for (const file of await fs.readdir(path.resolve(folder, sub))) {
|
|
54
|
-
if (file.endsWith(suffix)) {
|
|
55
|
-
yield [file.replace(suffix, ''), path.resolve(folder, sub, file)];
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
idSource = ModelCrudUtil.uuidSource();
|
|
62
|
-
|
|
63
|
-
get client(): string {
|
|
64
|
-
return this.config.folder;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* The root location for all activity
|
|
69
|
-
*/
|
|
70
|
-
constructor(public readonly config: FileModelConfig) { }
|
|
71
|
-
|
|
72
|
-
async #resolveName<T extends ModelType>(cls: Class<T> | string, suffix?: Suffix, id?: string): Promise<string> {
|
|
73
|
-
const name = typeof cls === 'string' ? cls : ModelRegistry.getStore(cls);
|
|
74
|
-
let resolved = path.resolve(this.config.folder, this.config.namespace, name);
|
|
75
|
-
if (id) {
|
|
76
|
-
resolved = path.resolve(resolved, id.replace(/^[/]/, '').substring(0, 3));
|
|
77
|
-
}
|
|
78
|
-
let dir = resolved;
|
|
79
|
-
if (id) {
|
|
80
|
-
resolved = path.resolve(resolved, `${id}${suffix}`);
|
|
81
|
-
dir = path.dirname(resolved);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
await fs.mkdir(dir, { recursive: true });
|
|
85
|
-
return resolved;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async #find<T extends ModelType>(cls: Class<T> | string, suffix: Suffix, id?: string): Promise<string> {
|
|
89
|
-
const file = await this.#resolveName(cls, suffix, id);
|
|
90
|
-
if (id && !(await exists(file))) {
|
|
91
|
-
throw new NotFoundError(cls, id);
|
|
92
|
-
}
|
|
93
|
-
return file;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
postConstruct(): void {
|
|
97
|
-
ModelExpiryUtil.registerCull(this);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
checkExpiry<T extends ModelType>(cls: Class<T>, item: T): T {
|
|
101
|
-
const { expiresAt } = ModelRegistry.get(cls);
|
|
102
|
-
if (expiresAt && ModelExpiryUtil.getExpiryState(cls, item).expired) {
|
|
103
|
-
throw new NotFoundError(cls, item.id);
|
|
104
|
-
}
|
|
105
|
-
return item;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async get<T extends ModelType>(cls: Class<T>, id: string): Promise<T> {
|
|
109
|
-
await this.#find(cls, '.json', id);
|
|
110
|
-
|
|
111
|
-
const file = await this.#resolveName(cls, '.json', id);
|
|
112
|
-
|
|
113
|
-
if (await exists(file)) {
|
|
114
|
-
const content = await fs.readFile(file);
|
|
115
|
-
return this.checkExpiry(cls, await ModelCrudUtil.load(cls, content));
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
throw new NotFoundError(cls, id);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async create<T extends ModelType>(cls: Class<T>, item: OptionalId<T>): Promise<T> {
|
|
122
|
-
if (!item.id) {
|
|
123
|
-
item.id = this.idSource.create();
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const file = await this.#resolveName(cls, '.json', item.id);
|
|
127
|
-
|
|
128
|
-
if (await exists(file)) {
|
|
129
|
-
throw new ExistsError(cls, item.id!);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return await this.upsert(cls, item);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async update<T extends ModelType>(cls: Class<T>, item: T): Promise<T> {
|
|
136
|
-
await this.get(cls, item.id);
|
|
137
|
-
return await this.upsert(cls, item);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
async upsert<T extends ModelType>(cls: Class<T>, item: OptionalId<T>): Promise<T> {
|
|
141
|
-
ModelCrudUtil.ensureNotSubType(cls);
|
|
142
|
-
const prepped = await ModelCrudUtil.preStore(cls, item, this);
|
|
143
|
-
|
|
144
|
-
const file = await this.#resolveName(cls, '.json', item.id);
|
|
145
|
-
await fs.writeFile(file, JSON.stringify(item), { encoding: 'utf8' });
|
|
146
|
-
|
|
147
|
-
return prepped;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async updatePartial<T extends ModelType>(cls: Class<T>, item: Partial<T> & { id: string }, view?: string): Promise<T> {
|
|
151
|
-
ModelCrudUtil.ensureNotSubType(cls);
|
|
152
|
-
const id = item.id;
|
|
153
|
-
item = await ModelCrudUtil.naivePartialUpdate(cls, item, view, () => this.get(cls, id));
|
|
154
|
-
const file = await this.#resolveName(cls, '.json', item.id);
|
|
155
|
-
await fs.writeFile(file, JSON.stringify(item), { encoding: 'utf8' });
|
|
156
|
-
|
|
157
|
-
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
158
|
-
return item as T;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
async delete<T extends ModelType>(cls: Class<T>, id: string): Promise<void> {
|
|
162
|
-
const file = await this.#find(cls, '.json', id);
|
|
163
|
-
await fs.unlink(file);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
async * list<T extends ModelType>(cls: Class<T>): AsyncIterable<T> {
|
|
167
|
-
for await (const [id] of FileModelService.scanFolder(await this.#resolveName(cls, '.json'), '.json')) {
|
|
168
|
-
try {
|
|
169
|
-
yield await this.get(cls, id);
|
|
170
|
-
} catch (err) {
|
|
171
|
-
if (!(err instanceof NotFoundError)) {
|
|
172
|
-
throw err;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Stream
|
|
179
|
-
async upsertStream(location: string, input: Readable, meta: StreamMeta): Promise<void> {
|
|
180
|
-
const file = await this.#resolveName(STREAMS, BIN, location);
|
|
181
|
-
await Promise.all([
|
|
182
|
-
await pipeline(input, createWriteStream(file)),
|
|
183
|
-
fs.writeFile(file.replace(BIN, META), JSON.stringify(meta), 'utf8')
|
|
184
|
-
]);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
async getStream(location: string, range?: StreamRange): Promise<Readable> {
|
|
188
|
-
const file = await this.#find(STREAMS, BIN, location);
|
|
189
|
-
if (range) {
|
|
190
|
-
const meta = await this.describeStream(location);
|
|
191
|
-
range = enforceRange(range, meta.size);
|
|
192
|
-
}
|
|
193
|
-
return createReadStream(file, range);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
async describeStream(location: string): Promise<StreamMeta> {
|
|
197
|
-
const file = await this.#find(STREAMS, META, location);
|
|
198
|
-
const content = await fs.readFile(file);
|
|
199
|
-
const text: StreamMeta = JSON.parse(content.toString('utf8'));
|
|
200
|
-
return text;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
async deleteStream(location: string): Promise<void> {
|
|
204
|
-
const file = await this.#resolveName(STREAMS, BIN, location);
|
|
205
|
-
if (await exists(file)) {
|
|
206
|
-
await Promise.all([
|
|
207
|
-
fs.unlink(file),
|
|
208
|
-
fs.unlink(file.replace('.bin', META))
|
|
209
|
-
]);
|
|
210
|
-
} else {
|
|
211
|
-
throw new NotFoundError('Stream', location);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Expiry
|
|
216
|
-
async deleteExpired<T extends ModelType>(cls: Class<T>): Promise<number> {
|
|
217
|
-
return ModelExpiryUtil.naiveDeleteExpired(this, cls);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Storage management
|
|
221
|
-
async createStorage(): Promise<void> {
|
|
222
|
-
const dir = path.resolve(this.config.folder, this.config.namespace);
|
|
223
|
-
await fs.mkdir(dir, { recursive: true });
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
async deleteStorage(): Promise<void> {
|
|
227
|
-
await fs.rm(path.resolve(this.config.folder, this.config.namespace), { recursive: true, force: true });
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
async truncateModel(cls: Class<ModelType>): Promise<void> {
|
|
231
|
-
await fs.rm(await this.#resolveName(cls === StreamModel ? STREAMS : cls), { recursive: true, force: true });
|
|
232
|
-
}
|
|
233
|
-
}
|