uploaderkit 1.0.0 → 2.0.0

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 (86) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +16 -0
  3. package/README.es.md +1140 -0
  4. package/README.md +1135 -0
  5. package/dist/FileViewer-ChTf7-uY.d.cts +49 -0
  6. package/dist/FileViewer-nuD-GEdJ.d.ts +49 -0
  7. package/dist/adapters/gcs.cjs +83 -0
  8. package/dist/adapters/gcs.cjs.map +1 -0
  9. package/dist/adapters/gcs.d.cts +31 -0
  10. package/dist/adapters/gcs.d.ts +31 -0
  11. package/dist/adapters/gcs.js +81 -0
  12. package/dist/adapters/gcs.js.map +1 -0
  13. package/dist/adapters/memory.cjs +27 -0
  14. package/dist/adapters/memory.cjs.map +1 -0
  15. package/dist/adapters/memory.d.cts +18 -0
  16. package/dist/adapters/memory.d.ts +18 -0
  17. package/dist/adapters/memory.js +25 -0
  18. package/dist/adapters/memory.js.map +1 -0
  19. package/dist/adapters/s3.cjs +75 -0
  20. package/dist/adapters/s3.cjs.map +1 -0
  21. package/dist/adapters/s3.d.cts +27 -0
  22. package/dist/adapters/s3.d.ts +27 -0
  23. package/dist/adapters/s3.js +73 -0
  24. package/dist/adapters/s3.js.map +1 -0
  25. package/dist/chunk-3FI44IOW.js +150 -0
  26. package/dist/chunk-3FI44IOW.js.map +1 -0
  27. package/dist/chunk-H7BRW5IY.js +308 -0
  28. package/dist/chunk-H7BRW5IY.js.map +1 -0
  29. package/dist/chunk-PDKAF4GX.js +707 -0
  30. package/dist/chunk-PDKAF4GX.js.map +1 -0
  31. package/dist/chunk-PTEX7F4R.js +309 -0
  32. package/dist/chunk-PTEX7F4R.js.map +1 -0
  33. package/dist/chunk-T5YZG6JW.js +511 -0
  34. package/dist/chunk-T5YZG6JW.js.map +1 -0
  35. package/dist/index.cjs +220 -27
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +57 -170
  38. package/dist/index.d.ts +57 -170
  39. package/dist/index.js +1 -324
  40. package/dist/index.js.map +1 -1
  41. package/dist/presets.cjs +1665 -0
  42. package/dist/presets.cjs.map +1 -0
  43. package/dist/presets.d.cts +100 -0
  44. package/dist/presets.d.ts +100 -0
  45. package/dist/presets.js +405 -0
  46. package/dist/presets.js.map +1 -0
  47. package/dist/react.cjs +981 -0
  48. package/dist/react.cjs.map +1 -0
  49. package/dist/react.d.cts +106 -0
  50. package/dist/react.d.ts +106 -0
  51. package/dist/react.js +5 -0
  52. package/dist/react.js.map +1 -0
  53. package/dist/server/express.cjs +179 -0
  54. package/dist/server/express.cjs.map +1 -0
  55. package/dist/server/express.d.cts +64 -0
  56. package/dist/server/express.d.ts +64 -0
  57. package/dist/server/express.js +111 -0
  58. package/dist/server/express.js.map +1 -0
  59. package/dist/server/next.cjs +165 -0
  60. package/dist/server/next.cjs.map +1 -0
  61. package/dist/server/next.d.cts +38 -0
  62. package/dist/server/next.d.ts +38 -0
  63. package/dist/server/next.js +107 -0
  64. package/dist/server/next.js.map +1 -0
  65. package/dist/server.cjs +550 -0
  66. package/dist/server.cjs.map +1 -0
  67. package/dist/server.d.cts +19 -0
  68. package/dist/server.d.ts +19 -0
  69. package/dist/server.js +80 -0
  70. package/dist/server.js.map +1 -0
  71. package/dist/storage-CYkSHWZX.d.cts +133 -0
  72. package/dist/storage-Qc9epG0G.d.ts +133 -0
  73. package/dist/types-BSlJJwti.d.cts +341 -0
  74. package/dist/types-BSlJJwti.d.ts +341 -0
  75. package/dist/ui.cjs +2325 -0
  76. package/dist/ui.cjs.map +1 -0
  77. package/dist/ui.d.cts +331 -0
  78. package/dist/ui.d.ts +331 -0
  79. package/dist/ui.js +749 -0
  80. package/dist/ui.js.map +1 -0
  81. package/dist/useSlottedUploader-BzT5jV8c.d.cts +139 -0
  82. package/dist/useSlottedUploader-IkG5RhEO.d.ts +139 -0
  83. package/dist/useUploader-BiBdS-7y.d.cts +117 -0
  84. package/dist/useUploader-CQHpj_oI.d.ts +117 -0
  85. package/package.json +189 -3
  86. package/tailwind.css +80 -0
