@toa.io/extensions.storages 1.0.0-alpha.31 → 1.0.0-alpha.310

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 (122) hide show
  1. package/package.json +38 -18
  2. package/readme.md +115 -47
  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 -19
  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 +33 -26
  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 -22
  21. package/transpiled/Storage.js +79 -142
  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/measurements.d.ts +3 -0
  30. package/transpiled/measurements.js +23 -0
  31. package/transpiled/measurements.js.map +1 -0
  32. package/transpiled/providers/Cloudinary.d.ts +50 -0
  33. package/transpiled/providers/Cloudinary.js +223 -0
  34. package/transpiled/providers/Cloudinary.js.map +1 -0
  35. package/transpiled/providers/Declaration.d.ts +9 -5
  36. package/transpiled/providers/Declaration.js +1 -2
  37. package/transpiled/providers/FileSystem.d.ts +16 -11
  38. package/transpiled/providers/FileSystem.js +64 -43
  39. package/transpiled/providers/FileSystem.js.map +1 -1
  40. package/transpiled/providers/S3.d.ts +14 -19
  41. package/transpiled/providers/S3.js +72 -123
  42. package/transpiled/providers/S3.js.map +1 -1
  43. package/transpiled/providers/Spaces.d.ts +12 -0
  44. package/transpiled/providers/Spaces.js +13 -0
  45. package/transpiled/providers/Spaces.js.map +1 -0
  46. package/transpiled/providers/Temporary.d.ts +2 -3
  47. package/transpiled/providers/Temporary.js +7 -11
  48. package/transpiled/providers/Temporary.js.map +1 -1
  49. package/transpiled/providers/Test.d.ts +4 -4
  50. package/transpiled/providers/Test.js +6 -12
  51. package/transpiled/providers/Test.js.map +1 -1
  52. package/transpiled/providers/index.d.ts +23 -11
  53. package/transpiled/providers/index.js +30 -14
  54. package/transpiled/providers/index.js.map +1 -1
  55. package/transpiled/providers/index.types.d.ts +5 -0
  56. package/transpiled/providers/index.types.js +2 -0
  57. package/transpiled/providers/index.types.js.map +1 -0
  58. package/transpiled/test/util.d.ts +113 -6
  59. package/transpiled/test/util.js +68 -15
  60. package/transpiled/test/util.js.map +1 -1
  61. package/schemas/annotation.cos.yaml +0 -10
  62. package/schemas/fs.cos.yaml +0 -9
  63. package/schemas/mem.cos.yaml +0 -6
  64. package/schemas/s3.cos.yaml +0 -16
  65. package/schemas/test.cos.yaml +0 -8
  66. package/schemas/tmp.cos.yaml +0 -9
  67. package/source/Annotation.ts +0 -39
  68. package/source/Aspect.ts +0 -25
  69. package/source/Entry.ts +0 -14
  70. package/source/Factory.ts +0 -68
  71. package/source/Provider.ts +0 -37
  72. package/source/Scanner.ts +0 -153
  73. package/source/Storage.test.ts +0 -472
  74. package/source/Storage.ts +0 -235
  75. package/source/deployment.ts +0 -57
  76. package/source/index.ts +0 -5
  77. package/source/manifest.ts +0 -8
  78. package/source/providers/Declaration.ts +0 -10
  79. package/source/providers/FileSystem.test.ts +0 -5
  80. package/source/providers/FileSystem.ts +0 -63
  81. package/source/providers/Memory.ts +0 -55
  82. package/source/providers/S3.test.ts +0 -134
  83. package/source/providers/S3.ts +0 -196
  84. package/source/providers/Temporary.ts +0 -16
  85. package/source/providers/Test.ts +0 -13
  86. package/source/providers/index.test.ts +0 -136
  87. package/source/providers/index.ts +0 -16
  88. package/source/providers/readme.md +0 -25
  89. package/source/schemas.test.ts +0 -58
  90. package/source/schemas.ts +0 -15
  91. package/source/test/.env.example +0 -1
  92. package/source/test/albert.jpg +0 -0
  93. package/source/test/empty.txt +0 -0
  94. package/source/test/lenna.ascii +0 -64
  95. package/source/test/lenna.png +0 -0
  96. package/source/test/sample.avi +0 -0
  97. package/source/test/sample.avif +0 -0
  98. package/source/test/sample.gif +0 -0
  99. package/source/test/sample.heic +0 -0
  100. package/source/test/sample.jpeg +0 -0
  101. package/source/test/sample.jxl +0 -0
  102. package/source/test/sample.svg +0 -4
  103. package/source/test/sample.wav +0 -0
  104. package/source/test/sample.webp +0 -0
  105. package/source/test/util.ts +0 -45
  106. package/transpiled/Annotation.d.ts +0 -3
  107. package/transpiled/Annotation.js +0 -57
  108. package/transpiled/Annotation.js.map +0 -1
  109. package/transpiled/deployment.d.ts +0 -5
  110. package/transpiled/deployment.js +0 -68
  111. package/transpiled/deployment.js.map +0 -1
  112. package/transpiled/manifest.d.ts +0 -1
  113. package/transpiled/manifest.js +0 -9
  114. package/transpiled/manifest.js.map +0 -1
  115. package/transpiled/providers/Memory.d.ts +0 -15
  116. package/transpiled/providers/Memory.js +0 -72
  117. package/transpiled/providers/Memory.js.map +0 -1
  118. package/transpiled/schemas.d.ts +0 -9
  119. package/transpiled/schemas.js +0 -14
  120. package/transpiled/schemas.js.map +0 -1
  121. package/transpiled/tsconfig.tsbuildinfo +0 -1
  122. package/tsconfig.json +0 -12
