@wrelik/storage 0.2.1 → 2.0.1

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.
@@ -1,26 +1,27 @@
1
1
 
2
- > @wrelik/storage@0.2.1 build /home/runner/work/wrelik-kit/wrelik-kit/packages/storage
3
- > tsup src/index.ts src/react-native.ts --format cjs,esm --dts --clean
2
+ > @wrelik/storage@2.0.1 build /Users/lee/Projects/wrelik-kit/packages/storage
3
+ > tsup src/server/index.ts src/client/index.ts src/shared/index.ts --format cjs,esm --dts --clean
4
4
 
5
- CLI Building entry: src/index.ts, src/react-native.ts
6
- CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.5.1
8
- CLI Target: es2022
9
- CLI Cleaning output folder
10
- CJS Build start
11
- ESM Build start
12
- ESM dist/index.mjs 1.46 KB
13
- ESM dist/react-native.mjs 701.00 B
14
- ESM dist/chunk-6VKCOC6I.mjs 421.00 B
15
- ESM ⚡️ Build success in 59ms
16
- CJS dist/index.js 3.10 KB
17
- CJS dist/react-native.js 2.17 KB
18
- CJS ⚡️ Build success in 61ms
19
- DTS Build start
20
- DTS ⚡️ Build success in 4085ms
21
- DTS dist/index.d.ts 815.00 B
22
- DTS dist/react-native.d.ts 457.00 B
23
- DTS dist/shared-DqbBUqiW.d.ts 201.00 B
24
- DTS dist/index.d.mts 816.00 B
25
- DTS dist/react-native.d.mts 458.00 B
26
- DTS dist/shared-DqbBUqiW.d.mts 201.00 B
5
+ CLI Building entry: src/shared/index.ts, src/server/index.ts, src/client/index.ts
6
+ CLI Using tsconfig: tsconfig.json
7
+ CLI tsup v8.5.1
8
+ CLI Target: es2022
9
+ CLI Cleaning output folder
10
+ CJS Build start
11
+ ESM Build start
12
+ ESM dist/client/index.mjs 625.00 B
13
+ ESM dist/server/index.mjs 2.86 KB
14
+ ESM dist/shared/index.mjs 433.00 B
15
+ ESM ⚡️ Build success in 249ms
16
+ CJS dist/shared/index.js 1.44 KB
17
+ CJS dist/client/index.js 1.67 KB
18
+ CJS dist/server/index.js 4.13 KB
19
+ CJS ⚡️ Build success in 249ms
20
+ DTS Build start
21
+ DTS ⚡️ Build success in 3780ms
22
+ DTS dist/shared/index.d.ts 193.00 B
23
+ DTS dist/server/index.d.ts 1.99 KB
24
+ DTS dist/client/index.d.ts 337.00 B
25
+ DTS dist/shared/index.d.mts 193.00 B
26
+ DTS dist/server/index.d.mts 1.99 KB
27
+ DTS dist/client/index.d.mts 337.00 B
@@ -0,0 +1,5 @@
1
+
2
+ > @wrelik/storage@0.2.1 lint /Users/lee/Projects/wrelik-kit/packages/storage
3
+ > eslint src/
4
+
5
+ Warning: React version was set to "detect" in eslint-plugin-react settings, but the "react" package is not installed. Assuming latest React version for linting.
@@ -0,0 +1,15 @@
1
+
2
+ > @wrelik/storage@2.0.1 test /Users/lee/Projects/wrelik-kit/packages/storage
3
+ > vitest run --passWithNoTests
4
+
5
+
6
+  RUN  v4.1.2 /Users/lee/Projects/wrelik-kit/packages/storage
7
+
8
+ ✓ src/shared/index.test.ts (1 test) 9ms
9
+ ✓ src/server/index.test.ts (4 tests) 11ms
10
+
11
+  Test Files  2 passed (2)
12
+  Tests  5 passed (5)
13
+  Start at  20:04:57
14
+  Duration  1.74s (transform 597ms, setup 0ms, import 1.03s, tests 21ms, environment 1ms)
15
+
@@ -0,0 +1,4 @@
1
+
2
+ > @wrelik/storage@2.0.0 typecheck /Users/lee/Projects/wrelik-kit/packages/storage
3
+ > tsc --noEmit
4
+
package/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # @wrelik/storage
2
2
 
3
+ ## 2.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 3262454: Upgrade runtime SDK and workspace tooling dependencies to latest compatible versions, including Clerk, Sentry, AWS SDK, Inngest, Resend, Upstash, Zod, TypeScript, Turbo, Vitest, TSUP, and ESLint.
8
+
9
+ Adopt root ESLint flat config and align wrapper peer ranges for updated SDK majors while preserving package export paths. Also apply a patched transitive Next.js resolution to close audit findings and keep production audit clean.
10
+
11
+ Note: Vite is pinned to the latest 7.x line for runtime compatibility in this environment; this avoids current rolldown native-binding failures seen with Vite 8 under Node 20.18.0.
12
+
13
+ Breaking change: `@wrelik/auth` and `@wrelik/clerk-next` now require `@clerk/nextjs@7.x` via updated peer ranges (`>=7 <8`). Consumers on Clerk v4-v6 must upgrade to Clerk v7 before adopting these versions.
14
+
15
+ - Updated dependencies [3262454]
16
+ - @wrelik/errors@2.0.1
17
+
18
+ ## 2.0.0
19
+
20
+ ### Major Changes
21
+
22
+ - 818ed58: Refactor runtime packages to strict subpath exports (`/server`, `/client`, `/shared`) with side-effect free entrypoints and hard CI/runtime boundary enforcement.
23
+
24
+ ### Minor Changes
25
+
26
+ - Add platform wrapper packages for Clerk (Next/Expo), Next Sentry client instrumentation, and Upstash server SDK access so apps can avoid direct vendor imports while staying inside the `@wrelik/*` boundary.
27
+
28
+ Add temporary deprecated server-side compatibility singleton exports to analytics, email, jobs, and storage to support DRX migration from root-import convenience APIs to runtime subpaths in one cutover.
29
+
30
+ ### Patch Changes
31
+
32
+ - Updated dependencies [818ed58]
33
+ - @wrelik/errors@2.0.0
34
+
3
35
  ## 0.2.1
