@toa.io/extensions.storages 1.0.0-alpha.28 → 1.0.0-alpha.282
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/CHANGELOG.md +74 -0
- package/package.json +12 -18
- package/readme.md +99 -46
- package/schemas/annotation.cos.yaml +3 -2
- package/schemas/cloudinary.cos.yaml +38 -0
- package/schemas/fs.cos.yaml +5 -1
- package/schemas/s3.cos.yaml +4 -3
- package/schemas/spaces.cos.yaml +16 -0
- package/schemas/test.cos.yaml +3 -1
- package/schemas/tmp.cos.yaml +3 -2
- package/source/Annotation.ts +3 -3
- package/source/Aspect.ts +1 -1
- package/source/Entry.ts +13 -11
- package/source/Factory.ts +24 -18
- package/source/Provider.ts +21 -22
- package/source/Scanner.ts +32 -13
- package/source/Secrets.ts +6 -0
- package/source/Storage.test.ts +146 -301
- package/source/Storage.ts +97 -201
- package/source/deployment.ts +56 -21
- package/source/errors.ts +3 -0
- package/source/index.ts +6 -4
- package/source/manifest.ts +7 -6
- package/source/providers/Cloudinary.ts +320 -0
- package/source/providers/Declaration.ts +7 -4
- package/source/providers/FileSystem.ts +74 -32
- package/source/providers/S3.ts +75 -107
- package/source/providers/Spaces.ts +24 -0
- package/source/providers/Temporary.ts +3 -4
- package/source/providers/Test.ts +5 -5
- package/source/providers/index.test.ts +108 -88
- package/source/providers/index.ts +18 -9
- package/source/providers/readme.md +13 -11
- package/source/schemas.test.ts +14 -8
- package/source/schemas.ts +5 -4
- package/source/test/.env.example +8 -0
- package/source/test/arny.jpg +0 -0
- package/source/test/lenna.48x48.jpeg +0 -0
- package/source/test/plank.mp4 +0 -0
- package/source/test/sport.mp4 +0 -0
- package/source/test/util.ts +76 -13
- package/transpiled/Annotation.d.ts +1 -1
- package/transpiled/Annotation.js +8 -38
- package/transpiled/Annotation.js.map +1 -1
- package/transpiled/Aspect.d.ts +1 -1
- package/transpiled/Aspect.js +4 -11
- package/transpiled/Aspect.js.map +1 -1
- package/transpiled/Entry.d.ts +14 -11
- package/transpiled/Entry.js +1 -2
- package/transpiled/Factory.d.ts +1 -1
- package/transpiled/Factory.js +23 -25
- package/transpiled/Factory.js.map +1 -1
- package/transpiled/Provider.d.ts +17 -19
- package/transpiled/Provider.js +8 -33
- package/transpiled/Provider.js.map +1 -1
- package/transpiled/Scanner.d.ts +5 -4
- package/transpiled/Scanner.js +30 -19
- package/transpiled/Scanner.js.map +1 -1
- package/transpiled/Secrets.d.ts +5 -0
- package/transpiled/Secrets.js +2 -0
- package/transpiled/Secrets.js.map +1 -0
- package/transpiled/Storage.d.ts +23 -22
- package/transpiled/Storage.js +76 -142
- package/transpiled/Storage.js.map +1 -1
- package/transpiled/deployment.d.ts +1 -1
- package/transpiled/deployment.js +49 -50
- package/transpiled/deployment.js.map +1 -1
- package/transpiled/errors.d.ts +7 -0
- package/transpiled/errors.js +4 -0
- package/transpiled/errors.js.map +1 -0
- package/transpiled/index.d.ts +6 -4
- package/transpiled/index.js +3 -9
- package/transpiled/index.js.map +1 -1
- package/transpiled/manifest.js +6 -7
- package/transpiled/manifest.js.map +1 -1
- package/transpiled/providers/Cloudinary.d.ts +50 -0
- package/transpiled/providers/Cloudinary.js +219 -0
- package/transpiled/providers/Cloudinary.js.map +1 -0
- package/transpiled/providers/Declaration.d.ts +9 -5
- package/transpiled/providers/Declaration.js +1 -2
- package/transpiled/providers/FileSystem.d.ts +16 -11
- package/transpiled/providers/FileSystem.js +67 -43
- package/transpiled/providers/FileSystem.js.map +1 -1
- package/transpiled/providers/S3.d.ts +14 -19
- package/transpiled/providers/S3.js +70 -119
- package/transpiled/providers/S3.js.map +1 -1
- package/transpiled/providers/Spaces.d.ts +12 -0
- package/transpiled/providers/Spaces.js +15 -0
- package/transpiled/providers/Spaces.js.map +1 -0
- package/transpiled/providers/Temporary.d.ts +2 -3
- package/transpiled/providers/Temporary.js +7 -11
- package/transpiled/providers/Temporary.js.map +1 -1
- package/transpiled/providers/Test.d.ts +4 -4
- package/transpiled/providers/Test.js +4 -8
- package/transpiled/providers/Test.js.map +1 -1
- package/transpiled/providers/index.d.ts +15 -7
- package/transpiled/providers/index.js +13 -14
- package/transpiled/providers/index.js.map +1 -1
- package/transpiled/schemas.d.ts +4 -3
- package/transpiled/schemas.js +11 -13
- package/transpiled/schemas.js.map +1 -1
- package/transpiled/test/util.d.ts +113 -6
- package/transpiled/test/util.js +67 -14
- package/transpiled/test/util.js.map +1 -1
- package/tsconfig.json +2 -1
- package/tsconfig.tsbuildinfo +1 -0
- package/schemas/mem.cos.yaml +0 -6
- package/source/providers/FileSystem.test.ts +0 -5
- package/source/providers/Memory.ts +0 -55
- package/source/providers/S3.test.ts +0 -134
- package/transpiled/providers/Memory.d.ts +0 -15
- package/transpiled/providers/Memory.js +0 -72
- package/transpiled/providers/Memory.js.map +0 -1
- package/transpiled/tsconfig.tsbuildinfo +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
# [1.0.0-alpha.282](https://github.com/toa-io/toa/compare/v1.0.0-alpha.281...v1.0.0-alpha.282) (2026-09-03)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @toa.io/extensions.storages
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# [1.0.0-alpha.278](https://github.com/toa-io/toa/compare/v1.0.0-alpha.277...v1.0.0-alpha.278) (2026-09-03)
|
|
15
|
+
|
|
16
|
+
**Note:** Version bump only for package @toa.io/extensions.storages
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# [1.0.0-alpha.277](https://github.com/toa-io/toa/compare/v1.0.0-alpha.276...v1.0.0-alpha.277) (2026-09-03)
|
|
23
|
+
|
|
24
|
+
### Bug Fixes
|
|
25
|
+
|
|
26
|
+
* drop the build output that landed beside the sources ([a724f69](https://github.com/toa-io/toa/commit/a724f6921190e5c9d945e33c6035bb12d832e5c8))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# [1.0.0-alpha.274](https://github.com/toa-io/toa/compare/v1.0.0-alpha.273...v1.0.0-alpha.274) (2026-09-02)
|
|
30
|
+
|
|
31
|
+
**Note:** Version bump only for package @toa.io/extensions.storages
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# [1.0.0-alpha.273](https://github.com/toa-io/toa/compare/v1.0.0-alpha.272...v1.0.0-alpha.273) (2026-09-02)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
### Features
|
|
41
|
+
|
|
42
|
+
* **storages:** provider for DigitalOcean Spaces ([44ed14b](https://github.com/toa-io/toa/commit/44ed14b0bd796a35c9a7f9beef54ca4d5f8e8e07))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# [1.0.0-alpha.272](https://github.com/toa-io/toa/compare/v1.0.0-alpha.271...v1.0.0-alpha.272) (2026-09-01)
|
|
49
|
+
|
|
50
|
+
**Note:** Version bump only for package @toa.io/extensions.storages
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# [1.0.0-alpha.270](https://github.com/toa-io/toa/compare/v1.0.0-alpha.269...v1.0.0-alpha.270) (2026-08-31)
|
|
57
|
+
|
|
58
|
+
**Note:** Version bump only for package @toa.io/extensions.storages
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# [1.0.0-alpha.264](https://github.com/toa-io/toa/compare/v1.0.0-alpha.263...v1.0.0-alpha.264) (2026-08-29)
|
|
65
|
+
|
|
66
|
+
**Note:** Version bump only for package @toa.io/extensions.storages
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# [1.0.0-alpha.263](https://github.com/toa-io/toa/compare/v1.0.0-alpha.262...v1.0.0-alpha.263) (2026-08-29)
|
|
73
|
+
|
|
74
|
+
**Note:** Version bump only for package @toa.io/extensions.storages
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toa.io/extensions.storages",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.282",
|
|
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,27 +17,20 @@
|
|
|
16
17
|
"publishConfig": {
|
|
17
18
|
"access": "public"
|
|
18
19
|
},
|
|
19
|
-
"jest": {
|
|
20
|
-
"preset": "ts-jest",
|
|
21
|
-
"testEnvironment": "node"
|
|
22
|
-
},
|
|
23
20
|
"scripts": {
|
|
24
|
-
"test": "
|
|
21
|
+
"test": "echo \"Error: run tests from root\" && exit 1",
|
|
25
22
|
"transpile": "npx tsc"
|
|
26
23
|
},
|
|
27
|
-
"
|
|
28
|
-
"dotenv": "
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"dotenv": "17.4.2"
|
|
29
26
|
},
|
|
30
27
|
"dependencies": {
|
|
31
|
-
"@aws-sdk/client-s3": "3.
|
|
32
|
-
"@aws-sdk/lib-storage": "3.
|
|
33
|
-
"@toa.io/
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"matchacho": "0.6.0",
|
|
38
|
-
"msgpackr": "1.10.1",
|
|
39
|
-
"smithy-node-native-fetch": "1.0.6"
|
|
28
|
+
"@aws-sdk/client-s3": "3.1125.0",
|
|
29
|
+
"@aws-sdk/lib-storage": "3.1125.0",
|
|
30
|
+
"@toa.io/schemas": "1.0.0-alpha.282",
|
|
31
|
+
"cloudinary": "2.11.0",
|
|
32
|
+
"negotiator": "1.1.0",
|
|
33
|
+
"openspan": "1.0.0-alpha.277"
|
|
40
34
|
},
|
|
41
|
-
"gitHead": "
|
|
35
|
+
"gitHead": "4007c814725dd015d3bbebb9a28e28a247645c4f"
|
|
42
36
|
}
|
package/readme.md
CHANGED
|
@@ -6,15 +6,11 @@ Shared BLOB storage.
|
|
|
6
6
|
|
|
7
7
|
BLOBs are stored with the meta-information object (Entry) having the following properties:
|
|
8
8
|
|
|
9
|
-
- `
|
|
10
|
-
- `
|
|
11
|
-
- `
|
|
12
|
-
- `created
|
|
13
|
-
- `
|
|
14
|
-
- `name` - unique name
|
|
15
|
-
- `size` - size in bytes
|
|
16
|
-
- `type` - variant MIME type
|
|
17
|
-
- `meta` - object with application-specific information, empty by default
|
|
9
|
+
- `size`: size in bytes
|
|
10
|
+
- `type`: MIME type
|
|
11
|
+
- `checksum`: content checksum
|
|
12
|
+
- `created`: creation timestamp (ISO 8601)
|
|
13
|
+
- `attributes`: `Record<string, string>` with application-specific information, empty by default
|
|
18
14
|
|
|
19
15
|
### Example
|
|
20
16
|
|
|
@@ -22,12 +18,7 @@ BLOBs are stored with the meta-information object (Entry) having the following p
|
|
|
22
18
|
id: eecd837c
|
|
23
19
|
type: image/jpeg
|
|
24
20
|
created: 1698004822358
|
|
25
|
-
|
|
26
|
-
- name: thumbnail.jpeg
|
|
27
|
-
type: image/jpeg
|
|
28
|
-
- name: thumbnail.webp
|
|
29
|
-
type: image/webp
|
|
30
|
-
meta:
|
|
21
|
+
attributes:
|
|
31
22
|
face: true
|
|
32
23
|
nudity: false
|
|
33
24
|
```
|
|
@@ -39,7 +30,7 @@ containing named Storage instances, according to the annotation.
|
|
|
39
30
|
|
|
40
31
|
```javascript
|
|
41
32
|
async function effect (_, context) {
|
|
42
|
-
await context.storages.photos.
|
|
33
|
+
await context.storages.photos.get('/path/to/b4f577e0.thumbnail.jpeg')
|
|
43
34
|
}
|
|
44
35
|
```
|
|
45
36
|
|
|
@@ -49,11 +40,11 @@ async function effect (_, context) {
|
|
|
49
40
|
|
|
50
41
|
#### `async put(path: string, stream: Readable, options?: Options): Maybe<Entry>`
|
|
51
42
|
|
|
52
|
-
```
|
|
43
|
+
```ts
|
|
53
44
|
interface Options {
|
|
54
45
|
claim?: string
|
|
55
46
|
accept?: string
|
|
56
|
-
|
|
47
|
+
attributes?: Record<string, string>
|
|
57
48
|
}
|
|
58
49
|
```
|
|
59
50
|
|
|
@@ -75,13 +66,13 @@ are: `image/jpeg`, `image/png`, `image/gif`, `image/webp`, `image/heic`, `image/
|
|
|
75
66
|
|
|
76
67
|
See [source](source/Scanner.ts).
|
|
77
68
|
|
|
78
|
-
#### `async
|
|
69
|
+
#### `async head(path: string): Maybe<Entry>`
|
|
79
70
|
|
|
80
71
|
Get an entry.
|
|
81
72
|
|
|
82
73
|
If the entry does not exist, a `NOT_FOUND` error is returned.
|
|
83
74
|
|
|
84
|
-
#### `async
|
|
75
|
+
#### `async get(path: string): Maybe<Readable>`
|
|
85
76
|
|
|
86
77
|
Fetch the BLOB specified by `path`. If the path does not exist, a `NOT_FOUND` error is returned.
|
|
87
78
|
|
|
@@ -110,18 +101,6 @@ await storage.move('/path/to/eecd837c', './sub/eecd837c')
|
|
|
110
101
|
await storage.move('/path/to/eecd837c', './sub/')
|
|
111
102
|
```
|
|
112
103
|
|
|
113
|
-
#### `async entries(path: string): Entry[]`
|
|
114
|
-
|
|
115
|
-
Get a list of entries under the `path`.
|
|
116
|
-
|
|
117
|
-
#### `async diversify(path: string, name: string, stream: Readable): Maybe<void>`
|
|
118
|
-
|
|
119
|
-
Add or replace a `name` variant of the entry specified by `path`.
|
|
120
|
-
|
|
121
|
-
#### `async annotate(path: string, key: string, value: any): Maybe<void>`
|
|
122
|
-
|
|
123
|
-
Set a `key` property in the `meta` of the entry specified by `path`.
|
|
124
|
-
|
|
125
104
|
## Providers
|
|
126
105
|
|
|
127
106
|
Storage uses underlying providers to store BLOBs and entries.
|
|
@@ -152,40 +131,114 @@ and [`toa env`](/runtime/cli/readme.md#env)
|
|
|
152
131
|
for local environment.
|
|
153
132
|
`endpoint` parameter is optional.
|
|
154
133
|
|
|
155
|
-
###
|
|
134
|
+
### DigitalOcean Spaces
|
|
156
135
|
|
|
157
136
|
Annotation format is:
|
|
158
137
|
|
|
159
138
|
```yaml
|
|
160
139
|
storages:
|
|
161
|
-
photos
|
|
162
|
-
provider:
|
|
163
|
-
|
|
140
|
+
photos:
|
|
141
|
+
provider: spaces
|
|
142
|
+
space: my-space
|
|
143
|
+
region: fra1
|
|
164
144
|
```
|
|
165
145
|
|
|
166
|
-
|
|
146
|
+
`region` is the datacenter slug, the endpoint is derived from it.
|
|
167
147
|
|
|
168
|
-
|
|
148
|
+
Secrets `ACCESS_KEY_ID` and `SECRET_ACCESS_KEY` are required.
|
|
149
|
+
Keys are created in the DigitalOcean control panel,
|
|
150
|
+
under Spaces Object Storage, on the Access Keys tab.
|
|
151
|
+
|
|
152
|
+
### Cloudinary
|
|
153
|
+
|
|
154
|
+
[Cloudinary](https://cloudinary.com) provider is used to store and transform media files.
|
|
155
|
+
|
|
156
|
+
Objects stored in the Cloudinary storage can be requested with transformations,
|
|
157
|
+
using dot-separated path extensions: `/path/to/eecd837c.{extension}.{extension}`.
|
|
158
|
+
|
|
159
|
+
Transformation is defined by the `extension` property, which is a regular expression with named
|
|
160
|
+
groups.
|
|
161
|
+
The named groups are used to replace the placeholders in the `transform` object.
|
|
162
|
+
|
|
163
|
+
`transform` object is an element of
|
|
164
|
+
the [Cloudinary `transformation` array](https://cloudinary.com/documentation/node_image_manipulation#apply_common_image_transformations).
|
|
165
|
+
|
|
166
|
+
> - `zoom` should be specified as integer [0–100].
|
|
167
|
+
> - `format` value `jpeg` is converted to `jpg`.
|
|
168
|
+
> - `^` and `$` are added to the regular expression automatically, unless they are already present.
|
|
169
169
|
|
|
170
170
|
Annotation format is:
|
|
171
171
|
|
|
172
172
|
```yaml
|
|
173
173
|
storages:
|
|
174
|
-
|
|
175
|
-
provider:
|
|
176
|
-
|
|
174
|
+
media:
|
|
175
|
+
provider: cloudinary
|
|
176
|
+
environment: my-cloud
|
|
177
|
+
type: image # image or video
|
|
178
|
+
prefix: my-app
|
|
179
|
+
transformations:
|
|
180
|
+
- extension: (?<width>\d*)x(?<height>\d*)(z(?<zoom>\d*))?
|
|
181
|
+
transformation:
|
|
182
|
+
width: <width>
|
|
183
|
+
height: <height>
|
|
184
|
+
zoom: <zoom>
|
|
185
|
+
crop: thumb
|
|
186
|
+
gravity: face
|
|
187
|
+
optional: true
|
|
188
|
+
- extension: (?<format>jpeg|webp)
|
|
189
|
+
transformation:
|
|
190
|
+
fetch_format: <format>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Path extensions must be specified in the same order as in the annotation.
|
|
194
|
+
|
|
195
|
+
For the example above:
|
|
196
|
+
|
|
197
|
+
- `/path/to/eecd837c.100x100.jpeg`: matches
|
|
198
|
+
- `/path/to/eecd837c.jpeg.100x100`: does not match
|
|
199
|
+
|
|
200
|
+
If a path extension is not matched,
|
|
201
|
+
or if at least one of the transformations is not matched (unless it is `optional`),
|
|
202
|
+
then an error is returned.
|
|
203
|
+
|
|
204
|
+
Secrets:
|
|
205
|
+
|
|
206
|
+
- `API_KEY`
|
|
207
|
+
- `API_SECRET`
|
|
208
|
+
|
|
209
|
+
### Filesystem
|
|
210
|
+
|
|
211
|
+
Annotation format is:
|
|
212
|
+
|
|
213
|
+
```yaml
|
|
214
|
+
storages:
|
|
215
|
+
photos:
|
|
216
|
+
provider: fs
|
|
217
|
+
path: /var/my-photos
|
|
177
218
|
```
|
|
178
219
|
|
|
179
|
-
|
|
220
|
+
[Kubernetes PVC](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) can be mounted to
|
|
221
|
+
the storage:
|
|
180
222
|
|
|
181
|
-
|
|
223
|
+
```yaml
|
|
224
|
+
storages:
|
|
225
|
+
photos:
|
|
226
|
+
provider: fs
|
|
227
|
+
path: /var/my-photos
|
|
228
|
+
claim: photos-pvc
|
|
229
|
+
```
|
|
182
230
|
|
|
183
|
-
|
|
231
|
+
### Temporary
|
|
232
|
+
|
|
233
|
+
Filesystem using OS temporary directory.
|
|
234
|
+
|
|
235
|
+
Annotation format is:
|
|
184
236
|
|
|
185
237
|
```yaml
|
|
186
238
|
storages:
|
|
187
239
|
photos@dev:
|
|
188
|
-
provider:
|
|
240
|
+
provider: tmp
|
|
241
|
+
directory: my-app-tmp
|
|
189
242
|
```
|
|
190
243
|
|
|
191
244
|
## Deduplication
|
|
@@ -196,7 +249,7 @@ Variants, on the other hand, are not deduplicated across different entries.
|
|
|
196
249
|
|
|
197
250
|
Underlying directory structure:
|
|
198
251
|
|
|
199
|
-
```
|
|
252
|
+
```text
|
|
200
253
|
/temp
|
|
201
254
|
c28f4dfd # random id
|
|
202
255
|
/blobs
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
type: object
|
|
2
|
+
properties:
|
|
3
|
+
provider:
|
|
4
|
+
const: cloudinary
|
|
5
|
+
environment:
|
|
6
|
+
type: string
|
|
7
|
+
type:
|
|
8
|
+
enum:
|
|
9
|
+
- image
|
|
10
|
+
- video
|
|
11
|
+
prefix:
|
|
12
|
+
type: string
|
|
13
|
+
eager:
|
|
14
|
+
type: array
|
|
15
|
+
items:
|
|
16
|
+
type: object
|
|
17
|
+
transformations:
|
|
18
|
+
type: array
|
|
19
|
+
items:
|
|
20
|
+
type: object
|
|
21
|
+
properties:
|
|
22
|
+
extension:
|
|
23
|
+
type: string
|
|
24
|
+
transformation:
|
|
25
|
+
anyOf:
|
|
26
|
+
- type: object
|
|
27
|
+
- type: array
|
|
28
|
+
items:
|
|
29
|
+
type: object
|
|
30
|
+
optional:
|
|
31
|
+
type: boolean
|
|
32
|
+
required:
|
|
33
|
+
- extension
|
|
34
|
+
- transformation
|
|
35
|
+
required:
|
|
36
|
+
- environment
|
|
37
|
+
- type
|
|
38
|
+
additionalProperties: false
|
package/schemas/fs.cos.yaml
CHANGED
package/schemas/s3.cos.yaml
CHANGED
package/schemas/test.cos.yaml
CHANGED
package/schemas/tmp.cos.yaml
CHANGED
package/source/Annotation.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
|
-
import { providers } from './providers'
|
|
3
|
-
import * as schemas from './schemas'
|
|
4
|
-
import type { Declaration } from './providers'
|
|
2
|
+
import { providers } from './providers/index.js'
|
|
3
|
+
import * as schemas from './schemas.js'
|
|
4
|
+
import type { Declaration } from './providers/index.js'
|
|
5
5
|
import type { Schema } from '@toa.io/schemas'
|
|
6
6
|
|
|
7
7
|
export type Annotation = Record<string, Declaration>
|
package/source/Aspect.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
2
|
import { Connector, type extensions } from '@toa.io/core'
|
|
3
|
-
import { type Storage, type Storages } from './Storage'
|
|
3
|
+
import { type Storage, type Storages } from './Storage.js'
|
|
4
4
|
|
|
5
5
|
export class Aspect extends Connector implements extensions.Aspect {
|
|
6
6
|
public readonly name = 'storages'
|
package/source/Entry.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
id: string
|
|
3
|
-
size: number
|
|
4
|
-
type: string
|
|
5
|
-
created: number
|
|
6
|
-
variants: Variant[]
|
|
7
|
-
meta: Record<string, unknown>
|
|
8
|
-
}
|
|
1
|
+
import type { Readable } from 'node:stream'
|
|
9
2
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
export type Entry = { id: string } & Metadata
|
|
4
|
+
export type Stream = { stream: Readable } & Metadata
|
|
5
|
+
|
|
6
|
+
export interface Metadata {
|
|
13
7
|
type: string
|
|
8
|
+
size: number | null
|
|
9
|
+
checksum: string
|
|
10
|
+
created: string
|
|
11
|
+
attributes: Attributes
|
|
12
|
+
range?: string
|
|
13
|
+
partial?: boolean
|
|
14
14
|
}
|
|
15
|
+
|
|
16
|
+
export type Attributes = Record<string, string>
|
package/source/Factory.ts
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import assert from 'node:assert'
|
|
2
|
-
import {
|
|
3
|
-
import { providers } from './providers'
|
|
4
|
-
import { Storage, type Storages } from './Storage'
|
|
5
|
-
import { Aspect } from './Aspect'
|
|
6
|
-
import {
|
|
7
|
-
import { validateAnnotation } from './Annotation'
|
|
8
|
-
import type {
|
|
9
|
-
import type {
|
|
10
|
-
import type {
|
|
2
|
+
import { console } from 'openspan'
|
|
3
|
+
import { providers } from './providers/index.js'
|
|
4
|
+
import { Storage, type Storages } from './Storage.js'
|
|
5
|
+
import { Aspect } from './Aspect.js'
|
|
6
|
+
import { ENV_PREFIX } from './deployment.js'
|
|
7
|
+
import { validateAnnotation } from './Annotation.js'
|
|
8
|
+
import type { Constructor } from './Provider.js'
|
|
9
|
+
import type { Declaration } from './providers/index.js'
|
|
10
|
+
import type { Annotation } from './Annotation.js'
|
|
11
|
+
import type { Secrets } from './Secrets.js'
|
|
11
12
|
|
|
12
13
|
export class Factory {
|
|
13
14
|
private readonly annotation: Annotation
|
|
14
15
|
|
|
15
16
|
public constructor () {
|
|
16
|
-
const env = process.env
|
|
17
|
+
const env = process.env[ENV_PREFIX]
|
|
17
18
|
|
|
18
|
-
assert.ok(env !== undefined,
|
|
19
|
+
assert.ok(env !== undefined, `${ENV_PREFIX} is not defined`)
|
|
19
20
|
|
|
20
|
-
this.annotation =
|
|
21
|
+
this.annotation = JSON.parse(env)
|
|
21
22
|
|
|
22
23
|
validateAnnotation(this.annotation)
|
|
23
24
|
}
|
|
@@ -38,23 +39,28 @@ export class Factory {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
private createStorage (name: string, declaration: Declaration): Storage {
|
|
41
|
-
const { provider:
|
|
42
|
-
const Provider:
|
|
42
|
+
const { provider: id, ...options } = declaration
|
|
43
|
+
const Provider: Constructor = providers[id]
|
|
43
44
|
const secrets = this.resolveSecrets(name, Provider)
|
|
44
45
|
const provider = new Provider(options, secrets)
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
console.debug('Storage created', {
|
|
48
|
+
name,
|
|
49
|
+
provider: id,
|
|
50
|
+
...(provider.root === undefined ? undefined : { root: provider.root })
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
return new Storage(provider, { name, provider: id })
|
|
47
54
|
}
|
|
48
55
|
|
|
49
|
-
private resolveSecrets (storageName: string,
|
|
50
|
-
Class: ProviderConstructor): ProviderSecrets {
|
|
56
|
+
private resolveSecrets (storageName: string, Class: Constructor): Secrets {
|
|
51
57
|
if (Class.SECRETS === undefined)
|
|
52
58
|
return {}
|
|
53
59
|
|
|
54
60
|
const secrets: Record<string, string | undefined> = {}
|
|
55
61
|
|
|
56
62
|
for (const secret of Class.SECRETS) {
|
|
57
|
-
const variable = `${
|
|
63
|
+
const variable = `${ENV_PREFIX}_${storageName}_${secret.name}`.toUpperCase()
|
|
58
64
|
const value = process.env[variable]
|
|
59
65
|
|
|
60
66
|
assert.ok(secret.optional === true || value !== undefined,
|
package/source/Provider.ts
CHANGED
|
@@ -1,37 +1,36 @@
|
|
|
1
|
-
import { type Readable } from 'node:stream'
|
|
2
1
|
import * as assert from 'node:assert'
|
|
2
|
+
import type { Metadata, Stream } from './Entry.js'
|
|
3
|
+
import type { Readable } from 'node:stream'
|
|
4
|
+
import type { Maybe } from '@toa.io/types'
|
|
5
|
+
import type { Secret, Secrets } from './Secrets.js'
|
|
3
6
|
|
|
4
|
-
export
|
|
7
|
+
export abstract class Provider<Options = unknown> {
|
|
8
|
+
public static readonly SECRETS?: readonly Secret[]
|
|
9
|
+
public readonly root?: string
|
|
10
|
+
public readonly options: Options
|
|
5
11
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
readonly optional?: boolean
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export abstract class Provider<Options = void> {
|
|
12
|
-
public static readonly SECRETS: readonly ProviderSecret[] = []
|
|
12
|
+
protected constructor (options: Options, secrets?: Secrets) {
|
|
13
|
+
this.options = options
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
assert.ok(optional || secrets?.[name] !== undefined, `Missing secret '${name}'`)
|
|
15
|
+
new.target.SECRETS?.forEach(({ name, optional }) =>
|
|
16
|
+
assert.ok(optional === true || secrets?.[name] !== undefined, `Missing secret '${name}'`))
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
public abstract get (path: string): Promise<
|
|
19
|
+
public abstract get (path: string, options?: unknown): Promise<Maybe<Stream>>
|
|
20
20
|
|
|
21
|
-
public abstract
|
|
21
|
+
public abstract head (path: string): Promise<Maybe<Metadata>>
|
|
22
22
|
|
|
23
|
-
public abstract put (path: string,
|
|
23
|
+
public abstract put (path: string, stream: Readable): Promise<void>
|
|
24
24
|
|
|
25
|
-
public abstract
|
|
25
|
+
public abstract commit (path: string, metadata: Metadata): Promise<void>
|
|
26
26
|
|
|
27
|
-
public abstract
|
|
27
|
+
public abstract delete (path: string): Promise<void>
|
|
28
28
|
|
|
29
|
-
public abstract
|
|
29
|
+
public abstract move (from: string, to: string): Promise<Maybe<void>>
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export interface
|
|
33
|
-
|
|
34
|
-
prototype: Provider
|
|
32
|
+
export interface Constructor<Options = any> {
|
|
33
|
+
SECRETS?: readonly Secret[]
|
|
35
34
|
|
|
36
|
-
new (options:
|
|
35
|
+
new (options: Options, secrets?: Secrets): Provider<Options>
|
|
37
36
|
}
|