@@ -0,0 +1,117 @@
1
+ import { o as StoredFile, p as UploadStatus, d as ScopeConfig, e as ScopeRegistry, U as UploaderLabels } from './types-BSlJJwti.js';
2
+
3
+ /** Signals the strategy receives while a file is in flight. */
4
+ type UploadStrategyOptions = {
5
+ /** 0–100. Driven by the transport when it can measure (XHR can). */
6
+ onProgress: (percent: number) => void;
7
+ /** Aborting must reject the promise with an `AbortError`-named error. */
8
+ signal: AbortSignal;
9
+ };
10
+ /**
11
+ * Deletes one persisted object from storage. The uploaders run it themselves
12
+ * on a confirmed removal — unless the scope is marked `keepOnRemove`, the
13
+ * history contract — so a consumer that wires a strategy can no longer orphan
14
+ * by forgetting a callback. Resolve `false` when the server refused.
15
+ */
16
+ type RemoveStrategy = (stored: StoredFile, scope: string, entityId: string) => Promise<boolean>;
17
+ /**
18
+ * The physical transport for one file. Injected into `useUploader` so the hook
19
+ * owns state and validation while the app owns how bytes travel — swap the
20
+ * endpoint, the auth header or the whole protocol without touching the hook.
21
+ */
22
+ type UploadStrategy = (file: File, scope: string, entityId: string, options: UploadStrategyOptions) => Promise<StoredFile>;
23
+ /** One file tracked by the hook, from selection to stored (or failed). */
24
+ type UploaderFile = {
25
+ id: string;
26
+ file: File;
27
+ status: UploadStatus;
28
+ /** 0–100 while uploading; 100 on success. */
29
+ progress: number;
30
+ /** Human, Spanish — comes from validation or the strategy's failure. */
31
+ error?: string;
32
+ /** Object URL for image files, for a local thumbnail before upload. */
33
+ preview?: string;
34
+ /** Present once the server confirmed the upload. */
35
+ stored?: StoredFile;
36
+ };
37
+
38
+ /**
39
+ * When the machine sends: `'manual'` holds files in `idle` until the app calls
40
+ * `upload()` — the form-submit flow, where the file travels together with the
41
+ * rest of the fields' action. `'select'` fires as soon as a valid file lands.
42
+ */
43
+ type UploadTrigger = 'select' | 'manual';
44
+ type RetryOptions = {
45
+ /** Total attempts per file, the first one included. @defaultValue 1 */
46
+ attempts?: number;
47
+ /** Delay before the first retry; doubles on each further one. @defaultValue 500 */
48
+ backoffMs?: number;
49
+ };
50
+ type UseUploaderOptions<T extends Record<string, ScopeConfig>> = {
51
+ /** The app's registry — the same object the server authorizes against. */
52
+ scopes: ScopeRegistry<T>;
53
+ scope: keyof T & string;
54
+ /** Owner of the uploads (customerId, userId, …), forwarded to the strategy. */
55
+ entityId: string;
56
+ /** Transport. Omit for local-only selection + validation (no `upload`). */
57
+ strategy?: UploadStrategy;
58
+ /**
59
+ * Overrides the arity derived from the scope's `maxFiles`. Rarely needed:
60
+ * the registry is what the server validates against.
61
+ */
62
+ multiple?: boolean;
63
+ /** Overrides the scope's own `maxFiles`, downward. */
64
+ maxFiles?: number;
65
+ /** @defaultValue 'manual' — the styled `Uploader` flips it to `'select'`. */
66
+ uploadOn?: UploadTrigger;
67
+ /**
68
+ * Fired when a batch actually starts travelling — from either trigger — with
69
+ * the files it carries. The place to flip a form into its "sending" state.
70
+ */
71
+ onUploadStart?: (files: UploaderFile[]) => void;
72
+ /** Fired once per batch with the files the server confirmed. */
73
+ onUploaded?: (stored: StoredFile[]) => void;
74
+ onError?: (message: string) => void;
75
+ /**
76
+ * Re-run a failed strategy call before surfacing the error. A number is
77
+ * shorthand for `{ attempts }`. Aborts never retry, and validation failures
78
+ * never reach the strategy in the first place.
79
+ */
80
+ retry?: number | RetryOptions;
81
+ /** Max uploads in flight per batch; the rest queue. @defaultValue Infinity */
82
+ concurrency?: number;
83
+ /**
84
+ * Rename each file before it enters the machine — a client-side input, a
85
+ * folio, a slug. Runs before validation, so a rename that changes the
86
+ * extension is rejected like any other invalid file. The stored key comes
87
+ * from the scope's `path(entityId, file)`, which reads this name.
88
+ */
89
+ rename?: (file: File) => string;
90
+ labels?: Partial<UploaderLabels>;
91
+ };
92
+ type UseUploaderReturn = {
93
+ files: UploaderFile[];
94
+ /** Ready-made value for `<input accept>`, derived from the scope. */
95
+ accept: string;
96
+ isUploading: boolean;
97
+ /** `true` while any file waits in `idle` — a manual trigger has work to do. */
98
+ hasPending: boolean;
99
+ addFiles: (incoming: FileList | File[]) => Promise<void>;
100
+ /** Uploads every file still in `idle`. Resolves with the confirmed ones. */
101
+ upload: () => Promise<StoredFile[]>;
102
+ /** Abort one in-flight upload, or all of them when no id is given. */
103
+ abort: (id?: string) => void;
104
+ removeFile: (id: string) => void;
105
+ clear: () => void;
106
+ };
107
+ /**
108
+ * Headless upload state machine: selection → validation → (compression) →
109
+ * upload with progress and abort. Owns no markup — render `files` however the
110
+ * screen needs and wire `addFiles` to an input or a drop zone.
111
+ *
112
+ * Validation runs here with the same `validateForScope` the server runs, so
113
+ * the user sees the rejection before any byte leaves the machine.
114
+ */
115
+ declare const useUploader: <T extends Record<string, ScopeConfig>>({ scopes, scope, entityId, strategy, multiple: multipleOption, maxFiles: maxFilesOption, uploadOn, onUploadStart, onUploaded, onError, retry, concurrency, rename, labels, }: UseUploaderOptions<T>) => UseUploaderReturn;
116
+
117
+ export { type RemoveStrategy as R, type UseUploaderOptions as U, type UploadStrategy as a, type RetryOptions as b, type UploadStrategyOptions as c, type UploadTrigger as d, type UploaderFile as e, type UseUploaderReturn as f, useUploader as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uploaderkit",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "One upload contract for every storage provider — scope registry, isomorphic validation, headless React and a server router.",
5
5
  "keywords": [
6
6
  "upload",
@@ -35,7 +35,9 @@
35
35
  },
36
36
  "files": [
37
37
  "dist",
38
+ "tailwind.css",
38
39
  "README.md",
40
+ "README.es.md",
39
41
  "LICENSE",
40
42
  "NOTICE"
41
43
  ],
@@ -53,6 +55,97 @@
53
55
  "default": "./dist/index.cjs"
54
56
  }
