@toa.io/extensions.storages 1.0.0-alpha.3 → 1.0.0-alpha.300

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.
Files changed (116) hide show
  1. package/package.json +37 -19
  2. package/readme.md +129 -63
  3. package/transpiled/Aspect.d.ts +8 -4
  4. package/transpiled/Aspect.js +12 -14
  5. package/transpiled/Aspect.js.map +1 -1
  6. package/transpiled/Entry.d.ts +14 -11
  7. package/transpiled/Entry.js +1 -2
  8. package/transpiled/Factory.d.ts +1 -1
  9. package/transpiled/Factory.js +29 -30
  10. package/transpiled/Factory.js.map +1 -1
  11. package/transpiled/Provider.d.ts +17 -17
  12. package/transpiled/Provider.js +10 -33
  13. package/transpiled/Provider.js.map +1 -1
  14. package/transpiled/Scanner.d.ts +5 -4
  15. package/transpiled/Scanner.js +55 -34
  16. package/transpiled/Scanner.js.map +1 -1
  17. package/transpiled/Secrets.d.ts +5 -0
  18. package/transpiled/Secrets.js +2 -0
  19. package/transpiled/Secrets.js.map +1 -0
  20. package/transpiled/Storage.d.ts +23 -24
  21. package/transpiled/Storage.js +82 -166
  22. package/transpiled/Storage.js.map +1 -1
  23. package/transpiled/errors.d.ts +7 -0
  24. package/transpiled/errors.js +4 -0
  25. package/transpiled/errors.js.map +1 -0
  26. package/transpiled/index.d.ts +4 -4
  27. package/transpiled/index.js +1 -9
  28. package/transpiled/index.js.map +1 -1
  29. package/transpiled/providers/Cloudinary.d.ts +50 -0
  30. package/transpiled/providers/Cloudinary.js +223 -0
  31. package/transpiled/providers/Cloudinary.js.map +1 -0
  32. package/transpiled/providers/Declaration.d.ts +9 -5
  33. package/transpiled/providers/Declaration.js +1 -2
  34. package/transpiled/providers/FileSystem.d.ts +16 -9
  35. package/transpiled/providers/FileSystem.js +66 -36
  36. package/transpiled/providers/FileSystem.js.map +1 -1
  37. package/transpiled/providers/S3.d.ts +14 -16
  38. package/transpiled/providers/S3.js +74 -106
  39. package/transpiled/providers/S3.js.map +1 -1
  40. package/transpiled/providers/Spaces.d.ts +12 -0
  41. package/transpiled/providers/Spaces.js +13 -0
  42. package/transpiled/providers/Spaces.js.map +1 -0
  43. package/transpiled/providers/Temporary.d.ts +2 -3
  44. package/transpiled/providers/Temporary.js +7 -11
  45. package/transpiled/providers/Temporary.js.map +1 -1
  46. package/transpiled/providers/Test.d.ts +4 -4
  47. package/transpiled/providers/Test.js +6 -12
  48. package/transpiled/providers/Test.js.map +1 -1
  49. package/transpiled/providers/index.d.ts +23 -11
  50. package/transpiled/providers/index.js +30 -14
  51. package/transpiled/providers/index.js.map +1 -1
  52. package/transpiled/providers/index.types.d.ts +5 -0
  53. package/transpiled/providers/index.types.js +2 -0
  54. package/transpiled/providers/index.types.js.map +1 -0
  55. package/transpiled/test/util.d.ts +113 -6
  56. package/transpiled/test/util.js +68 -15
  57. package/transpiled/test/util.js.map +1 -1
  58. package/schemas/annotation.cos.yaml +0 -10
  59. package/schemas/fs.cos.yaml +0 -9
  60. package/schemas/mem.cos.yaml +0 -6
  61. package/schemas/s3.cos.yaml +0 -16
  62. package/schemas/test.cos.yaml +0 -8
  63. package/schemas/tmp.cos.yaml +0 -9
  64. package/source/Annotation.ts +0 -39
  65. package/source/Aspect.ts +0 -25
  66. package/source/Entry.ts +0 -14
  67. package/source/Factory.ts +0 -68
  68. package/source/Provider.ts +0 -33
  69. package/source/Scanner.ts +0 -133
  70. package/source/Storage.test.ts +0 -473
  71. package/source/Storage.ts +0 -245
  72. package/source/deployment.ts +0 -57
  73. package/source/index.ts +0 -5
  74. package/source/manifest.ts +0 -8
  75. package/source/providers/Declaration.ts +0 -10
  76. package/source/providers/FileSystem.test.ts +0 -5
  77. package/source/providers/FileSystem.ts +0 -51
  78. package/source/providers/Memory.ts +0 -41
  79. package/source/providers/S3.test.ts +0 -133
  80. package/source/providers/S3.ts +0 -166
  81. package/source/providers/Temporary.ts +0 -16
  82. package/source/providers/Test.ts +0 -13
  83. package/source/providers/index.test.ts +0 -126
  84. package/source/providers/index.ts +0 -16
  85. package/source/providers/readme.md +0 -25
  86. package/source/schemas.test.ts +0 -58
  87. package/source/schemas.ts +0 -15
  88. package/source/test/.env.example +0 -1
  89. package/source/test/albert.jpg +0 -0
  90. package/source/test/empty.txt +0 -0
  91. package/source/test/lenna.ascii +0 -64
  92. package/source/test/lenna.png +0 -0
  93. package/source/test/sample.avif +0 -0
  94. package/source/test/sample.gif +0 -0
  95. package/source/test/sample.heic +0 -0
  96. package/source/test/sample.jpeg +0 -0
  97. package/source/test/sample.jxl +0 -0
  98. package/source/test/sample.webp +0 -0
  99. package/source/test/util.ts +0 -45
  100. package/transpiled/Annotation.d.ts +0 -3
  101. package/transpiled/Annotation.js +0 -57
  102. package/transpiled/Annotation.js.map +0 -1
  103. package/transpiled/deployment.d.ts +0 -5
  104. package/transpiled/deployment.js +0 -68
  105. package/transpiled/deployment.js.map +0 -1
  106. package/transpiled/manifest.d.ts +0 -1
  107. package/transpiled/manifest.js +0 -9
  108. package/transpiled/manifest.js.map +0 -1
  109. package/transpiled/providers/Memory.d.ts +0 -13
  110. package/transpiled/providers/Memory.js +0 -60
  111. package/transpiled/providers/Memory.js.map +0 -1
  112. package/transpiled/schemas.d.ts +0 -9
  113. package/transpiled/schemas.js +0 -14
  114. package/transpiled/schemas.js.map +0 -1
  115. package/transpiled/tsconfig.tsbuildinfo +0 -1
  116. package/tsconfig.json +0 -12
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@toa.io/extensions.storages",
3
- "version": "1.0.0-alpha.3",
3
+ "version": "1.0.0-alpha.300",
4
+ "type": "module",
4
5
  "description": "Toa Storages",