@@ -1,29 +1,136 @@
1
+ import type { Secrets } from '../Secrets.ts';
2
+ import type { providers, S3Options, SpacesOptions, CloudinaryOptions, FileSystemOptions, TemporaryOptions } from '../providers/index.ts';
1
3
  export declare const suites: ({
2
4
  run: true;
3
5
  provider: "tmp";
4
6
  options: {
5
7
  directory: string;
6
8
  endpoint?: undefined;
7
- region?: undefined;
8
9
  bucket?: undefined;
10
+ space?: undefined;
11
+ region?: undefined;
12
+ environment?: undefined;
13
+ type?: undefined;
14
+ prefix?: undefined;
15
+ transformations?: undefined;
9
16
  };
10
17
  secrets?: undefined;
11
- } | {
12
- run: true;
13
- provider: "mem";
14
- options?: undefined;
15
- secrets?: undefined;
16
18
  } | {
17
19
  run: boolean;
18
20
  provider: "s3";
19
21
  options: {
22
+ directory?: undefined;
20
23
  endpoint: string;
21
24
  region: string;
22
25
  bucket: string;
26
+ space?: undefined;
27
+ environment?: undefined;
28
+ type?: undefined;
29
+ prefix?: undefined;
30
+ transformations?: undefined;
31
+ };
32
+ secrets: {
33
+ ACCESS_KEY_ID: string;
34
+ SECRET_ACCESS_KEY: string;
35
+ API_KEY?: undefined;
36
+ API_SECRET?: undefined;
37
+ };
38
+ } | {
39
+ run: boolean;
40
+ provider: "spaces";
41
+ options: {
23
42
  directory?: undefined;
43
+ endpoint?: undefined;
44
+ bucket?: undefined;
45
+ space: string;
46
+ region: string;
47
+ environment?: undefined;
48
+ type?: undefined;
49
+ prefix?: undefined;
50
+ transformations?: undefined;
24
51
  };
25
52
  secrets: {
26
53
  ACCESS_KEY_ID: string;
27
54
  SECRET_ACCESS_KEY: string;
55
+ API_KEY?: undefined;
56
+ API_SECRET?: undefined;
57
+ };
58
+ } | {
59
+ run: boolean;
60
+ provider: "cloudinary";
61
+ options: {
62
+ directory?: undefined;
63
+ endpoint?: undefined;
64
+ bucket?: undefined;
65
+ space?: undefined;
66
+ region?: undefined;
67
+ environment: string;
68
+ type: "image";
69
+ prefix: string;
70
+ transformations: ({
71
+ extension: string;
72
+ transformation: {
73
+ width: string;
74
+ height: string;
75
+ zoom: string;
76
+ crop: string;
77
+ gravity: string;
78
+ angle?: undefined;
79
+ fetch_format?: undefined;
80
+ };
81
+ optional: true;
82
+ condition?: undefined;
83
+ } | {
84
+ extension: string;
85
+ transformation: {
86
+ gravity?: undefined;
87
+ width: string;
88
+ height: string;
89
+ zoom: string;
90
+ crop: string;
91
+ angle?: undefined;
92
+ fetch_format?: undefined;
93
+ };
94
+ optional: true;
95
+ condition?: undefined;
96
+ } | {
97
+ extension: string;
98
+ condition: string;
99
+ transformation: {
100
+ gravity?: undefined;
101
+ width?: undefined;
102
+ height?: undefined;
103
+ zoom?: undefined;
104
+ crop?: undefined;
105
+ angle: number;
106
+ fetch_format?: undefined;
107
+ };
108
+ optional: true;
109
+ } | {
110
+ condition?: undefined;
111
+ extension: string;
112
+ transformation: {
113
+ gravity?: undefined;
114
+ width?: undefined;
115
+ height?: undefined;
116
+ zoom?: undefined;
117
+ crop?: undefined;
118
+ angle?: undefined;
119
+ fetch_format: string;
120
+ };
121
+ optional: true;
122
+ })[];
123
+ };
124
+ secrets: {
125
+ ACCESS_KEY_ID?: undefined;
126
+ SECRET_ACCESS_KEY?: undefined;
127
+ API_KEY: string;
128
+ API_SECRET: string;
28
129
  };
29
130
  })[];
