@velajs/storage 0.3.1 → 0.3.2

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 (99) hide show
  1. package/CHANGELOG.md +6 -1
  2. package/dist/body-BMUcBeQ0.js +93 -0
  3. package/dist/body-BMUcBeQ0.js.map +1 -0
  4. package/dist/client/index.d.ts +49 -46
  5. package/dist/client/index.js +207 -228
  6. package/dist/client/index.js.map +1 -0
  7. package/dist/drivers/memory/index.d.ts +17 -13
  8. package/dist/drivers/memory/index.js +190 -197
  9. package/dist/drivers/memory/index.js.map +1 -0
  10. package/dist/drivers/r2/index.d.ts +7 -4
  11. package/dist/drivers/r2/index.js +162 -160
  12. package/dist/drivers/r2/index.js.map +1 -0
  13. package/dist/drivers/r2-http/index.d.ts +23 -19
  14. package/dist/drivers/r2-http/index.js +52 -45
  15. package/dist/drivers/r2-http/index.js.map +1 -0
  16. package/dist/drivers/s3/index.d.ts +79 -5
  17. package/dist/drivers/s3/index.js +2 -3
  18. package/dist/index.d.ts +271 -17
  19. package/dist/index.js +1078 -16
  20. package/dist/index.js.map +1 -0
  21. package/dist/middleware/index.d.ts +177 -15
  22. package/dist/middleware/index.js +553 -8
  23. package/dist/middleware/index.js.map +1 -0
  24. package/dist/protocol.types-C5PPBD12.d.ts +80 -0
  25. package/dist/r2.types-Bw4Ft83t.d.ts +71 -0
  26. package/dist/retry-DXtO6R9D.js +119 -0
  27. package/dist/retry-DXtO6R9D.js.map +1 -0
  28. package/dist/s3-DrE9NrnD.js +557 -0
  29. package/dist/s3-DrE9NrnD.js.map +1 -0
  30. package/dist/storage.error-DVCOkJhc.js +49 -0
  31. package/dist/storage.error-DVCOkJhc.js.map +1 -0
  32. package/dist/storage.types-BJN7Yp1J.d.ts +288 -0
  33. package/dist/storagesdk/index.d.ts +73 -69
  34. package/dist/storagesdk/index.js +117 -130
  35. package/dist/storagesdk/index.js.map +1 -0
  36. package/dist/stored-file-2Yl07eRc.js +105 -0
  37. package/dist/stored-file-2Yl07eRc.js.map +1 -0
  38. package/dist/testing/index.d.ts +12 -8
  39. package/dist/testing/index.js +36 -53
  40. package/dist/testing/index.js.map +1 -0
  41. package/package.json +62 -48
  42. package/dist/base64.d.ts +0 -8
  43. package/dist/base64.js +0 -25
  44. package/dist/decorators/inject-storage.decorator.d.ts +0 -17
  45. package/dist/decorators/inject-storage.decorator.js +0 -21
  46. package/dist/drivers/r2/r2.types.d.ts +0 -67
  47. package/dist/drivers/r2/r2.types.js +0 -5
  48. package/dist/drivers/s3/s3-client.d.ts +0 -33
  49. package/dist/drivers/s3/s3-client.js +0 -154
  50. package/dist/drivers/s3/s3.driver.d.ts +0 -4
  51. package/dist/drivers/s3/s3.driver.js +0 -407
  52. package/dist/drivers/s3/s3.types.d.ts +0 -28
  53. package/dist/drivers/s3/s3.types.js +0 -1
  54. package/dist/drivers/s3/xml.d.ts +0 -6
  55. package/dist/drivers/s3/xml.js +0 -52
  56. package/dist/http/authorizer.types.d.ts +0 -59
  57. package/dist/http/authorizer.types.js +0 -1
  58. package/dist/http/http-helpers.d.ts +0 -22
  59. package/dist/http/http-helpers.js +0 -80
  60. package/dist/http/protocol.types.d.ts +0 -76
  61. package/dist/http/protocol.types.js +0 -1
  62. package/dist/internal/body.d.ts +0 -18
  63. package/dist/internal/body.js +0 -90
  64. package/dist/internal/retry.d.ts +0 -41
  65. package/dist/internal/retry.js +0 -127
  66. package/dist/internal/stored-file.d.ts +0 -27
  67. package/dist/internal/stored-file.js +0 -114
  68. package/dist/middleware/cache.d.ts +0 -50
  69. package/dist/middleware/cache.js +0 -101
  70. package/dist/middleware/compose.d.ts +0 -12
  71. package/dist/middleware/compose.js +0 -11
  72. package/dist/middleware/compression.d.ts +0 -16
  73. package/dist/middleware/compression.js +0 -101
  74. package/dist/middleware/encryption.d.ts +0 -14
  75. package/dist/middleware/encryption.js +0 -134
  76. package/dist/middleware/failover.d.ts +0 -18
  77. package/dist/middleware/failover.js +0 -52
  78. package/dist/middleware/retry.d.ts +0 -14
  79. package/dist/middleware/retry.js +0 -47
  80. package/dist/middleware/versioning.d.ts +0 -36
  81. package/dist/middleware/versioning.js +0 -89
  82. package/dist/middleware/wrap.d.ts +0 -11
  83. package/dist/middleware/wrap.js +0 -46
  84. package/dist/object-key.d.ts +0 -17
  85. package/dist/object-key.js +0 -42
  86. package/dist/storage.controller.d.ts +0 -21
  87. package/dist/storage.controller.js +0 -386
  88. package/dist/storage.error.d.ts +0 -38
  89. package/dist/storage.error.js +0 -52
  90. package/dist/storage.facade.d.ts +0 -47
  91. package/dist/storage.facade.js +0 -443
  92. package/dist/storage.module.d.ts +0 -46
  93. package/dist/storage.module.js +0 -113
  94. package/dist/storage.service.d.ts +0 -42
  95. package/dist/storage.service.js +0 -86
  96. package/dist/storage.tokens.d.ts +0 -13
  97. package/dist/storage.tokens.js +0 -26
  98. package/dist/storage.types.d.ts +0 -245
  99. package/dist/storage.types.js +0 -1