55
57
  },
58
+ "./react": {
59
+ "import": {
60
+ "types": "./dist/react.d.ts",
61
+ "default": "./dist/react.js"
62
+ },
63
+ "require": {
64
+ "types": "./dist/react.d.cts",
65
+ "default": "./dist/react.cjs"
66
+ }
67
+ },
68
+ "./server": {
69
+ "import": {
70
+ "types": "./dist/server.d.ts",
71
+ "default": "./dist/server.js"
72
+ },
73
+ "require": {
74
+ "types": "./dist/server.d.cts",
75
+ "default": "./dist/server.cjs"
76
+ }
77
+ },
78
+ "./server/express": {
79
+ "import": {
80
+ "types": "./dist/server/express.d.ts",
81
+ "default": "./dist/server/express.js"
82
+ },
83
+ "require": {
84
+ "types": "./dist/server/express.d.cts",
85
+ "default": "./dist/server/express.cjs"
86
+ }
87
+ },
88
+ "./server/next": {
89
+ "import": {
90
+ "types": "./dist/server/next.d.ts",
91
+ "default": "./dist/server/next.js"
92
+ },
93
+ "require": {
94
+ "types": "./dist/server/next.d.cts",
95
+ "default": "./dist/server/next.cjs"
96
+ }
97
+ },
98
+ "./adapters/memory": {
99
+ "import": {
100
+ "types": "./dist/adapters/memory.d.ts",
101
+ "default": "./dist/adapters/memory.js"
102
+ },
103
+ "require": {
104
+ "types": "./dist/adapters/memory.d.cts",
105
+ "default": "./dist/adapters/memory.cjs"
106
+ }
107
+ },
108
+ "./adapters/gcs": {
109
+ "import": {
110
+ "types": "./dist/adapters/gcs.d.ts",
111
+ "default": "./dist/adapters/gcs.js"
112
+ },
113
+ "require": {
114
+ "types": "./dist/adapters/gcs.d.cts",
115
+ "default": "./dist/adapters/gcs.cjs"
116
+ }
117
+ },
118
+ "./ui": {
119
+ "import": {
120
+ "types": "./dist/ui.d.ts",
121
+ "default": "./dist/ui.js"
122
+ },
123
+ "require": {
124
+ "types": "./dist/ui.d.cts",
125
+ "default": "./dist/ui.cjs"
126
+ }
127
+ },
128
+ "./presets": {
129
+ "import": {
130
+ "types": "./dist/presets.d.ts",
131
+ "default": "./dist/presets.js"
132
+ },
133
+ "require": {
134
+ "types": "./dist/presets.d.cts",
135
+ "default": "./dist/presets.cjs"
136
+ }
137
+ },
138
+ "./adapters/s3": {
139
+ "import": {
140
+ "types": "./dist/adapters/s3.d.ts",
141
+ "default": "./dist/adapters/s3.js"
142
+ },
143
+ "require": {
144
+ "types": "./dist/adapters/s3.d.cts",
145
+ "default": "./dist/adapters/s3.cjs"
146
+ }
147
+ },
148
+ "./tailwind.css": "./tailwind.css",
56
149
  "./package.json": "./package.json"