4
36
 
5
37
  ### Patch Changes
@@ -1,14 +1,11 @@
1
- import * as node_buffer from 'node:buffer';
2
- export { v as validateUpload } from './shared-DqbBUqiW.js';
3
-
4
1
  declare function uploadToSignedUrl({ url, file, contentType, headers, }: {
5
2
  url: string;
6
3
  file: File | Blob;
7
4
  contentType: string;
8
5
  headers?: Record<string, string>;
9
- }): Promise<boolean>;
6
+ }): Promise<true>;
10
7
  declare function downloadFromSignedUrl({ url }: {
11
8
  url: string;
12
- }): Promise<node_buffer.Blob>;
9
+ }): Promise<Blob>;
13
10
 
14
11
  export { downloadFromSignedUrl, uploadToSignedUrl };
@@ -1,14 +1,11 @@
1
- import * as node_buffer from 'node:buffer';
2
- export { v as validateUpload } from './shared-DqbBUqiW.mjs';
3
-
4
1
  declare function uploadToSignedUrl({ url, file, contentType, headers, }: {
5
2
  url: string;
6
3
  file: File | Blob;
7
4
  contentType: string;
8
5
  headers?: Record<string, string>;
9
- }): Promise<boolean>;
6
+ }): Promise<true>;
10
7
  declare function downloadFromSignedUrl({ url }: {
11
8
  url: string;
12
- }): Promise<node_buffer.Blob>;
9
+ }): Promise<Blob>;
13
10
 
14
11
  export { downloadFromSignedUrl, uploadToSignedUrl };