@@ -1,136 +1,123 @@
1
- import { byteLengthOf } from "../internal/body.js";
2
- import { createStoredFile } from "../internal/stored-file.js";
3
- import { StorageError } from "../storage.error.js";
1
+ import { t as byteLengthOf } from "../body-BMUcBeQ0.js";
2
+ import { t as StorageError } from "../storage.error-DVCOkJhc.js";
3
+ import { t as createStoredFile } from "../stored-file-2Yl07eRc.js";
4
+ //#region src/storagesdk/index.ts
4
5
  function toMillis(d) {
5
- if (d == null) return undefined;
6
- return d instanceof Date ? d.getTime() : d;
6
+ if (d == null) return void 0;
7
+ return d instanceof Date ? d.getTime() : d;
7
8
  }
8
9
  function metaOf(item) {
9
- return {
10
- key: item.path,
11
- size: item.size,
12
- type: item.contentType || 'application/octet-stream',
13
- etag: item.etag,
14
- lastModified: toMillis(item.lastModified),
15
- metadata: item.metadata
16
- };
10
+ return {
11
+ key: item.path,
12
+ size: item.size,
13
+ type: item.contentType || "application/octet-stream",
14
+ etag: item.etag,
15
+ lastModified: toMillis(item.lastModified),
16
+ metadata: item.metadata
17
+ };
17
18
  }