57
150
  },
58
151
  "scripts": {
@@ -65,18 +158,111 @@
65
158
  "size": "size-limit",
66
159
  "size:why": "size-limit --why",
67
160
  "check:publish": "publint",
68
- "docs": "typedoc"
161
+ "docs": "typedoc",
162
+ "check:types": "attw --pack . --exclude-entrypoints ./tailwind.css"
69
163
  },
70
164
  "size-limit": [
71
165
  {
72
166
  "name": "core (index)",
73
167
  "path": "dist/index.js",
74
168
  "limit": "6 kB"
169
+ },
170
+ {
171
+ "name": "react",
172
+ "path": "dist/react.js",
173
+ "limit": "9 kB",
174
+ "ignore": [
175
+ "react",
176
+ "react-dom"
177
+ ]
178
+ },
179
+ {
180
+ "name": "ui",
181
+ "path": "dist/ui.js",
182
+ "limit": "17 kB",
183
+ "ignore": [
184
+ "react",
185
+ "react-dom"
186
+ ]
187
+ },
188
+ {
189
+ "name": "presets",
190
+ "path": "dist/presets.js",
191
+ "limit": "14 kB",
192
+ "ignore": [
193
+ "react",
194
+ "react-dom"
195
+ ]
75
196
  }
76
197
  ],
77
198
  "devDependencies": {
199
+ "@aws-sdk/client-s3": "^3.1109.0",
200
+ "@aws-sdk/s3-request-presigner": "^3.1109.0",
201
+ "@google-cloud/storage": "^7.22.0",
202
+ "@testing-library/react": "^16.3.2",
203
+ "@types/node": "^26.2.0",
204
+ "@types/react": "^18.3.12",
205
+ "happy-dom": "^20.11.2",
206
+ "react": "^18.3.1",
207
+ "react-dom": "^18.3.1",
78
208
  "tsup": "^8.5.0",
79
209
  "typescript": "^5.9.3",
80
- "vitest": "^3.2.4"
210
+ "vitest": "^3.2.4",
211
+ "@types/react-dom": "^18.3.1"
212
+ },
213
+ "typesVersions": {
214
+ "*": {
215
+ "react": [
216
+ "./dist/react.d.ts"
217
+ ],
218
+ "server": [
219
+ "./dist/server.d.ts"
220
+ ],
221
+ "server/express": [
222
+ "./dist/server/express.d.ts"
223
+ ],
224
+ "server/next": [
225
+ "./dist/server/next.d.ts"
226
+ ],
227
+ "adapters/memory": [
228
+ "./dist/adapters/memory.d.ts"
229
+ ],
230
+ "adapters/gcs": [
231
+ "./dist/adapters/gcs.d.ts"
232
+ ],
233
+ "ui": [
234
+ "./dist/ui.d.ts"
235
+ ],
236
+ "presets": [
237
+ "./dist/presets.d.ts"
238
+ ],
239
+ "adapters/s3": [
240
+ "./dist/adapters/s3.d.ts"
241
+ ]
242
+ }
243
+ },
244
+ "peerDependencies": {
245
+ "@aws-sdk/client-s3": ">=3",
246
+ "@aws-sdk/s3-request-presigner": ">=3",
247
+ "@google-cloud/storage": ">=7",
248
+ "react": ">=18",
249
+ "react-dom": ">=18"
250
+ },
251
+ "peerDependenciesMeta": {
252
+ "@aws-sdk/client-s3": {
253
+ "optional": true
254
+ },
255
+ "@aws-sdk/s3-request-presigner": {
256
+ "optional": true
257
+ },
258
+ "@google-cloud/storage": {
259
+ "optional": true
260
+ },
261
+ "react": {
262
+ "optional": true
263
+ },
264
+ "react-dom": {
265
+ "optional": true
266
+ }
81
267
  }
