@arcote.tech/arc-files 0.8.2 → 0.8.4

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 (2) hide show
  1. package/package.json +4 -4
  2. package/src/s3/s3-client.ts +30 -13
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@arcote.tech/arc-files",
3
3
  "type": "module",
4
- "version": "0.8.2",
4
+ "version": "0.8.4",
5
5
  "private": false,
6
6
  "description": "Generic file upload fragment for Arc framework — S3 presigned upload + provider file_id binding",
7
7
  "main": "./src/index.ts",
@@ -10,9 +10,9 @@
10
10
  "type-check": "tsc --noEmit"
11
11
  },
12
12
  "peerDependencies": {
13
- "@arcote.tech/arc": "^0.8.2",
14
- "@arcote.tech/arc-ds": "^0.8.2",
15
- "@arcote.tech/platform": "^0.8.2",
13
+ "@arcote.tech/arc": "^0.8.4",
14
+ "@arcote.tech/arc-ds": "^0.8.4",
15
+ "@arcote.tech/platform": "^0.8.4",
16
16
  "@aws-sdk/client-s3": "^3.658.0",
17
17
  "@aws-sdk/s3-request-presigner": "^3.658.0",
18
18
  "lucide-react": ">=0.400.0",
@@ -14,9 +14,13 @@ import type { S3Config } from "../types";
14
14
  * parsing 10 MB SDK. Pierwsze użycie ma ~100-200 ms overhead lazy load
15
15
  * (acceptable dla async S3 operation).
16
16
  *
17
- * Cały moduł guard'owany przez `ONLY_SERVER` w browser bundle bundler
18
- * tree-shake'uje konstruktor (nigdy nie istnieje), więc browser nie widzi
19
- * aws-sdk w ogóle.
17
+ * KAŻDY `import("@aws-sdk/...")` MUSI być owinięty w `ONLY_SERVER ? : null`
18
+ * (helpery #s3 / #presigner niżej). Sam `if (!ONLY_SERVER) throw` NIE
19
+ * wystarcza — bundler nie robi analizy przepływu, więc gołe `import(...)` w
20
+ * ciele metody zostaje w grafie i (bez splittingu w buildContextPackages)
21
+ * ląduje INLINE w dist/browser konsumenta. W NDT dało to ~840 KB AWS SDK +
22
+ * ~600 KB polyfilli node:crypto/stream w KAŻDYM wejściu. Z guardem `define
23
+ * ONLY_SERVER=false` składa martwą gałąź do `null` → aws-sdk znika z klienta.
20
24
  */
21
25
  export class S3Bridge {
22
26
  readonly bucket: string;
@@ -28,17 +32,30 @@ export class S3Bridge {
28
32
  this.#config = config;
29
33
  }
30
34
 
35
+ /** Guarded loader — na przeglądarce (ONLY_SERVER=false) cała gałąź importu
36
+ * jest martwa i wypada z bundla; na serwerze ładuje aws-sdk lazy. */
37
+ async #s3() {
38
+ const mod = ONLY_SERVER ? await import("@aws-sdk/client-s3") : null;
39
+ if (!mod) throw new Error("S3Bridge not available in browser context");
40
+ return mod;
41
+ }
42
+
43
+ async #presigner() {
44
+ const mod = ONLY_SERVER
45
+ ? await import("@aws-sdk/s3-request-presigner")
46
+ : null;
47
+ if (!mod) throw new Error("S3Bridge not available in browser context");
48
+ return mod;
49
+ }
50
+
31
51
  /**
32
52
  * Lazy client construction. Pierwszy call odpala dynamic import aws-sdk
33
53
  * + `new S3Client(...)`; kolejne calle reużywają cached Promise.
34
54
  */
35
55
  async #client(): Promise<S3ClientLike> {
36
- if (!ONLY_SERVER) {
37
- throw new Error("S3Bridge not available in browser context");
38
- }
39
56
  if (!this.#clientPromise) {
40
57
  this.#clientPromise = (async () => {
41
- const { S3Client } = await import("@aws-sdk/client-s3");
58
+ const { S3Client } = await this.#s3();
42
59
  return new S3Client({
43
60
  region: this.#config.region,
44
61
  endpoint: this.#config.endpoint,
@@ -57,8 +74,8 @@ export class S3Bridge {
57
74
  async presignUpload(key: string, mime: string, expiresIn = 300) {
58
75
  const [client, { PutObjectCommand }, { getSignedUrl }] = await Promise.all([
59
76
  this.#client(),
60
- import("@aws-sdk/client-s3"),
61
- import("@aws-sdk/s3-request-presigner"),
77
+ this.#s3(),
78
+ this.#presigner(),
62
79
  ]);
63
80
  return getSignedUrl(
64
81
  client as any,
@@ -74,8 +91,8 @@ export class S3Bridge {
74
91
  async presignDownload(key: string, expiresIn = 3600) {
75
92
  const [client, { GetObjectCommand }, { getSignedUrl }] = await Promise.all([
76
93
  this.#client(),
77
- import("@aws-sdk/client-s3"),
78
- import("@aws-sdk/s3-request-presigner"),
94
+ this.#s3(),
95
+ this.#presigner(),
79
96
  ]);
80
97
  return getSignedUrl(
81
98
  client as any,
@@ -87,7 +104,7 @@ export class S3Bridge {
87
104
  async downloadObject(key: string): Promise<Buffer> {
88
105
  const [client, { GetObjectCommand }] = await Promise.all([
89
106
  this.#client(),
90
- import("@aws-sdk/client-s3"),
107
+ this.#s3(),
91
108
  ]);
92
109
  const out = await client.send(
93
110
  new GetObjectCommand({ Bucket: this.bucket, Key: key }),
@@ -101,7 +118,7 @@ export class S3Bridge {
101
118
  async deleteObject(key: string): Promise<void> {
102
119
  const [client, { DeleteObjectCommand }] = await Promise.all([
103
120
  this.#client(),
104
- import("@aws-sdk/client-s3"),
121
+ this.#s3(),
105
122
  ]);
106
123
  await client.send(
107
124
  new DeleteObjectCommand({ Bucket: this.bucket, Key: key }),