5
6
  "author": "temich <tema.gurtovoy@gmail.com>",
6
7
  "homepage": "https://github.com/toa-io/toa#readme",
@@ -16,28 +17,45 @@
16
17
  "publishConfig": {
17
18
  "access": "public"
18
19
  },
19
- "jest": {
20
- "preset": "ts-jest",
21
- "testEnvironment": "node"
22
- },
20
+ "files": [
21
+ "transpiled",
22
+ "readme.md",
23
+ "!**/tsconfig.tsbuildinfo",
24
+ "!**/*.test.*",
25
+ "!**/.env*"
26
+ ],
23
27
  "scripts": {
24
- "test": "jest",
28
+ "test": "echo \"Error: run tests from root\" && exit 1",
25
29
  "transpile": "npx tsc"
26
30
  },
27
31
  "devDependencies": {
28
- "@toa.io/streams": "1.0.0-alpha.3",
29
- "dotenv": "16.3.1"
32
+ "@aws-sdk/client-s3": "3.1125.0",
33
+ "@aws-sdk/lib-storage": "3.1125.0",
34
+ "cloudinary": "2.11.0",
35
+ "dotenv": "17.4.2"
36
+ },
37
+ "peerDependencies": {
38
+ "@aws-sdk/client-s3": "3.1125.0",
39
+ "@aws-sdk/lib-storage": "3.1125.0",
40
+ "cloudinary": "2.11.0"
41
+ },
42
+ "peerDependenciesMeta": {
43
+ "@aws-sdk/client-s3": {
44
+ "optional": true
45
+ },
46
+ "@aws-sdk/lib-storage": {
47
+ "optional": true
48
+ },
49
+ "cloudinary": {
50
+ "optional": true
51
+ }
30
52
  },