@@ -17,28 +17,13 @@ var __copyProps = (to, from, except, desc) => {
17
17
  };
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
- // src/react-native.ts
21
- var react_native_exports = {};
22
- __export(react_native_exports, {
20
+ // src/client/index.ts
21
+ var client_exports = {};
22
+ __export(client_exports, {
23
23
  downloadFromSignedUrl: () => downloadFromSignedUrl,
24
- uploadToSignedUrl: () => uploadToSignedUrl,
25
- validateUpload: () => validateUpload
24
+ uploadToSignedUrl: () => uploadToSignedUrl
26
25
  });
27
- module.exports = __toCommonJS(react_native_exports);
28
-
29
- // src/shared.ts
30
- var import_errors = require("@wrelik/errors");
31
- function validateUpload(file, policy) {
32
- if (file.sizeBytes > policy.maxSizeBytes) {
33
- throw new import_errors.ValidationError(`File too large. Max ${policy.maxSizeBytes} bytes.`);
34
- }
35
- if (!policy.allowedTypes.includes(file.contentType)) {
36
- throw new import_errors.ValidationError(`Invalid file type ${file.contentType}`);
37
- }
38
- return true;
39
- }
40
-
41
- // src/react-native.ts
26
+ module.exports = __toCommonJS(client_exports);
42
27
  async function uploadToSignedUrl({
43
28
  url,
44
29
  file,
@@ -68,6 +53,5 @@ async function downloadFromSignedUrl({ url }) {
68
53
  // Annotate the CommonJS export names for ESM import in node:
69
54
  0 && (module.exports = {
70
55
  downloadFromSignedUrl,
71
- uploadToSignedUrl,
72
- validateUpload
56
+ uploadToSignedUrl
73
57
  });
@@ -1,8 +1,4 @@
1
- import {
2
- validateUpload
3
- } from "./chunk-6VKCOC6I.mjs";
4
-
5
- // src/react-native.ts
1
+ // src/client/index.ts
6
2
  async function uploadToSignedUrl({
7
3
  url,
8
4
  file,
@@ -31,6 +27,5 @@ async function downloadFromSignedUrl({ url }) {
31
27
  }
32
28
  export {
33
29
  downloadFromSignedUrl,
34
- uploadToSignedUrl,
35
- validateUpload
30
+ uploadToSignedUrl
36
31
  };
@@ -0,0 +1,39 @@
1
+ interface StorageConfig {
2
+ accountId: string;
3
+ accessKeyId: string;
4
+ secretAccessKey: string;
5
+ bucketName: string;
6
+ region?: string;
7
+ }
8
+ interface StorageServer {
9
+ putObject: (body: Buffer | Uint8Array | Blob | string, key: string, contentType: string, metadata?: Record<string, string>) => Promise<unknown>;
10
+ getSignedUploadUrl: (key: string, contentType: string, expiresIn?: number) => Promise<string>;
11
+ getSignedDownloadUrl: (key: string, expiresIn?: number) => Promise<string>;
12
+ }
13
+ interface StorageServerDependencies {
14
+ client?: {
15
+ send: (command: unknown) => Promise<unknown>;
16
+ };
17
+ signUrl?: (client: unknown, command: unknown, options: {
18
+ expiresIn: number;
19
+ }) => Promise<string>;
20
+ }
21
+ declare function createStorageServer(config: StorageConfig, deps?: StorageServerDependencies): StorageServer;
22
+ /**
23
+ * @deprecated Temporary DRX compatibility singleton. Prefer createStorageServer(...) and explicit dependency wiring.
24
+ */
25
+ declare function initStorage(config: StorageConfig, deps?: StorageServerDependencies): void;
26
+ /**
27
+ * @deprecated Temporary DRX compatibility singleton. Prefer createStorageServer(...).putObject(...)
28
+ */
29
+ declare function putObject(body: Parameters<StorageServer['putObject']>[0], key: string, contentType: string, metadata?: Record<string, string>): ReturnType<StorageServer['putObject']>;
30
+ /**
31
+ * @deprecated Temporary DRX compatibility singleton. Prefer createStorageServer(...).getSignedUploadUrl(...)
32
+ */
33
+ declare function getSignedUploadUrl(key: string, contentType: string, expiresIn?: number): ReturnType<StorageServer['getSignedUploadUrl']>;
34
+ /**
35
+ * @deprecated Temporary DRX compatibility singleton. Prefer createStorageServer(...).getSignedDownloadUrl(...)
36
+ */
37
+ declare function getSignedDownloadUrl(key: string, expiresIn?: number): ReturnType<StorageServer['getSignedDownloadUrl']>;
38
+
39
+ export { type StorageConfig, type StorageServer, type StorageServerDependencies, createStorageServer, getSignedDownloadUrl, getSignedUploadUrl, initStorage, putObject };
@@ -0,0 +1,39 @@
1
+ interface StorageConfig {
2
+ accountId: string;
3
+ accessKeyId: string;
4
+ secretAccessKey: string;
5
+ bucketName: string;
6
+ region?: string;
7
+ }
8
+ interface StorageServer {
9
+ putObject: (body: Buffer | Uint8Array | Blob | string, key: string, contentType: string, metadata?: Record<string, string>) => Promise<unknown>;
10
+ getSignedUploadUrl: (key: string, contentType: string, expiresIn?: number) => Promise<string>;
11
+ getSignedDownloadUrl: (key: string, expiresIn?: number) => Promise<string>;
12
+ }
13
+ interface StorageServerDependencies {
14
+ client?: {
15
+ send: (command: unknown) => Promise<unknown>;
16
+ };
17
+ signUrl?: (client: unknown, command: unknown, options: {
18
+ expiresIn: number;
19
+ }) => Promise<string>;
20
+ }
21
+ declare function createStorageServer(config: StorageConfig, deps?: StorageServerDependencies): StorageServer;
22
+ /**
23
+ * @deprecated Temporary DRX compatibility singleton. Prefer createStorageServer(...) and explicit dependency wiring.
24
+ */
25
+ declare function initStorage(config: StorageConfig, deps?: StorageServerDependencies): void;
26
+ /**
27
+ * @deprecated Temporary DRX compatibility singleton. Prefer createStorageServer(...).putObject(...)
28
+ */
29
+ declare function putObject(body: Parameters<StorageServer['putObject']>[0], key: string, contentType: string, metadata?: Record<string, string>): ReturnType<StorageServer['putObject']>;
30
+ /**
31
+ * @deprecated Temporary DRX compatibility singleton. Prefer createStorageServer(...).getSignedUploadUrl(...)
32
+ */
33
+ declare function getSignedUploadUrl(key: string, contentType: string, expiresIn?: number): ReturnType<StorageServer['getSignedUploadUrl']>;
34
+ /**
35
+ * @deprecated Temporary DRX compatibility singleton. Prefer createStorageServer(...).getSignedDownloadUrl(...)
36
+ */
37
+ declare function getSignedDownloadUrl(key: string, expiresIn?: number): ReturnType<StorageServer['getSignedDownloadUrl']>;
38
+
39
+ export { type StorageConfig, type StorageServer, type StorageServerDependencies, createStorageServer, getSignedDownloadUrl, getSignedUploadUrl, initStorage, putObject };
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/server/index.ts
21
+ var server_exports = {};
22
+ __export(server_exports, {
23
+ createStorageServer: () => createStorageServer,
24
+ getSignedDownloadUrl: () => getSignedDownloadUrl,
25
+ getSignedUploadUrl: () => getSignedUploadUrl,
26
+ initStorage: () => initStorage,
27
+ putObject: () => putObject
28
+ });
29
+ module.exports = __toCommonJS(server_exports);
30
+ var import_client_s3 = require("@aws-sdk/client-s3");
31
+ var import_s3_request_presigner = require("@aws-sdk/s3-request-presigner");
32
+ var import_shared = require("@wrelik/errors/shared");
33
+ function assertNonEmpty(value, field) {
34
+ if (!value?.trim()) {
35
+ throw new import_shared.ValidationError(`${field} is required`);
36
+ }
37
+ }
38
+ function assertExpiresIn(expiresIn) {
39
+ if (!Number.isInteger(expiresIn) || expiresIn <= 0) {
40
+ throw new import_shared.ValidationError("expiresIn must be a positive integer");
41
+ }
42
+ }
43
+ function createStorageServer(config, deps) {
44
+ const endpoint = `https://${config.accountId}.r2.cloudflarestorage.com`;
45
+ const client = deps?.client ?? new import_client_s3.S3Client({
46
+ region: config.region ?? "auto",
47
+ endpoint,
48
+ credentials: {
49
+ accessKeyId: config.accessKeyId,
50
+ secretAccessKey: config.secretAccessKey
51
+ }
52
+ });
53
+ const signUrl = deps?.signUrl ?? ((client2, command, options) => (0, import_s3_request_presigner.getSignedUrl)(client2, command, options));
54
+ return {
55
+ putObject(body, key, contentType, metadata) {
56
+ assertNonEmpty(key, "key");
57
+ assertNonEmpty(contentType, "contentType");
58
+ return client.send(
59
+ new import_client_s3.PutObjectCommand({
60
+ Bucket: config.bucketName,
61
+ Key: key,
62
+ Body: body,
63
+ ContentType: contentType,
64
+ Metadata: metadata
65
+ })
66
+ );
67
+ },
68
+ getSignedUploadUrl(key, contentType, expiresIn = 3600) {
69
+ assertNonEmpty(key, "key");
70
+ assertNonEmpty(contentType, "contentType");
71
+ assertExpiresIn(expiresIn);
72
+ const command = new import_client_s3.PutObjectCommand({
73
+ Bucket: config.bucketName,
74
+ Key: key,
75
+ ContentType: contentType
76
+ });
77
+ return signUrl(client, command, { expiresIn });
78
+ },
79
+ getSignedDownloadUrl(key, expiresIn = 3600) {
80
+ assertNonEmpty(key, "key");
81
+ assertExpiresIn(expiresIn);
82
+ const command = new import_client_s3.GetObjectCommand({
83
+ Bucket: config.bucketName,
84
+ Key: key
85
+ });
86
+ return signUrl(client, command, { expiresIn });
87
+ }
88
+ };
89
+ }
90
+ var storageSingleton = null;
91
+ function requireStorageSingleton() {
92
+ if (!storageSingleton) {
93
+ throw new Error(
94
+ "@wrelik/storage/server compatibility API is not initialized. Call initStorage(config) before putObject/getSignedUploadUrl/getSignedDownloadUrl."
95
+ );
96
+ }
97
+ return storageSingleton;
98
+ }
99
+ function initStorage(config, deps) {
100
+ storageSingleton = createStorageServer(config, deps);
101
+ }
102
+ function putObject(body, key, contentType, metadata) {
103
+ return requireStorageSingleton().putObject(body, key, contentType, metadata);
104
+ }
105
+ function getSignedUploadUrl(key, contentType, expiresIn) {
106
+ return requireStorageSingleton().getSignedUploadUrl(key, contentType, expiresIn);
107
+ }
108
+ function getSignedDownloadUrl(key, expiresIn) {
109
+ return requireStorageSingleton().getSignedDownloadUrl(key, expiresIn);
110
+ }
111
+ // Annotate the CommonJS export names for ESM import in node:
112
+ 0 && (module.exports = {
113
+ createStorageServer,
114
+ getSignedDownloadUrl,
115
+ getSignedUploadUrl,
116
+ initStorage,
117
+ putObject
118
+ });
@@ -0,0 +1,89 @@
1
+ // src/server/index.ts
2
+ import { GetObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
3
+ import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
4
+ import { ValidationError } from "@wrelik/errors/shared";
5
+ function assertNonEmpty(value, field) {
6
+ if (!value?.trim()) {
7
+ throw new ValidationError(`${field} is required`);
8
+ }
9
+ }
10
+ function assertExpiresIn(expiresIn) {
11
+ if (!Number.isInteger(expiresIn) || expiresIn <= 0) {
12
+ throw new ValidationError("expiresIn must be a positive integer");
13
+ }
14
+ }
15
+ function createStorageServer(config, deps) {
16
+ const endpoint = `https://${config.accountId}.r2.cloudflarestorage.com`;
17
+ const client = deps?.client ?? new S3Client({
18
+ region: config.region ?? "auto",
19
+ endpoint,
20
+ credentials: {
21
+ accessKeyId: config.accessKeyId,
22
+ secretAccessKey: config.secretAccessKey
23
+ }
24
+ });
25
+ const signUrl = deps?.signUrl ?? ((client2, command, options) => getSignedUrl(client2, command, options));
26
+ return {
27
+ putObject(body, key, contentType, metadata) {
28
+ assertNonEmpty(key, "key");
29
+ assertNonEmpty(contentType, "contentType");
30
+ return client.send(
31
+ new PutObjectCommand({
32
+ Bucket: config.bucketName,
33
+ Key: key,
34
+ Body: body,
35
+ ContentType: contentType,
36
+ Metadata: metadata
37
+ })
38
+ );
39
+ },
40
+ getSignedUploadUrl(key, contentType, expiresIn = 3600) {
41
+ assertNonEmpty(key, "key");
42
+ assertNonEmpty(contentType, "contentType");
43
+ assertExpiresIn(expiresIn);
44
+ const command = new PutObjectCommand({
45
+ Bucket: config.bucketName,
46
+ Key: key,
47
+ ContentType: contentType
48
+ });
49
+ return signUrl(client, command, { expiresIn });
50
+ },
51
+ getSignedDownloadUrl(key, expiresIn = 3600) {
52
+ assertNonEmpty(key, "key");
53
+ assertExpiresIn(expiresIn);
54
+ const command = new GetObjectCommand({
55
+ Bucket: config.bucketName,
56
+ Key: key
57
+ });
58
+ return signUrl(client, command, { expiresIn });
59
+ }
60
+ };
61
+ }
62
+ var storageSingleton = null;
63
+ function requireStorageSingleton() {
64
+ if (!storageSingleton) {
65
+ throw new Error(
66
+ "@wrelik/storage/server compatibility API is not initialized. Call initStorage(config) before putObject/getSignedUploadUrl/getSignedDownloadUrl."
67
+ );
68
+ }
69
+ return storageSingleton;
70
+ }
71
+ function initStorage(config, deps) {
72
+ storageSingleton = createStorageServer(config, deps);
73
+ }
74
+ function putObject(body, key, contentType, metadata) {
75
+ return requireStorageSingleton().putObject(body, key, contentType, metadata);
76
+ }
77
+ function getSignedUploadUrl(key, contentType, expiresIn) {
78
+ return requireStorageSingleton().getSignedUploadUrl(key, contentType, expiresIn);
79
+ }
80
+ function getSignedDownloadUrl(key, expiresIn) {
81
+ return requireStorageSingleton().getSignedDownloadUrl(key, expiresIn);
82
+ }
83
+ export {
84
+ createStorageServer,
85
+ getSignedDownloadUrl,
86
+ getSignedUploadUrl,
87
+ initStorage,
88
+ putObject
89
+ };
@@ -4,6 +4,6 @@ declare function validateUpload(file: {
4
4
  }, policy: {
5
5
  maxSizeBytes: number;
6
6
  allowedTypes: string[];
7
- }): boolean;
7
+ }): true;
8
8
 
9
- export { validateUpload as v };
9
+ export { validateUpload };
@@ -4,6 +4,6 @@ declare function validateUpload(file: {
4
4
  }, policy: {
5
5
  maxSizeBytes: number;
6
6
  allowedTypes: string[];
7
- }): boolean;
7
+ }): true;
8
8
 
9
- export { validateUpload as v };
9
+ export { validateUpload };
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/shared/index.ts
21
+ var shared_exports = {};
22
+ __export(shared_exports, {
23
+ validateUpload: () => validateUpload
24
+ });
25
+ module.exports = __toCommonJS(shared_exports);
26
+ var import_shared = require("@wrelik/errors/shared");
27
+ function validateUpload(file, policy) {
28
+ if (file.sizeBytes > policy.maxSizeBytes) {
29
+ throw new import_shared.ValidationError(`File too large. Max ${policy.maxSizeBytes} bytes.`);
30
+ }
31
+ if (!policy.allowedTypes.includes(file.contentType)) {
32
+ throw new import_shared.ValidationError(`Invalid file type ${file.contentType}`);
33
+ }
34
+ return true;
35
+ }
36
+ // Annotate the CommonJS export names for ESM import in node:
37
+ 0 && (module.exports = {
38
+ validateUpload
39
+ });
@@ -1,5 +1,5 @@
1
- // src/shared.ts
2
- import { ValidationError } from "@wrelik/errors";
1
+ // src/shared/index.ts
2
+ import { ValidationError } from "@wrelik/errors/shared";
3
3
  function validateUpload(file, policy) {
4
4
  if (file.sizeBytes > policy.maxSizeBytes) {
5
5
  throw new ValidationError(`File too large. Max ${policy.maxSizeBytes} bytes.`);
@@ -9,7 +9,6 @@ function validateUpload(file, policy) {
9
9
  }
10
10
  return true;
11
11
  }
12
-
13
12
  export {
14
13
  validateUpload
15
14
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrelik/storage",
3
- "version": "0.2.1",
3
+ "version": "2.0.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -9,25 +9,49 @@
9
9
  "url": "https://github.com/lwhite702/wrelik-kit.git",
10
10
  "directory": "packages/storage"
11
11
  },
12
+ "sideEffects": false,
12
13
  "exports": {
13
- ".": "./dist/index.js",
14
- "./react-native": "./dist/react-native.js"
14
+ "./server": {
15
+ "types": "./dist/server/index.d.ts",
16
+ "import": "./dist/server/index.mjs",
17
+ "require": "./dist/server/index.js"
18
+ },
19
+ "./client": {
20
+ "types": "./dist/client/index.d.ts",
21
+ "import": "./dist/client/index.mjs",
22
+ "require": "./dist/client/index.js"
23
+ },
24
+ "./shared": {
25
+ "types": "./dist/shared/index.d.ts",
26
+ "import": "./dist/shared/index.mjs",
27
+ "require": "./dist/shared/index.js"
28
+ },
29
+ "./package.json": "./package.json"
15
30
  },
16
31
  "dependencies": {
17
- "@aws-sdk/client-s3": "^3.500.0",
18
- "@aws-sdk/s3-request-presigner": "^3.500.0",
19
- "@wrelik/errors": "0.2.1"
32
+ "@aws-sdk/client-s3": "^3.1023.0",
33
+ "@aws-sdk/s3-request-presigner": "^3.1023.0",
34
+ "@wrelik/errors": "2.0.1"
20
35
  },
21
36
  "devDependencies": {
22
- "@types/node": "^25.2.0",
23
- "tsup": "^8.0.1",
24
- "vitest": "^1.2.2",
25
- "@wrelik/eslint-config": "0.1.1",
26
- "@wrelik/tsconfig": "0.1.1"
37
+ "@types/node": "^25.5.2",
38
+ "tsup": "^8.5.1",
39
+ "vite": "^7.3.1",
40
+ "vitest": "^4.1.2",
41
+ "@wrelik/tsconfig": "0.1.2",
42
+ "@wrelik/eslint-config": "0.1.2"
43
+ },
44
+ "wrelik": {
45
+ "runtimes": [
46
+ "server",
47
+ "client",
48
+ "shared"
49
+ ]
27
50
  },
28
51
  "scripts": {
29
- "build": "tsup src/index.ts src/react-native.ts --format cjs,esm --dts --clean",
52
+ "build": "tsup src/server/index.ts src/client/index.ts src/shared/index.ts --format cjs,esm --dts --clean",
30
53
  "lint": "eslint src/",
54
+ "typecheck": "tsc --noEmit",
31
55
  "test": "vitest run --passWithNoTests"
32
56
  }
33
57
  }
@@ -1,5 +1,3 @@
1
- export * from './shared';
2
-
3
1
  export async function uploadToSignedUrl({
4
2
  url,
5
3
  file,
@@ -10,7 +8,7 @@ export async function uploadToSignedUrl({
10
8
  file: File | Blob;
11
9
  contentType: string;
12
10
  headers?: Record<string, string>;
13
- }) {
11
+ }): Promise<true> {
14
12
  const result = await fetch(url, {
15
13
  method: 'PUT',
16
14
  body: file,
@@ -27,10 +25,11 @@ export async function uploadToSignedUrl({
27
25
  return true;
28
26
  }
29
27
 
30
- export async function downloadFromSignedUrl({ url }: { url: string }) {
28
+ export async function downloadFromSignedUrl({ url }: { url: string }): Promise<Blob> {
31
29
  const result = await fetch(url);
32
30
  if (!result.ok) {
33
31
  throw new Error(`Download failed with status ${result.status}`);
34
32
  }
33
+
35
34
  return result.blob();
36
35
  }
@@ -0,0 +1,71 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { ValidationError } from '@wrelik/errors/shared';
3
+ import { createStorageServer } from './index';
4
+
5
+ const config = {
6
+ accountId: 'acct',
7
+ accessKeyId: 'key',
8
+ secretAccessKey: 'secret',
9
+ bucketName: 'bucket',
10
+ };
11
+
12
+ afterEach(() => {
13
+ vi.resetModules();
14
+ vi.clearAllMocks();
15
+ });
16
+
17
+ describe('@wrelik/storage/server', () => {
18
+ it('generates signed URL shapes with configured signer', async () => {
19
+ const signUrl = vi.fn(async (_client, command, options: { expiresIn: number }) => {
20
+ const input = (command as { input: { Key: string } }).input;
21
+ return `https://signed.example/${input.Key}?expiresIn=${options.expiresIn}`;
22
+ });
23
+
24
+ const storage = createStorageServer(config, {
25
+ client: { send: vi.fn() },
26
+ signUrl,
27
+ });
28
+
29
+ await expect(storage.getSignedUploadUrl('uploads/a.png', 'image/png', 60)).resolves.toBe(
30
+ 'https://signed.example/uploads/a.png?expiresIn=60',
31
+ );
32
+ await expect(storage.getSignedDownloadUrl('uploads/a.png', 120)).resolves.toBe(
33
+ 'https://signed.example/uploads/a.png?expiresIn=120',
34
+ );
35
+ });
36
+
37
+ it('validates signed URL input rules', async () => {
38
+ const storage = createStorageServer(config, {
39
+ client: { send: vi.fn() },
40
+ signUrl: vi.fn(async () => 'https://signed.example/ok'),
41
+ });
42
+
43
+ expect(() => storage.getSignedUploadUrl('', 'image/png')).toThrow(ValidationError);
44
+ expect(() => storage.getSignedUploadUrl('a.png', '', 60)).toThrow(ValidationError);
45
+ expect(() => storage.getSignedDownloadUrl('a.png', 0)).toThrow(/positive integer/);
46
+ });
47
+
48
+ it('compatibility singleton throws before initStorage', async () => {
49
+ const mod = await import('./index.js');
50
+ expect(() => mod.getSignedDownloadUrl('uploads/a.png', 60)).toThrow(/initStorage/i);
51
+ });
52
+
53
+ it('compatibility singleton delegates after initStorage', async () => {
54
+ const send = vi.fn(async () => ({ etag: 'etag_1' }));
55
+ const signUrl = vi.fn(async (_client, command, options: { expiresIn: number }) => {
56
+ const input = (command as { input: { Key: string } }).input;
57
+ return `https://signed.example/${input.Key}?expiresIn=${options.expiresIn}`;
58
+ });
59
+ const mod = await import('./index.js');
60
+
61
+ mod.initStorage(config, { client: { send }, signUrl });
62
+
63
+ await mod.putObject('body', 'uploads/a.png', 'image/png');
64
+ await expect(mod.getSignedUploadUrl('uploads/a.png', 'image/png', 30)).resolves.toBe(
65
+ 'https://signed.example/uploads/a.png?expiresIn=30',
66
+ );
67
+
68
+ expect(send).toHaveBeenCalledTimes(1);
69
+ expect(signUrl).toHaveBeenCalledTimes(1);
70
+ });
71
+ });
@@ -0,0 +1,146 @@
1
+ import { GetObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
2
+ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
3
+ import { ValidationError } from '@wrelik/errors/shared';
4
+
5
+ export interface StorageConfig {
6
+ accountId: string;
7
+ accessKeyId: string;
8
+ secretAccessKey: string;
9
+ bucketName: string;
10
+ region?: string;
11
+ }
12
+
13
+ export interface StorageServer {
14
+ putObject: (
15
+ body: Buffer | Uint8Array | Blob | string,
16
+ key: string,
17
+ contentType: string,
18
+ metadata?: Record<string, string>,
19
+ ) => Promise<unknown>;
20
+ getSignedUploadUrl: (key: string, contentType: string, expiresIn?: number) => Promise<string>;
21
+ getSignedDownloadUrl: (key: string, expiresIn?: number) => Promise<string>;
22
+ }
23
+
24
+ export interface StorageServerDependencies {
25
+ client?: {
26
+ send: (command: unknown) => Promise<unknown>;
27
+ };
28
+ signUrl?: (client: unknown, command: unknown, options: { expiresIn: number }) => Promise<string>;
29
+ }
30
+
31
+ function assertNonEmpty(value: string, field: string): void {
32
+ if (!value?.trim()) {
33
+ throw new ValidationError(`${field} is required`);
34
+ }
35
+ }
36
+
37
+ function assertExpiresIn(expiresIn: number): void {
38
+ if (!Number.isInteger(expiresIn) || expiresIn <= 0) {
39
+ throw new ValidationError('expiresIn must be a positive integer');
40
+ }
41
+ }
42
+
43
+ export function createStorageServer(config: StorageConfig, deps?: StorageServerDependencies): StorageServer {
44
+ const endpoint = `https://${config.accountId}.r2.cloudflarestorage.com`;
45
+ const client =
46
+ deps?.client ??
47
+ new S3Client({
48
+ region: config.region ?? 'auto',
49
+ endpoint,
50
+ credentials: {
51
+ accessKeyId: config.accessKeyId,
52
+ secretAccessKey: config.secretAccessKey,
53
+ },
54
+ });
55
+ const signUrl =
56
+ deps?.signUrl ??
57
+ ((client: unknown, command: unknown, options: { expiresIn: number }) =>
58
+ getSignedUrl(client as S3Client, command as PutObjectCommand | GetObjectCommand, options));
59
+
60
+ return {
61
+ putObject(body, key, contentType, metadata) {
62
+ assertNonEmpty(key, 'key');
63
+ assertNonEmpty(contentType, 'contentType');
64
+ return client.send(
65
+ new PutObjectCommand({
66
+ Bucket: config.bucketName,
67
+ Key: key,
68
+ Body: body,
69
+ ContentType: contentType,
70
+ Metadata: metadata,
71
+ }),
72
+ );
73
+ },
74
+
75
+ getSignedUploadUrl(key, contentType, expiresIn = 3600) {
76
+ assertNonEmpty(key, 'key');
77
+ assertNonEmpty(contentType, 'contentType');
78
+ assertExpiresIn(expiresIn);
79
+ const command = new PutObjectCommand({
80
+ Bucket: config.bucketName,
81
+ Key: key,
82
+ ContentType: contentType,
83
+ });
84
+ return signUrl(client, command, { expiresIn });
85
+ },
86
+
87
+ getSignedDownloadUrl(key, expiresIn = 3600) {
88
+ assertNonEmpty(key, 'key');
89
+ assertExpiresIn(expiresIn);
90
+ const command = new GetObjectCommand({
91
+ Bucket: config.bucketName,
92
+ Key: key,
93
+ });
94
+ return signUrl(client, command, { expiresIn });
95
+ },
96
+ };
97
+ }
98
+
99
+ let storageSingleton: StorageServer | null = null;
100
+
101
+ function requireStorageSingleton(): StorageServer {
102
+ if (!storageSingleton) {
103
+ throw new Error(
104
+ '@wrelik/storage/server compatibility API is not initialized. Call initStorage(config) before putObject/getSignedUploadUrl/getSignedDownloadUrl.',
105
+ );
106
+ }
107
+
108
+ return storageSingleton;
109
+ }
110
+
111
+ /**
112
+ * @deprecated Temporary DRX compatibility singleton. Prefer createStorageServer(...) and explicit dependency wiring.
113
+ */
114
+ export function initStorage(config: StorageConfig, deps?: StorageServerDependencies): void {
115
+ storageSingleton = createStorageServer(config, deps);
116
+ }
117
+
118
+ /**
119
+ * @deprecated Temporary DRX compatibility singleton. Prefer createStorageServer(...).putObject(...)
120
+ */
121
+ export function putObject(
122
+ body: Parameters<StorageServer['putObject']>[0],
123
+ key: string,
124
+ contentType: string,
125
+ metadata?: Record<string, string>,
126
+ ): ReturnType<StorageServer['putObject']> {
127
+ return requireStorageSingleton().putObject(body, key, contentType, metadata);
128
+ }
129
+
130
+ /**
131
+ * @deprecated Temporary DRX compatibility singleton. Prefer createStorageServer(...).getSignedUploadUrl(...)
132
+ */
133
+ export function getSignedUploadUrl(
134
+ key: string,
135
+ contentType: string,
136
+ expiresIn?: number,
137
+ ): ReturnType<StorageServer['getSignedUploadUrl']> {
138
+ return requireStorageSingleton().getSignedUploadUrl(key, contentType, expiresIn);
139
+ }
140
+
141
+ /**
142
+ * @deprecated Temporary DRX compatibility singleton. Prefer createStorageServer(...).getSignedDownloadUrl(...)
143
+ */
144
+ export function getSignedDownloadUrl(key: string, expiresIn?: number): ReturnType<StorageServer['getSignedDownloadUrl']> {
145
+ return requireStorageSingleton().getSignedDownloadUrl(key, expiresIn);
146
+ }
@@ -1,12 +1,11 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { validateUpload } from './shared';
1
+ import { describe, expect, it } from 'vitest';
2
+ import { validateUpload } from './index';
3
3
 
4
- describe('Storage Shared', () => {
4
+ describe('@wrelik/storage/shared', () => {
5
5
  it('validates upload policy', () => {
6
6
  const policy = { maxSizeBytes: 100, allowedTypes: ['image/png'] };
7
7
 
8
8
  expect(() => validateUpload({ contentType: 'image/png', sizeBytes: 50 }, policy)).not.toThrow();
9
-
10
9
  expect(() => validateUpload({ contentType: 'image/jpeg', sizeBytes: 50 }, policy)).toThrow(
11
10
  /Invalid file type/,
12
11
  );
@@ -1,14 +1,16 @@
1
- import { ValidationError } from '@wrelik/errors';
1
+ import { ValidationError } from '@wrelik/errors/shared';
2
2
 
3
3
  export function validateUpload(
4
4
  file: { contentType: string; sizeBytes: number },
5
5
  policy: { maxSizeBytes: number; allowedTypes: string[] },
6
- ) {
6
+ ): true {
7
7
  if (file.sizeBytes > policy.maxSizeBytes) {
8
8
  throw new ValidationError(`File too large. Max ${policy.maxSizeBytes} bytes.`);
9
9
  }
10
+
10
11
  if (!policy.allowedTypes.includes(file.contentType)) {
11
12
  throw new ValidationError(`Invalid file type ${file.contentType}`);
12
13
  }
14
+
13
15
  return true;
14
16
  }
package/tsconfig.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "extends": "@wrelik/tsconfig/node.json",
3
3
  "compilerOptions": {
4
- "outDir": "dist", "skipLibCheck": true
4
+ "outDir": "dist",
5
+ "skipLibCheck": true,
6
+ "lib": ["DOM", "DOM.Iterable", "ES2022"],
7
+ "types": ["node"]
5
8
  },
6
9
  "include": ["src"]
7
10
  }
package/dist/index.d.mts DELETED
@@ -1,16 +0,0 @@
1
- import * as _aws_sdk_client_s3 from '@aws-sdk/client-s3';
2
- export { v as validateUpload } from './shared-DqbBUqiW.mjs';
3
-
4
- interface StorageConfig {
5
- accountId: string;
6
- accessKeyId: string;
7
- secretAccessKey: string;
8
- bucketName: string;
9
- region?: string;
10
- }
11
- declare function initStorage(config: StorageConfig): void;
12
- declare function putObject(body: Buffer | Uint8Array | Blob | string, key: string, contentType: string, metadata?: Record<string, string>): Promise<_aws_sdk_client_s3.PutObjectCommandOutput>;
13
- declare function getSignedUploadUrl(key: string, contentType: string, expiresIn?: number): Promise<string>;
14
- declare function getSignedDownloadUrl(key: string, expiresIn?: number): Promise<string>;
15
-
16
- export { type StorageConfig, getSignedDownloadUrl, getSignedUploadUrl, initStorage, putObject };
package/dist/index.d.ts DELETED
@@ -1,16 +0,0 @@
1
- import * as _aws_sdk_client_s3 from '@aws-sdk/client-s3';
2
- export { v as validateUpload } from './shared-DqbBUqiW.js';
3
-
4
- interface StorageConfig {
5
- accountId: string;
6
- accessKeyId: string;
7
- secretAccessKey: string;
8
- bucketName: string;
9
- region?: string;
10
- }
11
- declare function initStorage(config: StorageConfig): void;
12
- declare function putObject(body: Buffer | Uint8Array | Blob | string, key: string, contentType: string, metadata?: Record<string, string>): Promise<_aws_sdk_client_s3.PutObjectCommandOutput>;
13
- declare function getSignedUploadUrl(key: string, contentType: string, expiresIn?: number): Promise<string>;
14
- declare function getSignedDownloadUrl(key: string, expiresIn?: number): Promise<string>;
15
-
16
- export { type StorageConfig, getSignedDownloadUrl, getSignedUploadUrl, initStorage, putObject };
package/dist/index.js DELETED
@@ -1,96 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- getSignedDownloadUrl: () => getSignedDownloadUrl,
24
- getSignedUploadUrl: () => getSignedUploadUrl,
25
- initStorage: () => initStorage,
26
- putObject: () => putObject,
27
- validateUpload: () => validateUpload
28
- });
29
- module.exports = __toCommonJS(index_exports);
30
- var import_client_s3 = require("@aws-sdk/client-s3");
31
- var import_s3_request_presigner = require("@aws-sdk/s3-request-presigner");
32
-
33
- // src/shared.ts
34
- var import_errors = require("@wrelik/errors");
35
- function validateUpload(file, policy) {
36
- if (file.sizeBytes > policy.maxSizeBytes) {
37
- throw new import_errors.ValidationError(`File too large. Max ${policy.maxSizeBytes} bytes.`);
38
- }
39
- if (!policy.allowedTypes.includes(file.contentType)) {
40
- throw new import_errors.ValidationError(`Invalid file type ${file.contentType}`);
41
- }
42
- return true;
43
- }
44
-
45
- // src/index.ts
46
- var client;
47
- var bucket;
48
- function initStorage(config) {
49
- const endpoint = `https://${config.accountId}.r2.cloudflarestorage.com`;
50
- client = new import_client_s3.S3Client({
51
- region: config.region || "auto",
52
- endpoint,
53
- credentials: {
54
- accessKeyId: config.accessKeyId,
55
- secretAccessKey: config.secretAccessKey
56
- }
57
- });
58
- bucket = config.bucketName;
59
- }
60
- function getClient() {
61
- if (!client) throw new Error("Storage not initialized. Call initStorage first.");
62
- return client;
63
- }
64
- async function putObject(body, key, contentType, metadata) {
65
- const command = new import_client_s3.PutObjectCommand({
66
- Bucket: bucket,
67
- Key: key,
68
- Body: body,
69
- ContentType: contentType,
70
- Metadata: metadata
71
- });
72
- return getClient().send(command);
73
- }
74
- async function getSignedUploadUrl(key, contentType, expiresIn = 3600) {
75
- const command = new import_client_s3.PutObjectCommand({
76
- Bucket: bucket,
77
- Key: key,
78
- ContentType: contentType
79
- });
80
- return (0, import_s3_request_presigner.getSignedUrl)(getClient(), command, { expiresIn });
81
- }
82
- async function getSignedDownloadUrl(key, expiresIn = 3600) {
83
- const command = new import_client_s3.GetObjectCommand({
84
- Bucket: bucket,
85
- Key: key
86
- });
87
- return (0, import_s3_request_presigner.getSignedUrl)(getClient(), command, { expiresIn });
88
- }
89
- // Annotate the CommonJS export names for ESM import in node:
90
- 0 && (module.exports = {
91
- getSignedDownloadUrl,
92
- getSignedUploadUrl,
93
- initStorage,
94
- putObject,
95
- validateUpload
96
- });
package/dist/index.mjs DELETED
@@ -1,57 +0,0 @@
1
- import {
2
- validateUpload
3
- } from "./chunk-6VKCOC6I.mjs";
4
-
5
- // src/index.ts
6
- import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
7
- import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
8
- var client;
9
- var bucket;
10
- function initStorage(config) {
11
- const endpoint = `https://${config.accountId}.r2.cloudflarestorage.com`;
12
- client = new S3Client({
13
- region: config.region || "auto",
14
- endpoint,
15
- credentials: {
16
- accessKeyId: config.accessKeyId,
17
- secretAccessKey: config.secretAccessKey
18
- }
19
- });
20
- bucket = config.bucketName;
21
- }
22
- function getClient() {
23
- if (!client) throw new Error("Storage not initialized. Call initStorage first.");
24
- return client;
25
- }
26
- async function putObject(body, key, contentType, metadata) {
27
- const command = new PutObjectCommand({
28
- Bucket: bucket,
29
- Key: key,
30
- Body: body,
31
- ContentType: contentType,
32
- Metadata: metadata
33
- });
34
- return getClient().send(command);
35
- }
36
- async function getSignedUploadUrl(key, contentType, expiresIn = 3600) {
37
- const command = new PutObjectCommand({
38
- Bucket: bucket,
39
- Key: key,
40
- ContentType: contentType
41
- });
42
- return getSignedUrl(getClient(), command, { expiresIn });
43
- }
44
- async function getSignedDownloadUrl(key, expiresIn = 3600) {
45
- const command = new GetObjectCommand({
46
- Bucket: bucket,
47
- Key: key
48
- });
49
- return getSignedUrl(getClient(), command, { expiresIn });
50
- }
51
- export {
52
- getSignedDownloadUrl,
53
- getSignedUploadUrl,
54
- initStorage,
55
- putObject,
56
- validateUpload
57
- };
package/src/index.ts DELETED
@@ -1,73 +0,0 @@
1
- /* eslint-disable no-restricted-imports */
2
- import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
3
- import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
4
-
5
- export interface StorageConfig {
6
- accountId: string;
7
- accessKeyId: string;
8
- secretAccessKey: string;
9
- bucketName: string;
10
- region?: string;
11
- }
12
-
13
- let client: S3Client;
14
- let bucket: string;
15
-
16
- export function initStorage(config: StorageConfig) {
17
- // For R2, endpoint: https://<accountid>.r2.cloudflarestorage.com
18
- const endpoint = `https://${config.accountId}.r2.cloudflarestorage.com`;
19
-
20
- client = new S3Client({
21
- region: config.region || 'auto',
22
- endpoint,
23
- credentials: {
24
- accessKeyId: config.accessKeyId,
25
- secretAccessKey: config.secretAccessKey,
26
- },
27
- });
28
-
29
- bucket = config.bucketName;
30
- }
31
-
32
- function getClient() {
33
- if (!client) throw new Error('Storage not initialized. Call initStorage first.');
34
- return client;
35
- }
36
-
37
- export async function putObject(
38
- body: Buffer | Uint8Array | Blob | string,
39
- key: string,
40
- contentType: string,
41
- metadata?: Record<string, string>,
42
- ) {
43
- const command = new PutObjectCommand({
44
- Bucket: bucket,
45
- Key: key,
46
- Body: body,
47
- ContentType: contentType,
48
- Metadata: metadata,
49
- });
50
-
51
- return getClient().send(command);
52
- }
53
-
54
- export async function getSignedUploadUrl(key: string, contentType: string, expiresIn = 3600) {
55
- const command = new PutObjectCommand({
56
- Bucket: bucket,
57
- Key: key,
58
- ContentType: contentType,
59
- });
60
-
61
- return getSignedUrl(getClient(), command, { expiresIn });
62
- }
63
-
64
- export async function getSignedDownloadUrl(key: string, expiresIn = 3600) {
65
- const command = new GetObjectCommand({
66
- Bucket: bucket,
67
- Key: key,
68
- });
69
-
70
- return getSignedUrl(getClient(), command, { expiresIn });
71
- }
72
-
73
- export * from './shared';