@travetto/model 5.0.0-rc.12 → 5.0.0-rc.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/model",
3
- "version": "5.0.0-rc.12",
3
+ "version": "5.0.0-rc.13",
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": "^5.0.0-rc.12",
30
- "@travetto/di": "^5.0.0-rc.11",
31
- "@travetto/registry": "^5.0.0-rc.11",
32
- "@travetto/schema": "^5.0.0-rc.12"
29
+ "@travetto/config": "^5.0.0-rc.13",
30
+ "@travetto/di": "^5.0.0-rc.12",
31
+ "@travetto/registry": "^5.0.0-rc.12",
32
+ "@travetto/schema": "^5.0.0-rc.13"
33
33
  },
34
34
  "peerDependencies": {
35
- "@travetto/cli": "^5.0.0-rc.12",
36
- "@travetto/test": "^5.0.0-rc.11"
35
+ "@travetto/cli": "^5.0.0-rc.13",
36
+ "@travetto/test": "^5.0.0-rc.12"
37
37
  },
38
38
  "peerDependenciesMeta": {
39
39
  "@travetto/cli": {
@@ -1,5 +1,49 @@
1
- import { Class } from '@travetto/runtime';
1
+ import { Class, AppError, BinaryInput, BinaryUtil, BlobMeta, ByteRange } from '@travetto/runtime';
2
2
  import { ModelType } from '../../types/model';
3
3
 
4
4
  export const ModelBlobNamespace = '__blobs';
5
- export const MODEL_BLOB: Class<ModelType> = class { id: string; };
5
+ export const MODEL_BLOB: Class<ModelType> = class { id: string; };
6
+
7
+ import { Readable } from 'node:stream';
8
+
9
+
10
+ /**
11
+ * Utilities for processing assets
12
+ */
13
+ export class ModelBlobUtil {
14
+
15
+ /**
16
+ * Convert input to a Readable, and get what metadata is available
17
+ */
18
+ static async getInput(src: BinaryInput, metadata: BlobMeta = {}): Promise<[Readable, BlobMeta]> {
19
+ let input: Readable;
20
+ if (src instanceof Blob) {
21
+ metadata = { ...BinaryUtil.getBlobMeta(src), ...metadata };
22
+ metadata.size ??= src.size;
23
+ input = Readable.fromWeb(src.stream());
24
+ } else if (typeof src === 'object' && 'pipeThrough' in src) {
25
+ input = Readable.fromWeb(src);
26
+ } else if (typeof src === 'object' && 'pipe' in src) {
27
+ input = src;
28
+ } else {
29
+ metadata.size = src.length;
30
+ input = Readable.from(src);
31
+ }
32
+
33
+ return [input, metadata ?? {}];
34
+ }
35
+
36
+ /**
37
+ * Enforce byte range for stream stream/file of a certain size
38
+ */
39
+ static enforceRange({ start, end }: ByteRange, size: number): Required<ByteRange> {
40
+ // End is inclusive
41
+ end = Math.min(end ?? (size - 1), size - 1);
42
+
43
+ if (Number.isNaN(start) || Number.isNaN(end) || !Number.isFinite(start) || start >= size || start < 0 || start > end) {
44
+ throw new AppError('Invalid position, out of range', 'data');
45
+ }
46
+
47
+ return { start, end };
48
+ }
49
+ }
@@ -1,25 +1,20 @@
1
1
  import { BinaryInput, BlobMeta, ByteRange } from '@travetto/runtime';
2
2
 
3
3
  /**
4
- * Support for Blobs CRD. Blob update is not supported.
4
+ * Support for Blobs CRUD.
5
5
  *
6
6
  * @concrete ../internal/service/common#ModelBlobSupportTarget
7
7
  */
8
8
  export interface ModelBlobSupport {
9
9
 
10
- /**
11
- * Insert blob to storage
12
- * @param location The location of the blob
13
- * @param input The actual blob to write
14
- */
15
- insertBlob(location: string, input: BinaryInput, meta?: BlobMeta, errorIfExisting?: boolean): Promise<void>;
16
-
17
10
  /**
18
11
  * Upsert blob to storage
19
12
  * @param location The location of the blob
20
13
  * @param input The actual blob to write
14
+ * @param meta Additional metadata to store with the blob
15
+ * @param overwrite Should we replace content if already found, defaults to true
21
16
  */
22
- upsertBlob(location: string, input: BinaryInput, meta?: BlobMeta): Promise<void>;
17
+ upsertBlob(location: string, input: BinaryInput, meta?: BlobMeta, overwrite?: boolean): Promise<void>;
23
18
 
24
19
  /**
25
20
  * Get blob from storage
@@ -2,10 +2,12 @@ import assert from 'node:assert';
2
2
 
3
3
  import { Suite, Test, TestFixtures } from '@travetto/test';
4
4
  import { BaseModelSuite } from '@travetto/model/support/test/base';
5
- import { Util } from '@travetto/runtime';
5
+ import { BinaryUtil, Util } from '@travetto/runtime';
6
6
 
7
7
  import { ModelBlobSupport } from '../../src/service/blob';
8
- import { ModelBlobUtil } from '../../src/util/blob';
8
+ import { ModelBlobUtil } from '../../src/internal/service/blob';
9
+
10
+ const meta = BinaryUtil.getBlobMeta;
9
11
 
10
12
  @Suite()
11
13
  export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
@@ -20,9 +22,26 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
20
22
  const id = Util.uuid();
21
23
 
22
24
  await service.upsertBlob(id, buffer);
23
- const meta = await service.describeBlob(id);
25
+ const m = await service.describeBlob(id);
24
26
  const retrieved = await service.describeBlob(id);
25
- assert.deepStrictEqual(meta, retrieved);
27
+ assert.deepStrictEqual(m, retrieved);
28
+ }
29
+
30
+ @Test()
31
+ async upsert(): Promise<void> {
32
+ const service = await this.service;
33
+ const buffer = await this.fixture.read('/asset.yml', true);
34
+
35
+ const id = Util.uuid();
36
+
37
+ await service.upsertBlob(id, buffer, { hash: '10' });
38
+ assert((await service.describeBlob(id)).hash === '10');
39
+
40
+ await service.upsertBlob(id, buffer, { hash: '20' });
41
+ assert((await service.describeBlob(id)).hash === '20');
42
+
43
+ await service.upsertBlob(id, buffer, { hash: '30' }, false);
44
+ assert((await service.describeBlob(id)).hash === '20');
26
45
  }
27
46
 
28
47
  @Test()
@@ -32,11 +51,11 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
32
51
 
33
52
  const id = Util.uuid();
34
53
  await service.upsertBlob(id, buffer);
35
- const meta = await service.describeBlob(id);
54
+ const { hash } = await service.describeBlob(id);
36
55
 
37
56
  const retrieved = await service.getBlob(id);
38
- const retrievedMeta = retrieved.meta!;
39
- assert(meta.hash === retrievedMeta.hash);
57
+ const { hash: received } = meta(retrieved)!;
58
+ assert(hash === received);
40
59
  }
41
60
 
42
61
  @Test()
@@ -69,7 +88,7 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
69
88
 
70
89
  const partial = await service.getBlob(id, { start: 10, end: 20 });
71
90
  assert(partial.size === 11);
72
- const partialMeta = partial.meta!;
91
+ const partialMeta = meta(partial)!;
73
92
  const subContent = await partial.text();
74
93
  const range = await ModelBlobUtil.enforceRange({ start: 10, end: 20 }, partialMeta.size!);
75
94
  assert(subContent.length === (range.end - range.start) + 1);
@@ -79,7 +98,7 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
79
98
  assert(subContent === og.substring(10, 21));
80
99
 
81
100
  const partialUnbounded = await service.getBlob(id, { start: 10 });
82
- const partialUnboundedMeta = partial.meta!;
101
+ const partialUnboundedMeta = meta(partial)!;
83
102
  const subContent2 = await partialUnbounded.text();
84
103
  const range2 = await ModelBlobUtil.enforceRange({ start: 10 }, partialUnboundedMeta.size!);
85
104
  assert(subContent2.length === (range2.end - range2.start) + 1);
@@ -107,7 +126,7 @@ export abstract class ModelBlobSuite extends BaseModelSuite<ModelBlobSupport> {
107
126
  const buffer = await this.fixture.read('/asset.yml', true);
108
127
  await service.upsertBlob('orange', buffer, { contentType: 'text/yaml', filename: 'asset.yml' });
109
128
  const saved = await service.getBlob('orange');
110
- const savedMeta = saved.meta!;
129
+ const savedMeta = meta(saved)!;
111
130
 
112
131
  assert('text/yaml' === savedMeta.contentType);
113
132
  assert(buffer.length === savedMeta.size);
package/src/util/blob.ts DELETED
@@ -1,60 +0,0 @@
1
- import { Readable } from 'node:stream';
2
- import path from 'node:path';
3
-
4
- import { AppError, BinaryInput, BlobMeta, ByteRange, Util } from '@travetto/runtime';
5
-
6
- /**
7
- * Utilities for processing assets
8
- */
9
- export class ModelBlobUtil {
10
-
11
- /**
12
- * Get a hashed location/path for a blob
13
- */
14
- static getHashedLocation(meta: BlobMeta, prefix = ''): string {
15
- const hash = meta.hash ?? Util.uuid();
16
-
17
- let parts = hash.match(/(.{1,4})/g)!.slice();
18
- if (parts.length > 4) {
19
- parts = [...parts.slice(0, 4), parts.slice(4).join('')];
20
- }
21
-
22
- const ext = path.extname(meta.filename ?? '') || '.bin';
23
- return `${parts.join('/')}${ext}`;
24
- }
25
-
26
- /**
27
- * Convert input to a blob, containing all data in memory
28
- */
29
- static async getInput(src: BinaryInput, metadata: BlobMeta = {}): Promise<[Readable, BlobMeta]> {
30
- let input: Readable;
31
- if (src instanceof Blob) {
32
- metadata = { ...src.meta, ...metadata };
33
- metadata.size ??= src.size;
34
- input = Readable.fromWeb(src.stream());
35
- } else if (typeof src === 'object' && 'pipeThrough' in src) {
36
- input = Readable.fromWeb(src);
37
- } else if (typeof src === 'object' && 'pipe' in src) {
38
- input = src;
39
- } else {
40
- metadata.size = src.length;
41
- input = Readable.from(src);
42
- }
43
-
44
- return [input, metadata ?? {}];
45
- }
46
-
47
- /**
48
- * Enforce byte range for stream stream/file of a certain size
49
- */
50
- static enforceRange({ start, end }: ByteRange, size: number): Required<ByteRange> {
51
- // End is inclusive
52
- end = Math.min(end ?? (size - 1), size - 1);
53
-
54
- if (Number.isNaN(start) || Number.isNaN(end) || !Number.isFinite(start) || start >= size || start < 0 || start > end) {
55
- throw new AppError('Invalid position, out of range', 'data');
56
- }
57
-
58
- return { start, end };
59
- }
60
- }