31
53
  "dependencies": {
32
- "@aws-sdk/client-s3": "3.481.0",
33
- "@aws-sdk/lib-storage": "3.481.0",
34
- "@toa.io/agent": "1.0.0-alpha.3",
35
- "@toa.io/generic": "1.0.0-alpha.3",
36
- "@toa.io/schemas": "1.0.0-alpha.3",
37
- "error-value": "0.3.0",
38
- "matchacho": "0.6.0",
39
- "msgpackr": "1.10.1",
40
- "smithy-node-native-fetch": "1.0.6"
41
- },
42
- "gitHead": "e36ac7871fc14d15863aaf8f9bbdeace8bdfa9f0"
54
+ "@toa.io/definitions": "1.0.0-alpha.300",
55
+ "@toa.io/generic": "1.0.0-alpha.292",
56
+ "@toa.io/schemas": "1.0.0-alpha.299",
57
+ "negotiator": "1.1.0",
58
+ "openspan": "1.0.0-alpha.293"
59
+ },
60
+ "gitHead": "f91131a9aeb29aa271f177c4055ddcf0cda8c75e"
43
61
  }
package/readme.md CHANGED
@@ -2,19 +2,30 @@
2
2
 
3
3
  Shared BLOB storage.
4
4
 
5
+ ## Installing a provider
6
+
7
+ A provider's SDK is not installed with this extension: `s3` and `spaces` need
8
+ `@aws-sdk/client-s3` and `@aws-sdk/lib-storage`, `cloudinary` needs `cloudinary`, and `fs`, `tmp`
9
+ and `test` need nothing. A deploy installs what the components of each composition declare, so
10
+ an image carries the SDK of a provider it uses and no other.
11
+
12
+ A workspace installs the same set itself, once, and again when a storage is added:
13
+
14
+ ```shell
15
+ $ toa npm -p application
16
+ ```
17
+
18
+ Without it a composition fails to start, naming the package and version it wants.
19
+
5
20
  ## Entry
6
21
 
7
22
  BLOBs are stored with the meta-information object (Entry) having the following properties:
8
23
 
9
- - `id` - checksum
10
- - `size` - size in bytes
11
- - `type` - MIME type
12
- - `created` - creation timestamp (UNIX time, ms)
13
- - `variants` - array of:
14
- - `name` - unique name
15
- - `size` - size in bytes
16
- - `type` - variant MIME type
17
- - `meta` - object with application-specific information, empty by default
24
+ - `size`: size in bytes
25
+ - `type`: MIME type
26
+ - `checksum`: content checksum
27
+ - `created`: creation timestamp (ISO 8601)
28
+ - `attributes`: `Record<string, string>` with application-specific information, empty by default
18
29
 
19
30
  ### Example
20
31
 
@@ -22,12 +33,7 @@ BLOBs are stored with the meta-information object (Entry) having the following p
22
33
  id: eecd837c
23
34
  type: image/jpeg
24
35
  created: 1698004822358
25
- variants:
26
- - name: thumbnail.jpeg
27
- type: image/jpeg
28
- - name: thumbnail.webp
29
- type: image/webp
30
- meta:
36
+ attributes:
31
37
  face: true
32
38
  nudity: false
33
39
  ```
@@ -38,8 +44,8 @@ The Storages extension provides `storages` aspect,
38
44
  containing named Storage instances, according to the annotation.
39
45
 
40
46
  ```javascript