18
19
  /**
19
- * Adapt a `@storagesdk/adapters` adapter to a {@link StorageDriver}.
20
- *
21
- * storagesdk's shape differs from ours (path-based; `download` returns buffered
22
- * bytes rather than a stream; no first-class multipart or capability flags),
23
- * so this is an explicit mapping. Multipart / presigned-multipart are not
24
- * exposed (the facade will gate them as `Unsupported`).
25
- */ export function storageSdkDriver(adapter, options) {
26
- const hasUploadUrl = typeof adapter.signedUploadUrl === 'function';
27
- const driver = {
28
- name: `storagesdk:${adapter.name ?? 'adapter'}`,
29
- raw: adapter.raw,
30
- supportsRange: options?.supportsRange ?? false,
31
- supportsDelimiter: options?.supportsDelimiter ?? false,
32
- supportsMetadata: options?.supportsMetadata ?? true,
33
- supportsCacheControl: options?.supportsCacheControl ?? false,
34
- supportsServerSideCopy: true,
35
- reportsUploadProgress: false,
36
- signedUrl: {
37
- supported: typeof adapter.url === 'function',
38
- upload: hasUploadUrl
39
- },
40
- async upload (key, body, opts) {
41
- const r = await adapter.upload(key, body, {
42
- contentType: opts?.contentType,
43
- metadata: opts?.metadata,
44
- signal: opts?.signal
45
- });
46
- return {
47
- key,
48
- size: r?.size ?? byteLengthOf(body) ?? 0,
49
- contentType: opts?.contentType ?? r?.contentType ?? 'application/octet-stream',
50
- etag: r?.etag,
51
- lastModified: toMillis(r?.lastModified)
52
- };
53
- },
54
- async download (key, opts) {
55
- const item = await adapter.download(key, {
56
- signal: opts?.signal
57
- });
58
- return createStoredFile(metaOf(item), {
59
- kind: 'bytes',
60
- bytes: item.body
61
- });
62
- },
63
- async head (key, opts) {
64
- const item = await adapter.head(key, {
65
- signal: opts?.signal
66
- });
67
- return createStoredFile(metaOf(item), {
68
- kind: 'lazy',
69
- fetch: async ()=>new Response((await adapter.download(key)).body)
70
- });
71
- },
72
- async exists (key, opts) {
73
- if (adapter.exists) return adapter.exists(key, {
74
- signal: opts?.signal
75
- });
76
- try {
77
- await adapter.head(key, {
78
- signal: opts?.signal
79
- });
80
- return true;
81
- } catch (e) {
82
- if (e instanceof StorageError && e.code === 'NotFound') return false;
83
- if (e && typeof e === 'object' && e.code === 'NotFound') return false;
84
- throw e;
85
- }
86
- },
87
- async delete (key, opts) {
88
- await adapter.delete(key, {
89
- signal: opts?.signal
90
- });
91
- },
92
- async copy (from, to, opts) {
93
- await adapter.copy(from, to, {
94
- signal: opts?.signal
95
- });
96
- },
97
- async list (opts) {
98
- const r = await adapter.list({
99
- prefix: opts?.prefix,
100
- cursor: opts?.cursor,
101
- limit: opts?.limit,
102
- delimiter: opts?.delimiter,
103
- signal: opts?.signal
104
- });
105
- return {
106
- items: r.items.map((item)=>createStoredFile(metaOf(item), {
107
- kind: 'lazy',
108
- fetch: async ()=>new Response((await adapter.download(item.path)).body)
109
- })),
110
- prefixes: r.prefixes,
111
- cursor: r.cursor
112
- };
113
- },
114
- async url (key, opts) {
115
- return adapter.url(key, {
116
- expiresIn: opts?.expiresIn,
117
- signal: opts?.signal
118
- });
119
- },
120
- async signedUploadUrl (key, opts) {
121
- if (!adapter.signedUploadUrl) {
122
- throw new StorageError('Unsupported', 'storagesdk adapter cannot presign uploads');
123
- }
124
- return adapter.signedUploadUrl(key, {
125
- expiresIn: opts.expiresIn,
126
- contentType: opts.contentType
127
- });
128
- }
129
- };
130
- if (adapter.move) {
131
- driver.move = (from, to, opts)=>adapter.move(from, to, {
132
- signal: opts?.signal
133
- });
134
- }
135
- return driver;
20
+ * Adapt a `@storagesdk/adapters` adapter to a {@link StorageDriver}.
21
+ *
22
+ * storagesdk's shape differs from ours (path-based; `download` returns buffered
23
+ * bytes rather than a stream; no first-class multipart or capability flags),
24
+ * so this is an explicit mapping. Multipart / presigned-multipart are not
25
+ * exposed (the facade will gate them as `Unsupported`).
26
+ */
27
+ function storageSdkDriver(adapter, options) {
28
+ const hasUploadUrl = typeof adapter.signedUploadUrl === "function";
29
+ const driver = {
30
+ name: `storagesdk:${adapter.name ?? "adapter"}`,
31
+ raw: adapter.raw,
32
+ supportsRange: options?.supportsRange ?? false,
33
+ supportsDelimiter: options?.supportsDelimiter ?? false,
34
+ supportsMetadata: options?.supportsMetadata ?? true,
35
+ supportsCacheControl: options?.supportsCacheControl ?? false,
36
+ supportsServerSideCopy: true,
37
+ reportsUploadProgress: false,
38
+ signedUrl: {
39
+ supported: typeof adapter.url === "function",
40
+ upload: hasUploadUrl
41
+ },
42
+ async upload(key, body, opts) {
43
+ const r = await adapter.upload(key, body, {
44
+ contentType: opts?.contentType,
45
+ metadata: opts?.metadata,
46
+ signal: opts?.signal
47
+ });
48
+ return {
49
+ key,
50
+ size: r?.size ?? byteLengthOf(body) ?? 0,
51
+ contentType: opts?.contentType ?? r?.contentType ?? "application/octet-stream",
52
+ etag: r?.etag,
53
+ lastModified: toMillis(r?.lastModified)
54
+ };
55
+ },
56
+ async download(key, opts) {
57
+ const item = await adapter.download(key, { signal: opts?.signal });
58
+ return createStoredFile(metaOf(item), {
59
+ kind: "bytes",
60
+ bytes: item.body
61
+ });
62
+ },
63
+ async head(key, opts) {
64
+ return createStoredFile(metaOf(await adapter.head(key, { signal: opts?.signal })), {
65
+ kind: "lazy",
66
+ fetch: async () => new Response((await adapter.download(key)).body)
67
+ });
68
+ },
69
+ async exists(key, opts) {
70
+ if (adapter.exists) return adapter.exists(key, { signal: opts?.signal });
71
+ try {
72
+ await adapter.head(key, { signal: opts?.signal });
73
+ return true;
74
+ } catch (e) {
75
+ if (e instanceof StorageError && e.code === "NotFound") return false;
76
+ if (e && typeof e === "object" && e.code === "NotFound") return false;
77
+ throw e;
78
+ }
79
+ },
80
+ async delete(key, opts) {
81
+ await adapter.delete(key, { signal: opts?.signal });
82
+ },
83
+ async copy(from, to, opts) {
84
+ await adapter.copy(from, to, { signal: opts?.signal });
85
+ },
86
+ async list(opts) {
87
+ const r = await adapter.list({
88
+ prefix: opts?.prefix,
89
+ cursor: opts?.cursor,
90
+ limit: opts?.limit,
91
+ delimiter: opts?.delimiter,
92
+ signal: opts?.signal
93
+ });
94
+ return {
95
+ items: r.items.map((item) => createStoredFile(metaOf(item), {
96
+ kind: "lazy",
97
+ fetch: async () => new Response((await adapter.download(item.path)).body)
98
+ })),
99
+ prefixes: r.prefixes,
100
+ cursor: r.cursor
101
+ };
102
+ },
103
+ async url(key, opts) {
104
+ return adapter.url(key, {
105
+ expiresIn: opts?.expiresIn,
106
+ signal: opts?.signal
107
+ });
108
+ },
109
+ async signedUploadUrl(key, opts) {
110
+ if (!adapter.signedUploadUrl) throw new StorageError("Unsupported", "storagesdk adapter cannot presign uploads");
111
+ return adapter.signedUploadUrl(key, {
112
+ expiresIn: opts.expiresIn,
113
+ contentType: opts.contentType
114
+ });
115
+ }
116
+ };
117
+ if (adapter.move) driver.move = (from, to, opts) => adapter.move(from, to, { signal: opts?.signal });
118
+ return driver;
136
119
  }