82
268
  }
package/tailwind.css ADDED
@@ -0,0 +1,80 @@
1
+ /*
2
+ * uploaderkit — Tailwind v4 entry.
3
+ *
4
+ * Consumers add, in this order:
5
+ * @import 'tailwindcss';
6
+ * @import 'uploaderkit/tailwind.css';
7
+ *
8
+ * `@source` registers the compiled output so the utilities the components use
9
+ * are generated by the consumer's own Tailwind build.
10
+ *
11
+ * `@theme` declares DEFAULTS only. Every token is a CSS variable, so an app
12
+ * rebrands the whole styled layer by redeclaring the ones it cares about:
13
+ *
14
+ * :root {
15
+ * --color-ui-primary: #c41e3a;
16
+ * --color-ui-primary-hover: #8b1529;
17
+ * }
18
+ */
19
+
20
+ @source './dist/ui.js';
21
+ @source './dist/ui.cjs';
22
+
23
+ @theme {
24
+ /* Surfaces */
25
+ --color-ui-surface: #ffffff;
26
+ --color-ui-surface-subtle: #fafafa;
27
+ --color-ui-surface-muted: #f4f4f5;
28
+
29
+ /* Text */
30
+ --color-ui-fg: #111827;
31
+ --color-ui-muted: #6b7280;
32
+ --color-ui-placeholder: #9ca3af;
33
+
34
+ /* Borders & focus */
35
+ --color-ui-border: #e5e7eb;
36
+ --color-ui-border-strong: #d1d5db;
37
+ --color-ui-ring: #93c5fd;
38
+
39
+ /* Primary action */
40
+ --color-ui-primary: #2563eb;
41
+ --color-ui-primary-hover: #1d4ed8;
42
+ --color-ui-primary-fg: #ffffff;
43
+ --color-ui-primary-soft: #eff6ff;
44
+ --color-ui-primary-soft-fg: #1d4ed8;
45
+
46
+ /* Semantic — danger */
47
+ --color-ui-danger: #dc2626;
48
+ --color-ui-danger-hover: #b91c1c;
49
+ --color-ui-danger-fg: #ffffff;
50
+ --color-ui-danger-soft: #fef2f2;
51
+ --color-ui-danger-soft-fg: #b91c1c;
52
+
53
+ /* Semantic — success */
54
+ --color-ui-success: #16a34a;
55
+ --color-ui-success-soft: #f0fdf4;
56
+
57
+ /* Semantic — warning */
58
+ --color-ui-warning: #d97706;
59
+ --color-ui-warning-soft: #fffbeb;
60
+
61
+ /* Overlay scrim behind the viewer and dialogs */
62
+ --color-ui-overlay: oklch(0.28 0.03 265 / 0.45);
63
+
64
+ /* Control radii */
65
+ --radius-ui: 0.5rem;
66
+ --radius-ui-lg: 0.75rem;
67
+ --radius-ui-xl: 1rem;
68
+
69
+ /* Motion */
70
+ --animate-ui-fade-in: ui-fade-in 200ms ease-out;
71
+ }
72
+
73
+ @keyframes ui-fade-in {
74
+ from {
75
+ opacity: 0;
76
+ }
77
+ to {
78
+ opacity: 1;
79
+ }
80
+ }