41
- async function effect (_, context) {
42
- await context.storages.photos.fetch('/path/to/b4f577e0.thumbnail.jpeg')
47
+ async function effect(_, context) {
48
+ await context.storages.photos.get('/path/to/b4f577e0.thumbnail.jpeg')
43
49
  }
44
50
  ```
45
51
 
@@ -49,11 +55,11 @@ async function effect (_, context) {
49
55
 
50
56
  #### `async put(path: string, stream: Readable, options?: Options): Maybe<Entry>`
51
57
 
52
- ```
58
+ ```ts
53
59
  interface Options {
54
60
  claim?: string
55
61
  accept?: string
56
- meta?: Record<string, string>
62
+ attributes?: Record<string, string>
57
63
  }
58
64
  ```
59
65
 
@@ -75,15 +81,13 @@ are: `image/jpeg`, `image/png`, `image/gif`, `image/webp`, `image/heic`, `image/
75
81
 
76
82
  See [source](source/Scanner.ts).
77
83
 
78
- If the entry already exists, it is returned and [revealed](#async-revealpath-string-maybevoid).
79
-
80
- #### `async get(path: string): Maybe<Entry>`
84
+ #### `async head(path: string): Maybe<Entry>`
81
85
 
82
86
  Get an entry.
83
87
 
84
88
  If the entry does not exist, a `NOT_FOUND` error is returned.
85
89
 
86
- #### `async fetch(path: string): Maybe<Readable>`
90
+ #### `async get(path: string): Maybe<Readable>`
87
91
 
88
92
  Fetch the BLOB specified by `path`. If the path does not exist, a `NOT_FOUND` error is returned.
89
93
 
@@ -97,32 +101,20 @@ Fetch the BLOB specified by `path`. If the path does not exist, a `NOT_FOUND` er
97
101
 
98
102
  Delete the entry specified by `path`.
99
103
 
100
- #### `async list(path: string): string[]`
101
-
102
- Get ordered list of `id`s of entries in under the `path`.
103
-
104
- #### `async permute(path: string, ids: string[]): Maybe<void>`
105
-
106
- Reorder entries under the `path`.
107
-
108
- Given list must be a permutation of the current list, otherwise a `PERMUTATION_MISMATCH` error is
109
- returned.
110
-
111
- #### `async diversify(path: string, name: string, stream: Readable): Maybe<void>`
112
-
113
- Add or replace a `name` variant of the entry specified by `path`.
114
-
115
- #### `async conceal(path: string): Maybe<void>`
104
+ #### `async move(path: string, to: string): Maybe<void>`
116
105
 
117
- Remove the entry from the list.
106
+ Moves the entry specified by `path` to the new path.
118
107
 
119
- #### `async reveal(path: string): Maybe<void>`
108
+ `to` may be an absolute or relative path (starting with `.`), if it ends with `/`, the entry is
109
+ moved to the `to` directory, otherwise, the entry is moved to the `to` path.
120
110
 
121
- Restore the entry to the list.
111
+ The following examples are equivalent:
122
112
 
123
- #### `async annotate(path: string, key: string, value: any): Maybe<void>`
124
-
125
- Set a `key` property in the `meta` of the entry specified by `path`.
113
+ ```javascript
114
+ await storage.move('/path/to/eecd837c', '/path/to/sub/eecd837c')
115
+ await storage.move('/path/to/eecd837c', './sub/eecd837c')
116
+ await storage.move('/path/to/eecd837c', './sub/')
117
+ ```
126
118
 
127
119
  ## Providers
128
120
 
@@ -132,7 +124,7 @@ Custom providers are not supported.
132
124
 
133
125
  ### Amazon S3
134
126
 
135
- Annotation formats is like:
127
+ Annotation formats are like:
136
128
 
137
129
  ```yaml
138
130
  storages:
@@ -154,40 +146,114 @@ and [`toa env`](/runtime/cli/readme.md#env)
154
146
  for local environment.
155
147
  `endpoint` parameter is optional.
156
148
 
157
- ### Filesystem
149
+ ### DigitalOcean Spaces
158
150
 
159
151
  Annotation format is:
160
152
 
161
153
  ```yaml
162
154
  storages:
163
- photos@dev:
164
- provider: fs
165
- path: /var/my-storage
155
+ photos:
156
+ provider: spaces
157
+ space: my-space
158
+ region: fra1
166
159
  ```
167
160
 
168
- ### Temporary
161
+ `region` is the datacenter slug, the endpoint is derived from it.
169
162
 
170
- Filesystem using OS temporary directory.
163
+ Secrets `ACCESS_KEY_ID` and `SECRET_ACCESS_KEY` are required.
164
+ Keys are created in the DigitalOcean control panel,
165
+ under Spaces Object Storage, on the Access Keys tab.
166
+
167
+ ### Cloudinary
168
+
169
+ [Cloudinary](https://cloudinary.com) provider is used to store and transform media files.
170
+
171
+ Objects stored in the Cloudinary storage can be requested with transformations,
172
+ using dot-separated path extensions: `/path/to/eecd837c.{extension}.{extension}`.
173
+
174
+ Transformation is defined by the `extension` property, which is a regular expression with named
175
+ groups.
176
+ The named groups are used to replace the placeholders in the `transform` object.
177
+
178
+ `transform` object is an element of
179
+ the [Cloudinary `transformation` array](https://cloudinary.com/documentation/node_image_manipulation#apply_common_image_transformations).
180
+
181
+ > - `zoom` should be specified as integer [0–100].
182
+ > - `format` value `jpeg` is converted to `jpg`.
183
+ > - `^` and `$` are added to the regular expression automatically, unless they are already present.
171
184
 
172
185
  Annotation format is:
173
186
 
174
187
  ```yaml
175
188
  storages:
176
- photos@dev:
177
- provider: tmp
178
- directory: my-app-tmp
189
+ media:
190
+ provider: cloudinary
191
+ environment: my-cloud
192
+ type: image # image or video
193
+ prefix: my-app
194
+ transformations:
195
+ - extension: (?<width>\d*)x(?<height>\d*)(z(?<zoom>\d*))?
196
+ transformation:
197
+ width: <width>
198
+ height: <height>
199
+ zoom: <zoom>
200
+ crop: thumb
201
+ gravity: face
202
+ optional: true
203
+ - extension: (?<format>jpeg|webp)
204
+ transformation:
205
+ fetch_format: <format>
206
+ ```
207
+
208
+ Path extensions must be specified in the same order as in the annotation.
209
+
210
+ For the example above:
211
+
212
+ - `/path/to/eecd837c.100x100.jpeg`: matches
213
+ - `/path/to/eecd837c.jpeg.100x100`: does not match
214
+
215
+ If a path extension is not matched,
216
+ or if at least one of the transformations is not matched (unless it is `optional`),
217
+ then an error is returned.
218
+
219
+ Secrets:
220
+
221
+ - `API_KEY`
222
+ - `API_SECRET`
223
+
224
+ ### Filesystem
225
+
226
+ Annotation format is:
227
+
228
+ ```yaml
229
+ storages:
230
+ photos:
231
+ provider: fs
232
+ path: /var/my-photos
233
+ ```
234
+
235
+ [Kubernetes PVC](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) can be mounted to
236
+ the storage:
237
+
238
+ ```yaml
239
+ storages:
240
+ photos:
241
+ provider: fs
242
+ path: /var/my-photos
243
+ claim: photos-pvc
179
244
  ```
180
245
 
181
- ### Memory
246
+ ### Temporary
182
247
 
183
- In-memory non-persistent storage.
248
+ Filesystem using OS temporary directory.
184
249
 
185
- Annotation value format is:
250
+ Annotation format is:
186
251
 
187
252
  ```yaml
188
253
  storages:
189
254
  photos@dev:
190
- provider: mem
255
+ provider: tmp
256
+ directory: my-app-tmp
191
257
  ```
192
258
 
193
259
  ## Deduplication
@@ -198,17 +264,17 @@ Variants, on the other hand, are not deduplicated across different entries.
198
264
 
199
265
  Underlying directory structure:
200
266
 
201
- ```
267
+ ```text
202
268
  /temp
203
269
  c28f4dfd # random id
204
270
  /blobs
205
271
  b4f577e0 # checksum
206
272
  /storage
207
273
  /path/to
208
- .list # list of entries
274
+ /.entries/
275
+ b4f577e0 # entry
209
276
  /b4f577e0
210
- .meta # entry
211
- thumbnail.jpeg # variant BLOBs
277
+ thumbnail.jpeg # variant
212
278
  thumbnail.webp
213
279
  ```
214
280
 
@@ -1,8 +1,12 @@
1
- import { Connector, type extensions } from '@toa.io/core';
2
- import { type Storage, type Storages } from './Storage';
1
+ import { Connector } from '@toa.io/core';
2
+ import type { extensions } from '@toa.io/core/types';
3
+ import { type Storage, type Storages } from './Storage.js';
3
4
  export declare class Aspect extends Connector implements extensions.Aspect {
4
5
  readonly name = "storages";
5
- private readonly storages;
6
- constructor(storages: Storages);
6
+ private readonly create;
7
+ private storages;
8
+ /** The storages are made as the aspect connects: their providers load then, not before. */
9
+ constructor(create: () => Promise<Storages>);
10
+ protected open(): Promise<void>;
7
11
  invoke(name: string, method: keyof Storage, ...args: unknown[]): unknown;
8
12
  }
@@ -1,25 +1,23 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.Aspect = void 0;
7
- const node_assert_1 = __importDefault(require("node:assert"));
8
- const core_1 = require("@toa.io/core");
9
- class Aspect extends core_1.Connector {
1
+ import { Connector } from '@toa.io/core';
2
+ export class Aspect extends Connector {
10
3
  name = 'storages';
11
- storages;
12
- constructor(storages) {
4
+ create;
5
+ storages = {};
6
+ /** The storages are made as the aspect connects: their providers load then, not before. */
7
+ constructor(create) {
13
8
  super();
14
- this.storages = storages;
9
+ this.create = create;
10
+ }
11
+ async open() {
12
+ this.storages = await this.create();
15
13
  }
16
14
  invoke(name, method, ...args) {
17
15
  const storage = this.storages[name];
18
- node_assert_1.default.ok(storage !== undefined, `Storage '${name}' is not defined`);
16
+ if (storage === undefined)
17
+ throw new Error(`Storage '${name}' is not defined`);
19
18
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
20
19
  // @ts-expect-error
21
20
  return storage[method](...args);
22
21
  }
23
22
  }
24
- exports.Aspect = Aspect;
25
23
  //# sourceMappingURL=Aspect.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Aspect.js","sourceRoot":"","sources":["../source/Aspect.ts"],"names":[],"mappings":";;;;;;AAAA,8DAAgC;AAChC,uCAAyD;AAGzD,MAAa,MAAO,SAAQ,gBAAS;IACnB,IAAI,GAAG,UAAU,CAAA;IAEhB,QAAQ,CAAU;IAEnC,YAAoB,QAAkB;QACpC,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;IAC1B,CAAC;IAEM,MAAM,CAAE,IAAY,EAAE,MAAqB,EAAE,GAAG,IAAe;QACpE,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QAEnC,qBAAM,CAAC,EAAE,CAAC,OAAO,KAAK,SAAS,EAAE,YAAY,IAAI,kBAAkB,CAAC,CAAA;QAEpE,6DAA6D;QAC7D,mBAAmB;QACnB,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;IACjC,CAAC;CACF;AApBD,wBAoBC"}
1
+ {"version":3,"file":"Aspect.js","sourceRoot":"","sources":["../source/Aspect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAIxC,MAAM,OAAO,MAAO,SAAQ,SAAS;IACnB,IAAI,GAAG,UAAU,CAAA;IAEhB,MAAM,CAAyB;IACxC,QAAQ,GAAa,EAAE,CAAA;IAE/B,2FAA2F;IAC3F,YAAmB,MAA+B;QAChD,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;IAEkB,KAAK,CAAC,IAAI;QAC3B,IAAI,CAAC,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAA;IACrC,CAAC;IAEM,MAAM,CAAC,IAAY,EAAE,MAAqB,EAAE,GAAG,IAAe;QACnE,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QAEnC,IAAI,OAAO,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,kBAAkB,CAAC,CAAA;QAE9E,6DAA6D;QAC7D,mBAAmB;QACnB,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;IACjC,CAAC;CACF"}
@@ -1,14 +1,17 @@
1
- export interface Entry {
1
+ import type { Readable } from 'node:stream';
2
+ export type Entry = {
2
3
  id: string;
3
- size: number;
4
+ } & Metadata;
5
+ export type Stream = {
6
+ stream: Readable;
7
+ } & Metadata;
8
+ export interface Metadata {
4
9
  type: string;
5
- created: number;
6
- variants: Variant[];
7
- meta: Record<string, unknown>;
10
+ size: number | null;
11
+ checksum: string;
12
+ created: string;
13
+ attributes: Attributes;
14
+ range?: string;
15
+ partial?: boolean;
8
16
  }
9
- interface Variant {
10
- name: string;
11
- size: number;
12
- type: string;
13
- }
14
- export {};
17
+ export type Attributes = Record<string, string>;
@@ -1,3 +1,2 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ export {};
3
2
  //# sourceMappingURL=Entry.js.map
@@ -1,4 +1,4 @@
1
- import { Aspect } from './Aspect';
1
+ import { Aspect } from './Aspect.js';
2
2
  export declare class Factory {
3
3
  private readonly annotation;
4
4
  constructor();
@@ -1,53 +1,52 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.Factory = void 0;
7
- const node_assert_1 = __importDefault(require("node:assert"));
8
- const generic_1 = require("@toa.io/generic");
9
- const providers_1 = require("./providers");
10
- const Storage_1 = require("./Storage");
11
- const Aspect_1 = require("./Aspect");
12
- const deployment_1 = require("./deployment");
13
- const Annotation_1 = require("./Annotation");
14
- class Factory {
1
+ import { console } from 'openspan';
2
+ import { provider as load } from './providers/index.js';
3
+ import { Storage } from './Storage.js';
4
+ import { Aspect } from './Aspect.js';
5
+ import { ENV_PREFIX, validateAnnotation } from '@toa.io/definitions/extensions.storages';
6
+ import { environment } from '@toa.io/generic';
7
+ export class Factory {
15
8
  annotation;
16
9
  constructor() {
17
- const env = process.env.TOA_STORAGES;
18
- node_assert_1.default.ok(env !== undefined, 'TOA_STORAGES is not defined');
19
- this.annotation = (0, generic_1.decode)(env);
20
- (0, Annotation_1.validateAnnotation)(this.annotation);
10
+ const env = environment.get(ENV_PREFIX);
11
+ if (env === undefined)
12
+ throw new Error(`${ENV_PREFIX} is not defined`);
13
+ this.annotation = JSON.parse(env);
14
+ validateAnnotation(this.annotation);
21
15
  }
22
16
  aspect() {
23
- const storages = this.createStorages();
24
- return new Aspect_1.Aspect(storages);
17
+ return new Aspect(() => this.createStorages());
25
18
  }
26
- createStorages() {
19
+ async createStorages() {
27
20
  const storages = {};
28
21
  for (const [name, declaration] of Object.entries(this.annotation))
29
- storages[name] = this.createStorage(name, declaration);
22
+ // the annotation is validated above, so a declaration is its provider's
23
+ storages[name] = await this.createStorage(name, declaration);
30
24
  return storages;
31
25
  }
32
- createStorage(name, declaration) {
33
- const { provider: providerId, ...options } = declaration;
34
- const Provider = providers_1.providers[providerId];
26
+ async createStorage(name, declaration) {
27
+ const { provider: id, ...options } = declaration;
28
+ const Provider = await load(id);
35
29
  const secrets = this.resolveSecrets(name, Provider);
36
30
  const provider = new Provider(options, secrets);
37
- return new Storage_1.Storage(provider);
31
+ console.debug('Storage created', {
32
+ name,
33
+ provider: id,
34
+ ...(provider.root === undefined ? undefined : { root: provider.root })
35
+ });
36
+ return new Storage(provider, { name, provider: id });
38
37
  }
39
38
  resolveSecrets(storageName, Class) {
40
39
  if (Class.SECRETS === undefined)
41
40
  return {};
42
41
  const secrets = {};
43
42
  for (const secret of Class.SECRETS) {
44
- const variable = `${deployment_1.SERIALIZATION_PREFIX}_${storageName}_${secret.name}`.toUpperCase();
45
- const value = process.env[variable];
46
- node_assert_1.default.ok(secret.optional === true || value !== undefined, `'${variable}' is not defined`);
43
+ const variable = `${ENV_PREFIX}_${storageName}_${secret.name}`.toUpperCase();
44
+ const value = environment.get(variable);
45
+ if (secret.optional !== true && value === undefined)
46
+ throw new Error(`'${variable}' is not defined`);
47
47
  secrets[secret.name] = value;
48
48
  }
49
49
  return secrets;
50
50
  }
51
51
  }
52
- exports.Factory = Factory;
53
52
  //# sourceMappingURL=Factory.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Factory.js","sourceRoot":"","sources":["../source/Factory.ts"],"names":[],"mappings":";;;;;;AAAA,8DAAgC;AAChC,6CAAwC;AACxC,2CAAuC;AACvC,uCAAkD;AAClD,qCAAiC;AACjC,6CAAmD;AACnD,6CAAiD;AAKjD,MAAa,OAAO;IACD,UAAU,CAAY;IAEvC;QACE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAA;QAEpC,qBAAM,CAAC,EAAE,CAAC,GAAG,KAAK,SAAS,EAAE,6BAA6B,CAAC,CAAA;QAE3D,IAAI,CAAC,UAAU,GAAG,IAAA,gBAAM,EAAC,GAAG,CAAC,CAAA;QAE7B,IAAA,+BAAkB,EAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACrC,CAAC;IAEM,MAAM;QACX,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,CAAA;QAEtC,OAAO,IAAI,eAAM,CAAC,QAAQ,CAAC,CAAA;IAC7B,CAAC;IAEO,cAAc;QACpB,MAAM,QAAQ,GAAa,EAAE,CAAA;QAE7B,KAAK,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC;YAC/D,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;QAExD,OAAO,QAAQ,CAAA;IACjB,CAAC;IAEO,aAAa,CAAE,IAAY,EAAE,WAAwB;QAC3D,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,GAAG,WAAW,CAAA;QACxD,MAAM,QAAQ,GAAwB,qBAAS,CAAC,UAAU,CAAC,CAAA;QAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;QACnD,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAE/C,OAAO,IAAI,iBAAO,CAAC,QAAQ,CAAC,CAAA;IAC9B,CAAC;IAEO,cAAc,CAAE,WAAmB,EACzC,KAA0B;QAC1B,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;YAC7B,OAAO,EAAE,CAAA;QAEX,MAAM,OAAO,GAAuC,EAAE,CAAA;QAEtD,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnC,MAAM,QAAQ,GAAG,GAAG,iCAAoB,IAAI,WAAW,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;YACtF,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YAEnC,qBAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EACvD,IAAI,QAAQ,kBAAkB,CAAC,CAAA;YAEjC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAA;QAC9B,CAAC;QAED,OAAO,OAAO,CAAA;IAChB,CAAC;CACF;AAxDD,0BAwDC"}
1
+ {"version":3,"file":"Factory.js","sourceRoot":"","sources":["../source/Factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAClC,OAAO,EAAE,QAAQ,IAAI,IAAI,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,OAAO,EAAiB,MAAM,cAAc,CAAA;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAA;AACxF,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAM7C,MAAM,OAAO,OAAO;IACD,UAAU,CAAY;IAEvC;QACE,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;QAEvC,IAAI,GAAG,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,GAAG,UAAU,iBAAiB,CAAC,CAAA;QAEtE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAEjC,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACrC,CAAC;IAEM,MAAM;QACX,OAAO,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAA;IAChD,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,MAAM,QAAQ,GAAa,EAAE,CAAA;QAE7B,KAAK,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC;YAC/D,wEAAwE;YACxE,QAAQ,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,WAA0B,CAAC,CAAA;QAE7E,OAAO,QAAQ,CAAA;IACjB,CAAC;IAEO,KAAK,CAAC,aAAa,CAAC,IAAY,EAAE,WAAwB;QAChE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,GAAG,OAAO,EAAE,GAAG,WAAW,CAAA;QAChD,MAAM,QAAQ,GAAgB,MAAM,IAAI,CAAC,EAAE,CAAC,CAAA;QAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;QACnD,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAE/C,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE;YAC/B,IAAI;YACJ,QAAQ,EAAE,EAAE;YACZ,GAAG,CAAC,QAAQ,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;SACvE,CAAC,CAAA;QAEF,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAA;IACtD,CAAC;IAEO,cAAc,CAAC,WAAmB,EAAE,KAAkB;QAC5D,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;YAAE,OAAO,EAAE,CAAA;QAE1C,MAAM,OAAO,GAAuC,EAAE,CAAA;QAEtD,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnC,MAAM,QAAQ,GAAG,GAAG,UAAU,IAAI,WAAW,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;YAC5E,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YAEvC,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;gBACjD,MAAM,IAAI,KAAK,CAAC,IAAI,QAAQ,kBAAkB,CAAC,CAAA;YAEjD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAA;QAC9B,CAAC;QAED,OAAO,OAAO,CAAA;IAChB,CAAC;CACF"}
@@ -1,20 +1,20 @@
1
- /// <reference types="node" />
2
- import { type Readable } from 'node:stream';
3
- export type ProviderSecrets<K extends string = string> = Record<K | string, string | undefined>;
4
- export interface ProviderSecret {
5
- readonly name: string;
6
- readonly optional?: boolean;
7
- }
8
- export declare abstract class Provider<Options = void> {
9
- static readonly SECRETS: readonly ProviderSecret[];
10
- constructor(_: Options, secrets?: ProviderSecrets);
11
- abstract get(path: string): Promise<Readable | null>;
12
- abstract put(path: string, filename: string, stream: Readable): Promise<void>;
1
+ import type { Metadata, Stream } from './Entry.js';
2
+ import type { Readable } from 'node:stream';
3
+ import type { Maybe } from '@toa.io/core/types';
4
+ import type { Secret, Secrets } from './Secrets.js';
5
+ export declare abstract class Provider<Options = unknown> {
6
+ static readonly SECRETS?: readonly Secret[];
7
+ readonly root?: string;
8
+ readonly options: Options;
9
+ protected constructor(options: Options, secrets?: Secrets);
10
+ abstract get(path: string, options?: unknown): Promise<Maybe<Stream>>;
11
+ abstract head(path: string): Promise<Maybe<Metadata>>;
12
+ abstract put(path: string, stream: Readable): Promise<void>;
13
+ abstract commit(path: string, metadata: Metadata): Promise<void>;
13
14
  abstract delete(path: string): Promise<void>;
14
- abstract move(from: string, to: string): Promise<void>;
15
+ abstract move(from: string, to: string): Promise<Maybe<void>>;
15
16
  }
16
- export interface ProviderConstructor {
17
- readonly SECRETS: readonly ProviderSecret[];
18
- prototype: Provider;
19
- new (options: any, secrets?: ProviderSecrets): Provider;
17
+ export interface Constructor<Options = any> {
18
+ SECRETS?: readonly Secret[];
19
+ new (options: Options, secrets?: Secrets): Provider<Options>;
20
20
  }
@@ -1,36 +1,13 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.Provider = void 0;
27
- const assert = __importStar(require("node:assert"));
28
- class Provider {
29
- static SECRETS = [];
30
- constructor(_, secrets) {
31
- for (const { name, optional = false } of new.target.SECRETS)
32
- assert.ok(optional || secrets?.[name] !== undefined, `Missing secret '${name}'`);
1
+ export class Provider {
2
+ static SECRETS;
3
+ root;
4
+ options;
5
+ constructor(options, secrets) {
6
+ this.options = options;
7
+ new.target.SECRETS?.forEach(({ name, optional }) => {
8
+ if (optional !== true && secrets?.[name] === undefined)
9
+ throw new Error(`Missing secret '${name}'`);
10
+ });
33
11
  }
34
12
  }
35
- exports.Provider = Provider;
36
13
  //# sourceMappingURL=Provider.js.map