120
+ //#endregion
121
+ export { storageSdkDriver };
122
+
123
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/storagesdk/index.ts"],"sourcesContent":["import { byteLengthOf } from '../internal/body';\nimport { createStoredFile } from '../internal/stored-file';\nimport { StorageError } from '../storage.error';\nimport type {\n Body,\n DownloadOptions,\n ListOptions,\n ListResult,\n OperationOptions,\n SignUploadOptions,\n StorageDriver,\n StoredFile,\n UploadOptions,\n UploadResult,\n UrlOptions,\n} from '../storage.types';\n\n// Opt-in bridge for `@storagesdk/adapters`. It is defined against a STRUCTURAL\n// interface (below) — never an import of `@storagesdk/core` — so this file\n// stays edge-clean and adds no dependency. Bring your own storagesdk adapter;\n// it is structurally assignable. NOTE: whichever storagesdk adapter you\n// construct may pull the AWS SDK / node built-ins, so this subpath is for\n// Node/Bun deployments, never the edge path. It is never re-exported from `.`.\n\n/** Metadata for one storagesdk object (path-based). */\nexport interface StorageSdkItemMeta {\n path: string;\n size: number;\n contentType: string;\n etag: string;\n lastModified: Date | number;\n metadata?: Record<string, string>;\n}\n\n/** A storagesdk download result: metadata plus the buffered body. */\nexport interface StorageSdkItem extends StorageSdkItemMeta {\n body: Uint8Array<ArrayBuffer>;\n}\n\nexport interface StorageSdkListResult {\n items: StorageSdkItemMeta[];\n cursor?: string;\n prefixes?: string[];\n}\n\n/** The structural subset of a `@storagesdk/adapters` adapter we consume. */\nexport interface StorageSdkAdapterLike<Raw = unknown> {\n readonly name?: string;\n readonly raw?: Raw;\n download(path: string, opts?: { signal?: AbortSignal }): Promise<StorageSdkItem>;\n head(path: string, opts?: { signal?: AbortSignal }): Promise<StorageSdkItemMeta>;\n list(opts?: {\n prefix?: string;\n cursor?: string;\n limit?: number;\n delimiter?: string;\n signal?: AbortSignal;\n }): Promise<StorageSdkListResult>;\n url(path: string, opts?: { expiresIn?: number; signal?: AbortSignal }): Promise<string>;\n upload(\n path: string,\n body: Body,\n opts?: { contentType?: string; metadata?: Record<string, string>; signal?: AbortSignal },\n ): Promise<StorageSdkItemMeta | void>;\n delete(path: string, opts?: { signal?: AbortSignal }): Promise<void>;\n copy(from: string, to: string, opts?: { signal?: AbortSignal }): Promise<void>;\n move?(from: string, to: string, opts?: { signal?: AbortSignal }): Promise<void>;\n exists?(path: string, opts?: { signal?: AbortSignal }): Promise<boolean>;\n signedUploadUrl?(\n path: string,\n opts: { expiresIn: number; contentType?: string },\n ): Promise<{ method: 'PUT'; url: string; headers?: Record<string, string> }>;\n}\n\nexport interface StorageSdkBridgeOptions {\n /** Advertise range support (only if your adapter honors `range`). Default false. */\n supportsRange?: boolean;\n /** Advertise delimiter/prefix support. Default false. */\n supportsDelimiter?: boolean;\n /** Advertise arbitrary metadata support. Default true. */\n supportsMetadata?: boolean;\n /** Advertise cache-control support. Default false. */\n supportsCacheControl?: boolean;\n}\n\nfunction toMillis(d: Date | number | undefined): number | undefined {\n if (d == null) return undefined;\n return d instanceof Date ? d.getTime() : d;\n}\n\nfunction metaOf(item: StorageSdkItemMeta) {\n return {\n key: item.path,\n size: item.size,\n type: item.contentType || 'application/octet-stream',\n etag: item.etag,\n lastModified: toMillis(item.lastModified),\n metadata: item.metadata,\n };\n}\n\n/**\n * Adapt a `@storagesdk/adapters` adapter to a {@link StorageDriver}.\n *\n * storagesdk's shape differs from ours (path-based; `download` returns buffered\n * bytes rather than a stream; no first-class multipart or capability flags),\n * so this is an explicit mapping. Multipart / presigned-multipart are not\n * exposed (the facade will gate them as `Unsupported`).\n */\nexport function storageSdkDriver<Raw = unknown>(\n adapter: StorageSdkAdapterLike<Raw>,\n options?: StorageSdkBridgeOptions,\n): StorageDriver<Raw> {\n const hasUploadUrl = typeof adapter.signedUploadUrl === 'function';\n\n const driver: StorageDriver<Raw> = {\n name: `storagesdk:${adapter.name ?? 'adapter'}`,\n raw: adapter.raw as Raw,\n supportsRange: options?.supportsRange ?? false,\n supportsDelimiter: options?.supportsDelimiter ?? false,\n supportsMetadata: options?.supportsMetadata ?? true,\n supportsCacheControl: options?.supportsCacheControl ?? false,\n supportsServerSideCopy: true,\n reportsUploadProgress: false,\n signedUrl: { supported: typeof adapter.url === 'function', upload: hasUploadUrl },\n\n async upload(key: string, body: Body, opts?: UploadOptions): Promise<UploadResult> {\n const r = await adapter.upload(key, body, {\n contentType: opts?.contentType,\n metadata: opts?.metadata,\n signal: opts?.signal,\n });\n return {\n key,\n size: r?.size ?? byteLengthOf(body) ?? 0,\n contentType: opts?.contentType ?? r?.contentType ?? 'application/octet-stream',\n etag: r?.etag,\n lastModified: toMillis(r?.lastModified),\n };\n },\n\n async download(key: string, opts?: DownloadOptions): Promise<StoredFile> {\n const item = await adapter.download(key, { signal: opts?.signal });\n return createStoredFile(metaOf(item), { kind: 'bytes', bytes: item.body });\n },\n\n async head(key: string, opts?: OperationOptions): Promise<StoredFile> {\n const item = await adapter.head(key, { signal: opts?.signal });\n return createStoredFile(metaOf(item), {\n kind: 'lazy',\n fetch: async () => new Response((await adapter.download(key)).body),\n });\n },\n\n async exists(key: string, opts?: OperationOptions): Promise<boolean> {\n if (adapter.exists) return adapter.exists(key, { signal: opts?.signal });\n try {\n await adapter.head(key, { signal: opts?.signal });\n return true;\n } catch (e) {\n if (e instanceof StorageError && e.code === 'NotFound') return false;\n if (e && typeof e === 'object' && (e as { code?: string }).code === 'NotFound')\n return false;\n throw e;\n }\n },\n\n async delete(key: string, opts?: OperationOptions): Promise<void> {\n await adapter.delete(key, { signal: opts?.signal });\n },\n\n async copy(from: string, to: string, opts?: OperationOptions): Promise<void> {\n await adapter.copy(from, to, { signal: opts?.signal });\n },\n\n async list(opts?: ListOptions): Promise<ListResult> {\n const r = await adapter.list({\n prefix: opts?.prefix,\n cursor: opts?.cursor,\n limit: opts?.limit,\n delimiter: opts?.delimiter,\n signal: opts?.signal,\n });\n return {\n items: r.items.map((item) =>\n createStoredFile(metaOf(item), {\n kind: 'lazy',\n fetch: async () => new Response((await adapter.download(item.path)).body),\n }),\n ),\n prefixes: r.prefixes,\n cursor: r.cursor,\n };\n },\n\n async url(key: string, opts?: UrlOptions): Promise<string> {\n return adapter.url(key, { expiresIn: opts?.expiresIn, signal: opts?.signal });\n },\n\n async signedUploadUrl(key: string, opts: SignUploadOptions) {\n if (!adapter.signedUploadUrl) {\n throw new StorageError('Unsupported', 'storagesdk adapter cannot presign uploads');\n }\n return adapter.signedUploadUrl(key, {\n expiresIn: opts.expiresIn,\n contentType: opts.contentType,\n });\n },\n };\n\n if (adapter.move) {\n driver.move = (from, to, opts) => adapter.move!(from, to, { signal: opts?.signal });\n }\n return driver;\n}\n"],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ import { t as StorageError } from "./storage.error-DVCOkJhc.js";
2
+ //#region src/internal/stored-file.ts
3
+ function nameFromKey(key) {
4
+ const i = key.lastIndexOf("/");
5
+ return i === -1 ? key : key.slice(i + 1);
6
+ }
7
+ function bytesToStream(bytes) {
8
+ return new ReadableStream({ start(controller) {
9
+ controller.enqueue(bytes);
10
+ controller.close();
11
+ } });
12
+ }
13
+ function toArrayBuffer(bytes) {
14
+ const ab = new ArrayBuffer(bytes.byteLength);
15
+ new Uint8Array(ab).set(bytes);
16
+ return ab;
17
+ }
18
+ var StoredFileImpl = class {
19
+ key;
20
+ name;
21
+ size;
22
+ type;
23
+ lastModified;
24
+ etag;
25
+ metadata;
26
+ #source;
27
+ #bytes;
28
+ #streamTaken = false;
29
+ constructor(meta, source) {
30
+ this.key = meta.key;
31
+ this.name = meta.name ?? nameFromKey(meta.key);
32
+ this.size = meta.size;
33
+ this.type = meta.type ?? "application/octet-stream";
34
+ this.lastModified = meta.lastModified;
35
+ this.etag = meta.etag;
36
+ this.metadata = meta.metadata;
37
+ this.#source = source;
38
+ if (source.kind === "bytes") this.#bytes = source.bytes;
39
+ }
40
+ async #buffer() {
41
+ if (this.#bytes) return this.#bytes;
42
+ const s = this.#source;
43
+ if (s.kind === "bytes") {
44
+ this.#bytes = s.bytes;
45
+ return this.#bytes;
46
+ }
47
+ if (s.kind === "stream") {
48
+ if (this.#streamTaken) throw new StorageError("Provider", `body of ${this.key} already consumed`);
49
+ this.#streamTaken = true;
50
+ this.#bytes = new Uint8Array(await new Response(s.stream).arrayBuffer());
51
+ return this.#bytes;
52
+ }
53
+ const res = await s.fetch();
54
+ if (!res.ok) throw StorageError.fromStatus(res.status, `download failed: ${this.key}`);
55
+ this.#bytes = new Uint8Array(await res.arrayBuffer());
56
+ return this.#bytes;
57
+ }
58
+ async arrayBuffer() {
59
+ return toArrayBuffer(await this.#buffer());
60
+ }
61
+ async text() {
62
+ return new TextDecoder().decode(await this.#buffer());
63
+ }
64
+ async blob() {
65
+ const b = await this.#buffer();
66
+ return new Blob([toArrayBuffer(b)], { type: this.type });
67
+ }
68
+ stream() {
69
+ if (this.#bytes) return bytesToStream(this.#bytes);
70
+ const s = this.#source;
71
+ if (s.kind === "bytes") return bytesToStream(s.bytes);
72
+ if (s.kind === "stream") {
73
+ if (this.#streamTaken) throw new StorageError("Provider", `body of ${this.key} already consumed`);
74
+ this.#streamTaken = true;
75
+ return s.stream;
76
+ }
77
+ let reader;
78
+ const fetchFn = s.fetch;
79
+ const key = this.key;
80
+ return new ReadableStream({
81
+ async pull(controller) {
82
+ let r = reader;
83
+ if (!r) {
84
+ const res = await fetchFn();
85
+ if (!res.ok || !res.body) throw StorageError.fromStatus(res.status, `download failed: ${key}`);
86
+ r = res.body.getReader();
87
+ reader = r;
88
+ }
89
+ const { done, value } = await r.read();
90
+ if (done) controller.close();
91
+ else controller.enqueue(value);
92
+ },
93
+ cancel(reason) {
94
+ return reader?.cancel(reason);
95
+ }
96
+ });
97
+ }
98
+ };
99
+ function createStoredFile(meta, source) {
100
+ return new StoredFileImpl(meta, source);
101
+ }
102
+ //#endregion
103
+ export { createStoredFile as t };
104
+
105
+ //# sourceMappingURL=stored-file-2Yl07eRc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stored-file-2Yl07eRc.js","names":["#source","#bytes","#buffer","#streamTaken"],"sources":["../src/internal/stored-file.ts"],"sourcesContent":["import { StorageError } from '../storage.error';\nimport type { StoredFile } from '../storage.types';\n\nexport interface StoredFileMeta {\n key: string;\n name?: string;\n size: number;\n type?: string;\n lastModified?: number;\n etag?: string;\n metadata?: Record<string, string>;\n}\n\n/**\n * Where a {@link StoredFile}'s bytes come from:\n * - `bytes` — already in memory (memory driver, buffered reads).\n * - `stream` — a single-use byte stream (a live download response body).\n * - `lazy` — a thunk that issues the GET on first body access (head()).\n */\nexport type StoredFileSource =\n | { kind: 'bytes'; bytes: Uint8Array }\n | { kind: 'stream'; stream: ReadableStream<Uint8Array> }\n | { kind: 'lazy'; fetch: () => Promise<Response> };\n\nfunction nameFromKey(key: string): string {\n const i = key.lastIndexOf('/');\n return i === -1 ? key : key.slice(i + 1);\n}\n\nfunction bytesToStream(bytes: Uint8Array): ReadableStream<Uint8Array> {\n return new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(bytes);\n controller.close();\n },\n });\n}\n\nfunction toArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n const ab = new ArrayBuffer(bytes.byteLength);\n new Uint8Array(ab).set(bytes);\n return ab;\n}\n\nclass StoredFileImpl implements StoredFile {\n readonly key: string;\n readonly name: string;\n readonly size: number;\n readonly type: string;\n readonly lastModified?: number;\n readonly etag?: string;\n readonly metadata?: Record<string, string>;\n\n #source: StoredFileSource;\n #bytes: Uint8Array | undefined;\n #streamTaken = false;\n\n constructor(meta: StoredFileMeta, source: StoredFileSource) {\n this.key = meta.key;\n this.name = meta.name ?? nameFromKey(meta.key);\n this.size = meta.size;\n this.type = meta.type ?? 'application/octet-stream';\n this.lastModified = meta.lastModified;\n this.etag = meta.etag;\n this.metadata = meta.metadata;\n this.#source = source;\n if (source.kind === 'bytes') this.#bytes = source.bytes;\n }\n\n async #buffer(): Promise<Uint8Array> {\n if (this.#bytes) return this.#bytes;\n const s = this.#source;\n if (s.kind === 'bytes') {\n this.#bytes = s.bytes;\n return this.#bytes;\n }\n if (s.kind === 'stream') {\n if (this.#streamTaken) {\n throw new StorageError('Provider', `body of ${this.key} already consumed`);\n }\n this.#streamTaken = true;\n this.#bytes = new Uint8Array(await new Response(s.stream).arrayBuffer());\n return this.#bytes;\n }\n // s.kind === 'lazy'\n const res = await s.fetch();\n if (!res.ok) throw StorageError.fromStatus(res.status, `download failed: ${this.key}`);\n this.#bytes = new Uint8Array(await res.arrayBuffer());\n return this.#bytes;\n }\n\n async arrayBuffer(): Promise<ArrayBuffer> {\n return toArrayBuffer(await this.#buffer());\n }\n\n async text(): Promise<string> {\n return new TextDecoder().decode(await this.#buffer());\n }\n\n async blob(): Promise<Blob> {\n const b = await this.#buffer();\n return new Blob([toArrayBuffer(b)], { type: this.type });\n }\n\n stream(): ReadableStream<Uint8Array> {\n if (this.#bytes) return bytesToStream(this.#bytes);\n const s = this.#source;\n if (s.kind === 'bytes') return bytesToStream(s.bytes);\n if (s.kind === 'stream') {\n if (this.#streamTaken) {\n throw new StorageError('Provider', `body of ${this.key} already consumed`);\n }\n this.#streamTaken = true;\n return s.stream;\n }\n // lazy: fetch on first pull\n let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n const fetchFn = s.fetch;\n const key = this.key;\n return new ReadableStream<Uint8Array>({\n async pull(controller) {\n let r = reader;\n if (!r) {\n const res = await fetchFn();\n if (!res.ok || !res.body) {\n throw StorageError.fromStatus(res.status, `download failed: ${key}`);\n }\n r = res.body.getReader();\n reader = r;\n }\n const { done, value } = await r.read();\n if (done) controller.close();\n else controller.enqueue(value);\n },\n cancel(reason) {\n return reader?.cancel(reason);\n },\n });\n }\n}\n\nexport function createStoredFile(meta: StoredFileMeta, source: StoredFileSource): StoredFile {\n return new StoredFileImpl(meta, source);\n}\n"],"mappings":"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"}
@@ -1,17 +1,21 @@
1
- import type { ListOptions, ListResult, OperationOptions } from '../storage.types';
1
+ import { c as ListOptions, f as OperationOptions, l as ListResult } from "../storage.types-BJN7Yp1J.js";
2
+ //#region src/testing/index.d.ts
2
3
  /**
3
4
  * The read surface the assertions need. Satisfied by `StorageService`, the
4
5
  * `Storage` facade, and a bare `StorageDriver` — anything memory-backed works.
5
6
  */
6
- export interface StorageTarget {
7
- exists(key: string, opts?: OperationOptions): Promise<boolean>;
8
- list(opts?: ListOptions): Promise<ListResult>;
7
+ interface StorageTarget {
8
+ exists(key: string, opts?: OperationOptions): Promise<boolean>;
9
+ list(opts?: ListOptions): Promise<ListResult>;
9
10
  }
10
11
  /** Assert an object exists at `key`; throws a diagnostic `Error` otherwise. */
11
- export declare function assertExists(storage: StorageTarget, key: string): Promise<void>;
12
+ declare function assertExists(storage: StorageTarget, key: string): Promise<void>;
12
13
  /** Assert nothing is stored at `key`; throws a diagnostic `Error` otherwise. */
13
- export declare function assertMissing(storage: StorageTarget, key: string): Promise<void>;
14
+ declare function assertMissing(storage: StorageTarget, key: string): Promise<void>;
14
15
  /** Assert the disk holds exactly `expected` objects. */
15
- export declare function assertCount(storage: StorageTarget, expected: number): Promise<void>;
16
+ declare function assertCount(storage: StorageTarget, expected: number): Promise<void>;
16
17
  /** Assert exactly `expected` objects exist under `prefix`. */
17
- export declare function assertCount(storage: StorageTarget, prefix: string, expected: number): Promise<void>;
18
+ declare function assertCount(storage: StorageTarget, prefix: string, expected: number): Promise<void>;
19
+ //#endregion
20
+ export { StorageTarget, assertCount, assertExists, assertMissing };
21
+ //# sourceMappingURL=index.d.ts.map
@@ -1,57 +1,40 @@
1
- // @velajs/storage/testing — assertion helpers for tests.
2
- //
3
- // Vela has no separate storage fake: the in-package **memory driver IS the
4
- // fake**. Build a test disk in one obvious line and assert against it —
5
- // no bootstrap, no subclass:
6
- //
7
- // import { createStorage } from '@velajs/storage';
8
- // import { memoryDriver } from '@velajs/storage/drivers/memory';
9
- // import { assertExists, assertCount } from '@velajs/storage/testing';
10
- //
11
- // const storage = createStorage({ driver: memoryDriver() });
12
- // await storage.upload('avatar.png', bytes);
13
- // await assertExists(storage, 'avatar.png');
14
- // await assertCount(storage, 'avatars/', 1);
15
- //
16
- // For DI/integration tests, register the same driver on the module instead —
17
- // `StorageModule.forRoot({ driver: memoryDriver() })` — then inject the
18
- // `StorageService` and pass it to these helpers. Both a `Storage` facade and a
19
- // `StorageService` satisfy the {@link StorageTarget} surface below.
20
- //
21
- // The helpers are thin and framework-free: they throw a plain `Error` with a
22
- // diagnostic message on failure (no vitest/`expect` dependency), and drive the
23
- // public `exists`/`list` API so prefix scoping and drivers stay honest.
24
- /** Assert an object exists at `key`; throws a diagnostic `Error` otherwise. */ export async function assertExists(storage, key) {
25
- if (!await storage.exists(key)) {
26
- const keys = await collectKeys(storage);
27
- throw new Error(`assertExists: expected an object at "${key}" to exist, but it is missing. ` + `Stored: ${keys.length ? keys.join(', ') : '(none)'}`);
28
- }
1
+ //#region src/testing/index.ts
2
+ /** Assert an object exists at `key`; throws a diagnostic `Error` otherwise. */
3
+ async function assertExists(storage, key) {
4
+ if (!await storage.exists(key)) {
5
+ const keys = await collectKeys(storage);
6
+ throw new Error(`assertExists: expected an object at "${key}" to exist, but it is missing. Stored: ${keys.length ? keys.join(", ") : "(none)"}`);
7
+ }
29
8
  }
30
- /** Assert nothing is stored at `key`; throws a diagnostic `Error` otherwise. */ export async function assertMissing(storage, key) {
31
- if (await storage.exists(key)) {
32
- throw new Error(`assertMissing: expected no object at "${key}", but one exists`);
33
- }
9
+ /** Assert nothing is stored at `key`; throws a diagnostic `Error` otherwise. */
10
+ async function assertMissing(storage, key) {
11
+ if (await storage.exists(key)) throw new Error(`assertMissing: expected no object at "${key}", but one exists`);
34
12
  }
35
- export async function assertCount(storage, prefixOrExpected, maybeExpected) {
36
- const prefix = typeof prefixOrExpected === 'string' ? prefixOrExpected : undefined;
37
- const expected = typeof prefixOrExpected === 'number' ? prefixOrExpected : maybeExpected;
38
- const keys = await collectKeys(storage, prefix);
39
- if (keys.length !== expected) {
40
- const scope = prefix ? ` under prefix "${prefix}"` : '';
41
- const found = keys.length ? `: ${keys.join(', ')}` : '';
42
- throw new Error(`assertCount: expected ${expected} object(s)${scope}, but found ${keys.length}${found}`);
43
- }
13
+ async function assertCount(storage, prefixOrExpected, maybeExpected) {
14
+ const prefix = typeof prefixOrExpected === "string" ? prefixOrExpected : void 0;
15
+ const expected = typeof prefixOrExpected === "number" ? prefixOrExpected : maybeExpected;
16
+ const keys = await collectKeys(storage, prefix);
17
+ if (keys.length !== expected) {
18
+ const scope = prefix ? ` under prefix "${prefix}"` : "";
19
+ const found = keys.length ? `: ${keys.join(", ")}` : "";
20
+ throw new Error(`assertCount: expected ${expected} object(s)${scope}, but found ${keys.length}${found}`);
21
+ }
44
22
  }
45
- /** Walk every listing page (following the cursor) and collect the object keys. */ async function collectKeys(storage, prefix) {
46
- const keys = [];
47
- let cursor;
48
- do {
49
- const page = await storage.list({
50
- prefix,
51
- cursor
52
- });
53
- for (const item of page.items)keys.push(item.key);
54
- cursor = page.cursor;
55
- }while (cursor)
56
- return keys;
23
+ /** Walk every listing page (following the cursor) and collect the object keys. */
24
+ async function collectKeys(storage, prefix) {
25
+ const keys = [];
26
+ let cursor;
27
+ do {
28
+ const page = await storage.list({
29
+ prefix,
30
+ cursor
31
+ });
32
+ for (const item of page.items) keys.push(item.key);
33
+ cursor = page.cursor;
34
+ } while (cursor);
35
+ return keys;
57
36
  }
37
+ //#endregion
38
+ export { assertCount, assertExists, assertMissing };
39
+
40
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/testing/index.ts"],"sourcesContent":["// @velajs/storage/testing — assertion helpers for tests.\n//\n// Vela has no separate storage fake: the in-package **memory driver IS the\n// fake**. Build a test disk in one obvious line and assert against it —\n// no bootstrap, no subclass:\n//\n// import { createStorage } from '@velajs/storage';\n// import { memoryDriver } from '@velajs/storage/drivers/memory';\n// import { assertExists, assertCount } from '@velajs/storage/testing';\n//\n// const storage = createStorage({ driver: memoryDriver() });\n// await storage.upload('avatar.png', bytes);\n// await assertExists(storage, 'avatar.png');\n// await assertCount(storage, 'avatars/', 1);\n//\n// For DI/integration tests, register the same driver on the module instead —\n// `StorageModule.forRoot({ driver: memoryDriver() })` — then inject the\n// `StorageService` and pass it to these helpers. Both a `Storage` facade and a\n// `StorageService` satisfy the {@link StorageTarget} surface below.\n//\n// The helpers are thin and framework-free: they throw a plain `Error` with a\n// diagnostic message on failure (no vitest/`expect` dependency), and drive the\n// public `exists`/`list` API so prefix scoping and drivers stay honest.\n\nimport type { ListOptions, ListResult, OperationOptions } from '../storage.types';\n\n/**\n * The read surface the assertions need. Satisfied by `StorageService`, the\n * `Storage` facade, and a bare `StorageDriver` — anything memory-backed works.\n */\nexport interface StorageTarget {\n exists(key: string, opts?: OperationOptions): Promise<boolean>;\n list(opts?: ListOptions): Promise<ListResult>;\n}\n\n/** Assert an object exists at `key`; throws a diagnostic `Error` otherwise. */\nexport async function assertExists(storage: StorageTarget, key: string): Promise<void> {\n if (!(await storage.exists(key))) {\n const keys = await collectKeys(storage);\n throw new Error(\n `assertExists: expected an object at \"${key}\" to exist, but it is missing. ` +\n `Stored: ${keys.length ? keys.join(', ') : '(none)'}`,\n );\n }\n}\n\n/** Assert nothing is stored at `key`; throws a diagnostic `Error` otherwise. */\nexport async function assertMissing(storage: StorageTarget, key: string): Promise<void> {\n if (await storage.exists(key)) {\n throw new Error(`assertMissing: expected no object at \"${key}\", but one exists`);\n }\n}\n\n/** Assert the disk holds exactly `expected` objects. */\nexport function assertCount(storage: StorageTarget, expected: number): Promise<void>;\n/** Assert exactly `expected` objects exist under `prefix`. */\nexport function assertCount(\n storage: StorageTarget,\n prefix: string,\n expected: number,\n): Promise<void>;\nexport async function assertCount(\n storage: StorageTarget,\n prefixOrExpected: string | number,\n maybeExpected?: number,\n): Promise<void> {\n const prefix = typeof prefixOrExpected === 'string' ? prefixOrExpected : undefined;\n const expected = typeof prefixOrExpected === 'number' ? prefixOrExpected : maybeExpected!;\n const keys = await collectKeys(storage, prefix);\n if (keys.length !== expected) {\n const scope = prefix ? ` under prefix \"${prefix}\"` : '';\n const found = keys.length ? `: ${keys.join(', ')}` : '';\n throw new Error(\n `assertCount: expected ${expected} object(s)${scope}, but found ${keys.length}${found}`,\n );\n }\n}\n\n/** Walk every listing page (following the cursor) and collect the object keys. */\nasync function collectKeys(storage: StorageTarget, prefix?: string): Promise<string[]> {\n const keys: string[] = [];\n let cursor: string | undefined;\n do {\n const page = await storage.list({ prefix, cursor });\n for (const item of page.items) keys.push(item.key);\n cursor = page.cursor;\n } while (cursor);\n return keys;\n}\n"],"mappings":";;AAoCA,eAAsB,aAAa,SAAwB,KAA4B;CACrF,IAAI,CAAE,MAAM,QAAQ,OAAO,GAAG,GAAI;EAChC,MAAM,OAAO,MAAM,YAAY,OAAO;EACtC,MAAM,IAAI,MACR,wCAAwC,IAAI,yCAC/B,KAAK,SAAS,KAAK,KAAK,IAAI,IAAI,UAC/C;CACF;AACF;;AAGA,eAAsB,cAAc,SAAwB,KAA4B;CACtF,IAAI,MAAM,QAAQ,OAAO,GAAG,GAC1B,MAAM,IAAI,MAAM,yCAAyC,IAAI,kBAAkB;AAEnF;AAUA,eAAsB,YACpB,SACA,kBACA,eACe;CACf,MAAM,SAAS,OAAO,qBAAqB,WAAW,mBAAmB,KAAA;CACzE,MAAM,WAAW,OAAO,qBAAqB,WAAW,mBAAmB;CAC3E,MAAM,OAAO,MAAM,YAAY,SAAS,MAAM;CAC9C,IAAI,KAAK,WAAW,UAAU;EAC5B,MAAM,QAAQ,SAAS,kBAAkB,OAAO,KAAK;EACrD,MAAM,QAAQ,KAAK,SAAS,KAAK,KAAK,KAAK,IAAI,MAAM;EACrD,MAAM,IAAI,MACR,yBAAyB,SAAS,YAAY,MAAM,cAAc,KAAK,SAAS,OAClF;CACF;AACF;;AAGA,eAAe,YAAY,SAAwB,QAAoC;CACrF,MAAM,OAAiB,CAAC;CACxB,IAAI;CACJ,GAAG;EACD,MAAM,OAAO,MAAM,QAAQ,KAAK;GAAE;GAAQ;EAAO,CAAC;EAClD,KAAK,MAAM,QAAQ,KAAK,OAAO,KAAK,KAAK,KAAK,GAAG;EACjD,SAAS,KAAK;CAChB,SAAS;CACT,OAAO;AACT"}