131
+ export interface Suite {
132
+ run: boolean;
133
+ provider: keyof typeof providers;
134
+ options?: S3Options | SpacesOptions | CloudinaryOptions | FileSystemOptions | TemporaryOptions;
135
+ secrets?: Secrets;
136
+ }
@@ -1,13 +1,7 @@
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.suites = void 0;
7
- const node_path_1 = require("node:path");
8
- const dotenv_1 = __importDefault(require("dotenv"));
9
- dotenv_1.default.config({ path: (0, node_path_1.join)(__dirname, '.env') });
10
- exports.suites = [
1
+ import { join } from 'node:path';
2
+ import dotenv from 'dotenv';
3
+ dotenv.config({ path: join(import.meta.dirname, '.env') });
4
+ export const suites = [
11
5
  {
12
6
  run: true,
13
7
  provider: 'tmp',
@@ -15,15 +9,11 @@ exports.suites = [
15
9
  directory: 'toa-storages-temp'
16
10
  }
17
11
  },
18
- {
19
- run: true,
20
- provider: 'mem'
21
- },
22
12
  {
23
13
  run: process.env.RUN_S3 === '1',
24
14
  provider: 's3',
25
15
  options: {
26
- endpoint: 'http://localhost:4566',
16
+ endpoint: 'http://localhost:31050',
27
17
  region: 'us-west-1',
28
18
  bucket: 'test-bucket'
29
19
  },
@@ -31,6 +21,69 @@ exports.suites = [
31
21
  ACCESS_KEY_ID: 'developer',
32
22
  SECRET_ACCESS_KEY: 'secret'
33
23
  }
24
+ },
25
+ {
26
+ run: process.env.RUN_SPACES === '1',
27
+ provider: 'spaces',
28
+ options: {
29
+ space: 'ants',
30
+ region: 'fra1'
31
+ },
32
+ secrets: {
33
+ ACCESS_KEY_ID: process.env.SPACES_KEY_ID ?? '',
34
+ SECRET_ACCESS_KEY: process.env.SPACES_ACCESS_KEY ?? ''
35
+ }
36
+ },
37
+ {
38
+ run: process.env.RUN_CLOUDINARY === '1',
39
+ provider: 'cloudinary',
40
+ options: {
41
+ environment: 'dl5z4zgth',
42
+ type: 'image',
43
+ prefix: 'toa-dev',
44
+ transformations: [
45
+ {
46
+ extension: '(?<width>\\d*)x(?<height>\\d*)(z(?<zoom>\\d*))?',
47
+ transformation: {
48
+ width: '<width>',
49
+ height: '<height>',
50
+ zoom: '<zoom>',
51
+ crop: 'thumb',
52
+ gravity: 'face'
53
+ },
54
+ optional: true
55
+ },
56
+ {
57
+ extension: '\\[(?<width>\\d*)x(?<height>\\d*)\\](z(?<zoom>\\d+))?',
58
+ transformation: {
59
+ width: '<width>',
60
+ height: '<height>',
61
+ zoom: '<zoom>',
62
+ crop: 'fit'
63
+ },
64
+ optional: true
65
+ },
66
+ {
67
+ extension: 'vertical',
68
+ condition: 'w_gt_h',
69
+ transformation: {
70
+ angle: 90
71
+ },
72
+ optional: true
73
+ },
74
+ {
75
+ extension: '(?<format>jpeg|webp)',
76
+ transformation: {
77
+ fetch_format: '<format>'
78
+ },
79
+ optional: true
80
+ }
81
+ ]
82
+ },
83
+ secrets: {
84
+ API_KEY: process.env.CLOUDINARY_API_KEY ?? '',
85
+ API_SECRET: process.env.CLOUDINARY_API_SECRET ?? ''
86
+ }
34
87
  }
35
88
  // add more providers here, use `run` as a condition to run the test
36
89
  // e.g.: `run: process.env.ACCESS_KEY_ID !== undefined`
@@ -1 +1 @@
1
- {"version":3,"file":"util.js","sourceRoot":"","sources":["../../source/test/util.ts"],"names":[],"mappings":";;;;;;AAAA,yCAAgC;AAChC,oDAA2B;AAO3B,gBAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAA,gBAAI,EAAC,SAAS,EAAE,MAAM,CAAC,EAAE,CAAC,CAAA;AAEnC,QAAA,MAAM,GAAG;IACpB;QACE,GAAG,EAAE,IAAI;QACT,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE;YACP,SAAS,EAAE,mBAAmB;SAC/B;KACF;IACD;QACE,GAAG,EAAE,IAAI;QACT,QAAQ,EAAE,KAAK;KAChB;IACD;QACE,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG;QAC/B,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE;YACP,QAAQ,EAAE,uBAAuB;YACjC,MAAM,EAAE,WAAW;YACnB,MAAM,EAAE,aAAa;SACtB;QACD,OAAO,EAAE;YACP,aAAa,EAAE,WAAW;YAC1B,iBAAiB,EAAE,QAAQ;SAC5B;KACF;IACD,oEAAoE;IACpE,uDAAuD;CACtC,CAAA"}
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../../source/test/util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,MAAM,MAAM,QAAQ,CAAA;AAW3B,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,CAAA;AAE1D,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB;QACE,GAAG,EAAE,IAAI;QACT,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE;YACP,SAAS,EAAE,mBAAmB;SAC/B;KACF;IACD;QACE,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG;QAC/B,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE;YACP,QAAQ,EAAE,wBAAwB;YAClC,MAAM,EAAE,WAAW;YACnB,MAAM,EAAE,aAAa;SACtB;QACD,OAAO,EAAE;YACP,aAAa,EAAE,WAAW;YAC1B,iBAAiB,EAAE,QAAQ;SAC5B;KACF;IACD;QACE,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,GAAG;QACnC,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE;YACP,KAAK,EAAE,MAAM;YACb,MAAM,EAAE,MAAM;SACf;QACD,OAAO,EAAE;YACP,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,EAAE;YAC9C,iBAAiB,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,EAAE;SACvD;KACF;IACD;QACE,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,GAAG;QACvC,QAAQ,EAAE,YAAY;QACtB,OAAO,EAAE;YACP,WAAW,EAAE,WAAW;YACxB,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,SAAS;YACjB,eAAe,EAAE;gBACf;oBACE,SAAS,EAAE,iDAAiD;oBAC5D,cAAc,EAAE;wBACd,KAAK,EAAE,SAAS;wBAChB,MAAM,EAAE,UAAU;wBAClB,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,OAAO;wBACb,OAAO,EAAE,MAAM;qBAChB;oBACD,QAAQ,EAAE,IAAI;iBACf;gBACD;oBACE,SAAS,EAAE,uDAAuD;oBAClE,cAAc,EAAE;wBACd,KAAK,EAAE,SAAS;wBAChB,MAAM,EAAE,UAAU;wBAClB,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,KAAK;qBACZ;oBACD,QAAQ,EAAE,IAAI;iBACf;gBACD;oBACE,SAAS,EAAE,UAAU;oBACrB,SAAS,EAAE,QAAQ;oBACnB,cAAc,EAAE;wBACd,KAAK,EAAE,EAAE;qBACV;oBACD,QAAQ,EAAE,IAAI;iBACf;gBACD;oBACE,SAAS,EAAE,sBAAsB;oBACjC,cAAc,EAAE;wBACd,YAAY,EAAE,UAAU;qBACzB;oBACD,QAAQ,EAAE,IAAI;iBACf;aACF;SACF;QACD,OAAO,EAAE;YACP,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE;YAC7C,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,EAAE;SACpD;KACF;IACD,oEAAoE;IACpE,uDAAuD;CACtC,CAAA"}
@@ -1,10 +0,0 @@
1
- type: object
2
- patternProperties:
3
- '^[a-z0-9_]{1,32}$':
4
- oneOf:
5
- - $ref: s3
6
- - $ref: fs
7
- - $ref: tmp
8
- - $ref: mem
9
- - $ref: test
10
- additionalProperties: false
@@ -1,9 +0,0 @@
1
- type: object
2
- properties:
3
- provider:
4
- const: fs
5
- path:
6
- type: string
7
- maxLength: 4096
8
- required: [provider, path]
9
- additionalProperties: false
@@ -1,6 +0,0 @@
1
- type: object
2
- properties:
3
- provider:
4
- const: mem
5
- required: [provider]
6
- additionalProperties: false
@@ -1,16 +0,0 @@
1
- type: object
2
- properties:
3
- provider:
4
- const: s3
5
- bucket:
6
- type: string
7
- minLength: 3
8
- maxLength: 63
9
- region:
10
- type: string
11
- prefix:
12
- type: string
13
- endpoint:
14
- type: string
15
- format: uri
16
- required: [provider, bucket]
@@ -1,8 +0,0 @@
1
- type: object
2
- properties:
3
- provider:
4
- const: test
5
- directory:
6
- type: string
7
- required: [provider, directory]
8
- additionalProperties: false
@@ -1,9 +0,0 @@
1
- type: object
2
- properties:
3
- provider:
4
- type: string
5
- const: tmp
6
- directory:
7
- type: string
8
- required: [provider, directory]
9
- additionalProperties: false
@@ -1,39 +0,0 @@
1
- import assert from 'node:assert'
2
- import { providers } from './providers'
3
- import * as schemas from './schemas'
4
- import type { Declaration } from './providers'
5
- import type { Schema } from '@toa.io/schemas'
6
-
7
- export type Annotation = Record<string, Declaration>
8
-
9
- export function validateAnnotation (annotation: unknown): asserts annotation is Annotation {
10
- try {
11
- schemas.annotation.validate(annotation)
12
- } catch (error) {
13
- explain(annotation)
14
-
15
- // if all declarations are valid, re-throw the error
16
- throw error
17
- }
18
- }
19
-
20
- /*
21
- It is required because `oneOf` schema is used for the annotation validation.
22
- */
23
- function explain (annotation: unknown): void {
24
- assert.ok(typeof annotation === 'object' && annotation !== null,
25
- 'TOA_STORAGES is not an object')
26
-
27
- for (const declaration of Object.values(annotation)) {
28
- assert.ok(typeof declaration === 'object' && declaration !== null &&
29
- declaration.provider in providers,
30
- `Unknown provider '${declaration.provider}'`)
31
-
32
- assert.ok(declaration.provider in schemas,
33
- `No schema for provider '${declaration.provider}'`)
34
-
35
- const schema: Schema<Declaration> = schemas[declaration.provider as keyof typeof providers]
36
-
37
- schema.validate(declaration, `Storage '${declaration.provider}' annotation`)
38
- }
39
- }
package/source/Aspect.ts DELETED
@@ -1,25 +0,0 @@
1
- import assert from 'node:assert'
2
- import { Connector, type extensions } from '@toa.io/core'
3
- import { type Storage, type Storages } from './Storage'
4
-
5
- export class Aspect extends Connector implements extensions.Aspect {
6
- public readonly name = 'storages'
7
-
8
- private readonly storages: Storages
9
-
10
- public constructor (storages: Storages) {
11
- super()
12
-
13
- this.storages = storages
14
- }
15
-
16
- public invoke (name: string, method: keyof Storage, ...args: unknown[]): unknown {
17
- const storage = this.storages[name]
18
-
19
- assert.ok(storage !== undefined, `Storage '${name}' is not defined`)
20
-
21
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
22
- // @ts-expect-error
23
- return storage[method](...args)
24
- }
25
- }
package/source/Entry.ts DELETED
@@ -1,14 +0,0 @@
1
- export interface Entry {
2
- id: string
3
- size: number
4
- type: string
5
- created: number
6
- variants: Variant[]
7
- meta: Record<string, unknown>
8
- }
9
-
10
- interface Variant {
11
- name: string
12
- size: number
13
- type: string
14
- }
package/source/Factory.ts DELETED
@@ -1,68 +0,0 @@
1
- import assert from 'node:assert'
2
- import { decode } from '@toa.io/generic'
3
- import { providers } from './providers'
4
- import { Storage, type Storages } from './Storage'
5
- import { Aspect } from './Aspect'
6
- import { SERIALIZATION_PREFIX } from './deployment'
7
- import { validateAnnotation } from './Annotation'
8
- import type { Declaration } from './providers'
9
- import type { Annotation } from './Annotation'
10
- import type { ProviderConstructor, ProviderSecrets } from './Provider'
11
-
12
- export class Factory {
13
- private readonly annotation: Annotation
14
-
15
- public constructor () {
16
- const env = process.env.TOA_STORAGES
17
-
18
- assert.ok(env !== undefined, 'TOA_STORAGES is not defined')
19
-
20
- this.annotation = decode(env)
21
-
22
- validateAnnotation(this.annotation)
23
- }
24
-
25
- public aspect (): Aspect {
26
- const storages = this.createStorages()
27
-
28
- return new Aspect(storages)
29
- }
30
-
31
- private createStorages (): Storages {
32
- const storages: Storages = {}
33
-
34
- for (const [name, declaration] of Object.entries(this.annotation))
35
- storages[name] = this.createStorage(name, declaration)
36
-
37
- return storages
38
- }
39
-
40
- private createStorage (name: string, declaration: Declaration): Storage {
41
- const { provider: providerId, ...options } = declaration
42
- const Provider: ProviderConstructor = providers[providerId]
43
- const secrets = this.resolveSecrets(name, Provider)
44
- const provider = new Provider(options, secrets)
45
-
46
- return new Storage(provider)
47
- }
48
-
49
- private resolveSecrets (storageName: string,
50
- Class: ProviderConstructor): ProviderSecrets {
51
- if (Class.SECRETS === undefined)
52
- return {}
53
-
54
- const secrets: Record<string, string | undefined> = {}
55
-
56
- for (const secret of Class.SECRETS) {
57
- const variable = `${SERIALIZATION_PREFIX}_${storageName}_${secret.name}`.toUpperCase()
58
- const value = process.env[variable]
59
-
60
- assert.ok(secret.optional === true || value !== undefined,
61
- `'${variable}' is not defined`)
62
-
63
- secrets[secret.name] = value
64
- }
65
-
66
- return secrets
67
- }
68
- }
@@ -1,37 +0,0 @@
1
- import { type Readable } from 'node:stream'
2
- import * as assert from 'node:assert'
3
-
4
- export type ProviderSecrets<K extends string = string> = Record<K | string, string | undefined>
5
-
6
- export interface ProviderSecret {
7
- readonly name: string
8
- readonly optional?: boolean
9
- }
10
-
11
- export abstract class Provider<Options = void> {
12
- public static readonly SECRETS: readonly ProviderSecret[] = []
13
-
14
- public constructor (_: Options, secrets?: ProviderSecrets) {
15
- for (const { name, optional = false } of new.target.SECRETS)
16
- assert.ok(optional || secrets?.[name] !== undefined, `Missing secret '${name}'`)
17
- }
18
-
19
- public abstract get (path: string): Promise<Readable | null>
20
-
21
- public abstract list (path: string): Promise<string[]>
22
-
23
- public abstract put (path: string, filename: string, stream: Readable): Promise<void>
24
-
25
- public abstract delete (path: string): Promise<void>
26
-
27
- public abstract move (from: string, to: string): Promise<void>
28
-
29
- public abstract moveDir (from: string, to: string): Promise<void>
30
- }
31
-
32
- export interface ProviderConstructor {
33
- readonly SECRETS: readonly ProviderSecret[]
34
- prototype: Provider
35
-
36
- new (options: any, secrets?: ProviderSecrets): Provider
37
- }
package/source/Scanner.ts DELETED
@@ -1,153 +0,0 @@
1
- import { PassThrough, type TransformCallback } from 'node:stream'
2
- import { createHash } from 'node:crypto'
3
- import { negotiate } from '@toa.io/agent'
4
- import { Err } from 'error-value'
5
-
6
- export class Scanner extends PassThrough {
7
- public size = 0
8
- public type = 'application/octet-stream'
9
- public error: Error | null = null
10
-
11
- private readonly hash = createHash('md5')
12
- private readonly claim?: string
13
- private readonly accept?: string
14
- private position = 0
15
- private detected = false
16
- private readonly chunks: Buffer[] = []
17
-
18
- public constructor (control?: ScanOptions) {
19
- super()
20
-
21
- this.claim = control?.claim
22
- this.accept = control?.accept
23
- }
24
-
25
- public digest (): string {
26
- return this.hash.digest('hex')
27
- }
28
-
29
- public override _transform
30
- (buffer: Buffer, encoding: BufferEncoding, callback: TransformCallback): void {
31
- super._transform(buffer, encoding, callback)
32
-
33
- this.process(buffer)
34
- }
35
-
36
- private readonly process = (buffer: Buffer): void => {
37
- this.size += buffer.length
38
- this.hash.update(buffer)
39
-
40
- if (this.detected)
41
- return
42
-
43
- if (this.position + buffer.length > HEADER_SIZE)
44
- buffer = buffer.subarray(0, HEADER_SIZE - this.position)
45
-
46
- this.chunks.push(buffer)
47
- this.position += buffer.length
48
-
49
- if (this.position === HEADER_SIZE)
50
- this.complete()
51
- }
52
-
53
- private complete (): void {
54
- const header = Buffer.concat(this.chunks).toString('hex')
55
-
56
- const signature = SIGNATURES
57
- .find(({ off, hex, expression }) => {
58
- const sig = header.slice(off, off + hex.length)
59
-
60
- return expression === undefined
61
- ? sig === hex
62
- : expression.test(sig)
63
- })
64
-
65
- const type = signature?.type ?? this.claim
66
-
67
- if (type !== undefined) {
68
- this.match(type)
69
- this.type = type
70
- }
71
-
72
- this.verify(signature)
73
- this.detected = true
74
- }
75
-
76
- private verify (signature: Signature | undefined): void {
77
- if (this.claim === undefined || this.claim === 'application/octet-stream')
78
- return
79
-
80
- const mismatch = signature === undefined
81
- ? KNOWN_TYPES.has(this.claim)
82
- : this.claim !== signature.type
83
-
84
- if (mismatch)
85
- this.interrupt(ERR_TYPE_MISMATCH)
86
- }
87
-
88
- private match (type: string): void {
89
- if (this.accept === undefined)
90
- return
91
-
92
- const unacceptable = negotiate(this.accept, [type]) === null
93
-
94
- if (unacceptable)
95
- this.interrupt(ERR_NOT_ACCEPTABLE)
96
- }
97
-
98
- private interrupt (error: Error): void {
99
- this.error = error
100
- this.end()
101
- }
102
- }
103
-
104
- // https://en.wikipedia.org/wiki/List_of_file_signatures
105
- const SIGNATURES: Signature[] = [
106
- { hex: 'ff d8 ff e0', off: 0, type: 'image/jpeg' },
107
- { hex: 'ff d8 ff e1', off: 0, type: 'image/jpeg' },
108
- { hex: 'ff d8 ff ee', off: 0, type: 'image/jpeg' },
109
- { hex: 'ff d8 ff db', off: 0, type: 'image/jpeg' },
110
- { hex: '89 50 4e 47', off: 0, type: 'image/png' },
111
- { hex: '47 49 46 38', off: 0, type: 'image/gif' },
112
- { hex: '52 49 46 46 ?? ?? ?? ?? 57 45 42 50', off: 0, type: 'image/webp' },
113
- { hex: '4a 58 4c 20 0d 0a 87 0a', off: 8, type: 'image/jxl' },
114
- { hex: '66 74 79 70 68 65 69 63', off: 8, type: 'image/heic' },
115
- { hex: '66 74 79 70 61 76 69 66', off: 8, type: 'image/avif' },
116
- { hex: '52 49 46 46 ?? ?? ?? ?? 41 56 49 20', off: 0, type: 'video/avi' },
117
- { hex: '52 49 46 46 ?? ?? ?? ?? 57 41 56 45', off: 0, type: 'audio/wav' }
118
- /*
119
- When adding a new signature, include a copyright-free sample file in the `.tests` directory
120
- and update the 'signatures' test group in `Storage.test.ts`.
121
- */
122
- ].map((signature: Signature) => {
123
- signature.hex = signature.hex.replaceAll(' ', '')
124
-
125
- if (signature.hex.includes('??')) {
126
- const expression = signature.hex.replaceAll(/(?<wildcards>\?{1,24})/g,
127
- (_, wildcards) => `[0-9a-f]{${wildcards.length}}`)
128
-
129
- signature.expression = new RegExp(expression, 'i')
130
- }
131
-
132
- return signature
133
- })
134
-
135
- const HEADER_SIZE = SIGNATURES
136
- .reduce((max, { off, hex }) => Math.max(max, off + hex.length), 0) / 2
137
-
138
- const KNOWN_TYPES = new Set(SIGNATURES.map(({ type }) => type))
139
-
140
- const ERR_TYPE_MISMATCH = Err('TYPE_MISMATCH')
141
- const ERR_NOT_ACCEPTABLE = Err('NOT_ACCEPTABLE')
142
-
143
- export interface ScanOptions {
144
- claim?: string
145
- accept?: string
146
- }
147
-
148
- interface Signature {
149
- type: string
150
- off: number
151
- hex: string
152
- expression?: RegExp
153
- }