@anvilkit/plugin-asset-manager 0.1.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.
Files changed (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +159 -0
  3. package/dist/adapters/data-url.cjs +78 -0
  4. package/dist/adapters/data-url.d.cts +6 -0
  5. package/dist/adapters/data-url.d.cts.map +1 -0
  6. package/dist/adapters/data-url.d.ts +6 -0
  7. package/dist/adapters/data-url.d.ts.map +1 -0
  8. package/dist/adapters/data-url.js +44 -0
  9. package/dist/adapters/extract-image-dimensions.cjs +69 -0
  10. package/dist/adapters/extract-image-dimensions.d.cts +21 -0
  11. package/dist/adapters/extract-image-dimensions.d.cts.map +1 -0
  12. package/dist/adapters/extract-image-dimensions.d.ts +21 -0
  13. package/dist/adapters/extract-image-dimensions.d.ts.map +1 -0
  14. package/dist/adapters/extract-image-dimensions.js +35 -0
  15. package/dist/adapters/in-memory.cjs +62 -0
  16. package/dist/adapters/in-memory.d.cts +3 -0
  17. package/dist/adapters/in-memory.d.cts.map +1 -0
  18. package/dist/adapters/in-memory.d.ts +3 -0
  19. package/dist/adapters/in-memory.d.ts.map +1 -0
  20. package/dist/adapters/in-memory.js +28 -0
  21. package/dist/adapters/s3-presigned.cjs +166 -0
  22. package/dist/adapters/s3-presigned.d.cts +59 -0
  23. package/dist/adapters/s3-presigned.d.cts.map +1 -0
  24. package/dist/adapters/s3-presigned.d.ts +59 -0
  25. package/dist/adapters/s3-presigned.d.ts.map +1 -0
  26. package/dist/adapters/s3-presigned.js +129 -0
  27. package/dist/asset-reference.cjs +38 -0
  28. package/dist/asset-reference.d.cts +10 -0
  29. package/dist/asset-reference.d.cts.map +1 -0
  30. package/dist/asset-reference.d.ts +10 -0
  31. package/dist/asset-reference.d.ts.map +1 -0
  32. package/dist/asset-reference.js +4 -0
  33. package/dist/csp.cjs +83 -0
  34. package/dist/csp.d.cts +45 -0
  35. package/dist/csp.d.cts.map +1 -0
  36. package/dist/csp.d.ts +45 -0
  37. package/dist/csp.d.ts.map +1 -0
  38. package/dist/csp.js +49 -0
  39. package/dist/errors.cjs +58 -0
  40. package/dist/errors.d.cts +15 -0
  41. package/dist/errors.d.cts.map +1 -0
  42. package/dist/errors.d.ts +15 -0
  43. package/dist/errors.d.ts.map +1 -0
  44. package/dist/errors.js +21 -0
  45. package/dist/header-action.cjs +44 -0
  46. package/dist/header-action.d.cts +3 -0
  47. package/dist/header-action.d.cts.map +1 -0
  48. package/dist/header-action.d.ts +3 -0
  49. package/dist/header-action.d.ts.map +1 -0
  50. package/dist/header-action.js +10 -0
  51. package/dist/index.cjs +90 -0
  52. package/dist/index.d.cts +18 -0
  53. package/dist/index.d.cts.map +1 -0
  54. package/dist/index.d.ts +18 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +10 -0
  57. package/dist/infer-kind.cjs +45 -0
  58. package/dist/infer-kind.d.cts +8 -0
  59. package/dist/infer-kind.d.cts.map +1 -0
  60. package/dist/infer-kind.d.ts +8 -0
  61. package/dist/infer-kind.d.ts.map +1 -0
  62. package/dist/infer-kind.js +11 -0
  63. package/dist/plugin.cjs +258 -0
  64. package/dist/plugin.d.cts +9 -0
  65. package/dist/plugin.d.cts.map +1 -0
  66. package/dist/plugin.d.ts +9 -0
  67. package/dist/plugin.d.ts.map +1 -0
  68. package/dist/plugin.js +212 -0
  69. package/dist/registry.cjs +198 -0
  70. package/dist/registry.d.cts +3 -0
  71. package/dist/registry.d.cts.map +1 -0
  72. package/dist/registry.d.ts +3 -0
  73. package/dist/registry.d.ts.map +1 -0
  74. package/dist/registry.js +164 -0
  75. package/dist/resolver.cjs +185 -0
  76. package/dist/resolver.d.cts +10 -0
  77. package/dist/resolver.d.cts.map +1 -0
  78. package/dist/resolver.d.ts +10 -0
  79. package/dist/resolver.d.ts.map +1 -0
  80. package/dist/resolver.js +148 -0
  81. package/dist/retry.cjs +123 -0
  82. package/dist/retry.d.cts +71 -0
  83. package/dist/retry.d.cts.map +1 -0
  84. package/dist/retry.d.ts +71 -0
  85. package/dist/retry.d.ts.map +1 -0
  86. package/dist/retry.js +86 -0
  87. package/dist/studio-asset-source.cjs +211 -0
  88. package/dist/studio-asset-source.d.cts +52 -0
  89. package/dist/studio-asset-source.d.cts.map +1 -0
  90. package/dist/studio-asset-source.d.ts +52 -0
  91. package/dist/studio-asset-source.d.ts.map +1 -0
  92. package/dist/studio-asset-source.js +171 -0
  93. package/dist/testing/index.cjs +66 -0
  94. package/dist/testing/index.d.cts +24 -0
  95. package/dist/testing/index.d.cts.map +1 -0
  96. package/dist/testing/index.d.ts +24 -0
  97. package/dist/testing/index.d.ts.map +1 -0
  98. package/dist/testing/index.js +29 -0
  99. package/dist/types.cjs +18 -0
  100. package/dist/types.d.cts +132 -0
  101. package/dist/types.d.cts.map +1 -0
  102. package/dist/types.d.ts +132 -0
  103. package/dist/types.d.ts.map +1 -0
  104. package/dist/types.js +0 -0
  105. package/dist/ui/AssetBrowser.cjs +271 -0
  106. package/dist/ui/AssetBrowser.d.cts +45 -0
  107. package/dist/ui/AssetBrowser.d.cts.map +1 -0
  108. package/dist/ui/AssetBrowser.d.ts +45 -0
  109. package/dist/ui/AssetBrowser.d.ts.map +1 -0
  110. package/dist/ui/AssetBrowser.js +237 -0
  111. package/dist/ui/AssetCommandPalette.cjs +135 -0
  112. package/dist/ui/AssetCommandPalette.d.cts +21 -0
  113. package/dist/ui/AssetCommandPalette.d.cts.map +1 -0
  114. package/dist/ui/AssetCommandPalette.d.ts +21 -0
  115. package/dist/ui/AssetCommandPalette.d.ts.map +1 -0
  116. package/dist/ui/AssetCommandPalette.js +101 -0
  117. package/dist/ui/AssetManagerUI.cjs +169 -0
  118. package/dist/ui/AssetManagerUI.d.cts +15 -0
  119. package/dist/ui/AssetManagerUI.d.cts.map +1 -0
  120. package/dist/ui/AssetManagerUI.d.ts +15 -0
  121. package/dist/ui/AssetManagerUI.d.ts.map +1 -0
  122. package/dist/ui/AssetManagerUI.js +135 -0
  123. package/dist/ui/DeleteAssetDialog.cjs +70 -0
  124. package/dist/ui/DeleteAssetDialog.d.cts +22 -0
  125. package/dist/ui/DeleteAssetDialog.d.cts.map +1 -0
  126. package/dist/ui/DeleteAssetDialog.d.ts +22 -0
  127. package/dist/ui/DeleteAssetDialog.d.ts.map +1 -0
  128. package/dist/ui/DeleteAssetDialog.js +36 -0
  129. package/dist/ui/MetadataPanel.cjs +147 -0
  130. package/dist/ui/MetadataPanel.d.cts +21 -0
  131. package/dist/ui/MetadataPanel.d.cts.map +1 -0
  132. package/dist/ui/MetadataPanel.d.ts +21 -0
  133. package/dist/ui/MetadataPanel.d.ts.map +1 -0
  134. package/dist/ui/MetadataPanel.js +113 -0
  135. package/dist/ui/ReplaceAssetDialog.cjs +125 -0
  136. package/dist/ui/ReplaceAssetDialog.d.cts +14 -0
  137. package/dist/ui/ReplaceAssetDialog.d.cts.map +1 -0
  138. package/dist/ui/ReplaceAssetDialog.d.ts +14 -0
  139. package/dist/ui/ReplaceAssetDialog.d.ts.map +1 -0
  140. package/dist/ui/ReplaceAssetDialog.js +91 -0
  141. package/dist/ui/UploadButton.cjs +189 -0
  142. package/dist/ui/UploadButton.d.cts +17 -0
  143. package/dist/ui/UploadButton.d.cts.map +1 -0
  144. package/dist/ui/UploadButton.d.ts +17 -0
  145. package/dist/ui/UploadButton.d.ts.map +1 -0
  146. package/dist/ui/UploadButton.js +155 -0
  147. package/dist/ui/index.cjs +60 -0
  148. package/dist/ui/index.d.cts +15 -0
  149. package/dist/ui/index.d.cts.map +1 -0
  150. package/dist/ui/index.d.ts +15 -0
  151. package/dist/ui/index.d.ts.map +1 -0
  152. package/dist/ui/index.js +7 -0
  153. package/dist/validate-upload-result.cjs +149 -0
  154. package/dist/validate-upload-result.d.cts +9 -0
  155. package/dist/validate-upload-result.d.cts.map +1 -0
  156. package/dist/validate-upload-result.d.ts +9 -0
  157. package/dist/validate-upload-result.d.ts.map +1 -0
  158. package/dist/validate-upload-result.js +115 -0
  159. package/package.json +131 -0
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ s3PresignedAdapter: ()=>s3PresignedAdapter,
28
+ RetryableError: ()=>external_retry_cjs_namespaceObject.RetryableError
29
+ });
30
+ const external_errors_cjs_namespaceObject = require("../errors.cjs");
31
+ const external_retry_cjs_namespaceObject = require("../retry.cjs");
32
+ function s3PresignedAdapter(options) {
33
+ const fetchImpl = options.fetch ?? globalThis.fetch;
34
+ if ("function" != typeof fetchImpl) throw new Error("s3PresignedAdapter: no `fetch` implementation available. Pass `options.fetch`.");
35
+ const generateId = options.idGenerator ?? defaultIdGenerator;
36
+ return async (file)=>{
37
+ const presign = await (0, external_retry_cjs_namespaceObject.withRetry)(()=>requestPresign(file, fetchImpl, options), {
38
+ ...options.retry ?? {},
39
+ signal: options.signal
40
+ });
41
+ await (0, external_retry_cjs_namespaceObject.withRetry)(()=>putToS3(file, presign, fetchImpl, options.signal), {
42
+ ...options.retry ?? {},
43
+ signal: options.signal
44
+ });
45
+ const id = presign.id ?? generateId();
46
+ const publicUrl = presign.publicUrl ?? stripQueryAndFragment(presign.url);
47
+ const result = {
48
+ id,
49
+ url: publicUrl,
50
+ ...file.name ? {
51
+ name: file.name
52
+ } : {},
53
+ meta: {
54
+ size: file.size,
55
+ ...file.type ? {
56
+ mimeType: file.type
57
+ } : {}
58
+ }
59
+ };
60
+ return result;
61
+ };
62
+ }
63
+ async function requestPresign(file, fetchImpl, options) {
64
+ const url = "string" == typeof options.presignEndpoint ? options.presignEndpoint : options.presignEndpoint.toString();
65
+ const body = JSON.stringify({
66
+ name: file.name,
67
+ type: file.type,
68
+ size: file.size
69
+ });
70
+ let response;
71
+ try {
72
+ response = await fetchImpl(url, {
73
+ method: "POST",
74
+ headers: {
75
+ "Content-Type": "application/json",
76
+ ...options.headers ?? {}
77
+ },
78
+ body,
79
+ ...options.signal ? {
80
+ signal: options.signal
81
+ } : {}
82
+ });
83
+ } catch (cause) {
84
+ throw new external_retry_cjs_namespaceObject.RetryableError(`s3PresignedAdapter: presign request failed (${describeError(cause)}).`, {
85
+ cause
86
+ });
87
+ }
88
+ if (response.status >= 500) throw new external_retry_cjs_namespaceObject.RetryableError(`s3PresignedAdapter: presign returned ${response.status}.`, {
89
+ retryAfterMs: parseRetryAfter(response.headers.get("retry-after"))
90
+ });
91
+ if (!response.ok) throw new external_errors_cjs_namespaceObject.AssetValidationError("UPLOAD_FAILED", `s3PresignedAdapter: presign returned ${response.status}.`);
92
+ let payload;
93
+ try {
94
+ payload = await response.json();
95
+ } catch (cause) {
96
+ throw new external_errors_cjs_namespaceObject.AssetValidationError("UPLOAD_FAILED", "s3PresignedAdapter: presign response was not JSON.", {
97
+ cause
98
+ });
99
+ }
100
+ if (!isPresignResponse(payload)) throw new external_errors_cjs_namespaceObject.AssetValidationError("UPLOAD_FAILED", "s3PresignedAdapter: presign response missing `url`.");
101
+ return payload;
102
+ }
103
+ async function putToS3(file, presign, fetchImpl, signal) {
104
+ const headers = {
105
+ ...file.type ? {
106
+ "Content-Type": file.type
107
+ } : {},
108
+ ...presign.headers ?? {}
109
+ };
110
+ let response;
111
+ try {
112
+ response = await fetchImpl(presign.url, {
113
+ method: "PUT",
114
+ body: file,
115
+ headers,
116
+ ...signal ? {
117
+ signal
118
+ } : {}
119
+ });
120
+ } catch (cause) {
121
+ throw new external_retry_cjs_namespaceObject.RetryableError(`s3PresignedAdapter: PUT failed (${describeError(cause)}).`, {
122
+ cause
123
+ });
124
+ }
125
+ if (response.status >= 500) throw new external_retry_cjs_namespaceObject.RetryableError(`s3PresignedAdapter: PUT returned ${response.status}.`, {
126
+ retryAfterMs: parseRetryAfter(response.headers.get("retry-after"))
127
+ });
128
+ if (!response.ok) throw new external_errors_cjs_namespaceObject.AssetValidationError("UPLOAD_FAILED", `s3PresignedAdapter: PUT returned ${response.status}.`);
129
+ }
130
+ function isPresignResponse(value) {
131
+ return null !== value && "object" == typeof value && "string" == typeof value.url && value.url.length > 0;
132
+ }
133
+ function stripQueryAndFragment(url) {
134
+ const queryIdx = url.indexOf("?");
135
+ const fragmentIdx = url.indexOf("#");
136
+ const cuts = [
137
+ queryIdx,
138
+ fragmentIdx
139
+ ].filter((i)=>-1 !== i);
140
+ if (0 === cuts.length) return url;
141
+ return url.slice(0, Math.min(...cuts));
142
+ }
143
+ function parseRetryAfter(header) {
144
+ if (null === header) return;
145
+ const seconds = Number(header);
146
+ if (Number.isFinite(seconds) && seconds >= 0) return 1000 * seconds;
147
+ const date = Date.parse(header);
148
+ if (!Number.isNaN(date)) return Math.max(0, date - Date.now());
149
+ }
150
+ function describeError(error) {
151
+ if (error instanceof Error) return error.message || error.name;
152
+ return String(error);
153
+ }
154
+ function defaultIdGenerator() {
155
+ if (void 0 !== globalThis.crypto && "function" == typeof globalThis.crypto.randomUUID) return globalThis.crypto.randomUUID();
156
+ return `asset-${Math.random().toString(36).slice(2)}-${Date.now()}`;
157
+ }
158
+ exports.RetryableError = __webpack_exports__.RetryableError;
159
+ exports.s3PresignedAdapter = __webpack_exports__.s3PresignedAdapter;
160
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
161
+ "RetryableError",
162
+ "s3PresignedAdapter"
163
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
164
+ Object.defineProperty(exports, '__esModule', {
165
+ value: true
166
+ });
@@ -0,0 +1,59 @@
1
+ /**
2
+ * @file S3 presigned adapter — production-ready upload backend that
3
+ * fronts an arbitrary "presign + PUT" service.
4
+ *
5
+ * Pipeline:
6
+ * 1. POST `presignEndpoint` with `{ name, type, size }`.
7
+ * 2. Receive `{ url, publicUrl?, fields?, headers?, id? }`.
8
+ * 3. PUT the file body to `url` with `Content-Type: file.type`.
9
+ * 4. Return a validated `UploadResult` for the registry.
10
+ *
11
+ * Both phases are wrapped in `withRetry()` — 5xx and network failures
12
+ * throw `RetryableError`; 4xx and shape errors throw
13
+ * `AssetValidationError("UPLOAD_FAILED")` (no retry).
14
+ *
15
+ * The adapter never logs file contents — only `name`, `size`, and
16
+ * `mimeType` are safe to log.
17
+ *
18
+ * @experimental Public surface may change before v1.0.
19
+ */
20
+ import { type RetryOptions } from "../retry.js";
21
+ import type { UploadAdapter } from "../types.js";
22
+ export { RetryableError } from "../retry.js";
23
+ export type { RetryOptions } from "../retry.js";
24
+ export interface S3PresignedAdapterOptions {
25
+ /** Endpoint that returns a presigned PUT target for the file. */
26
+ readonly presignEndpoint: string | URL;
27
+ /**
28
+ * Injectable `fetch` implementation. Defaults to `globalThis.fetch`.
29
+ * Tests pass a fake; production callers can wire in a custom fetch
30
+ * for instrumentation or redirects.
31
+ */
32
+ readonly fetch?: typeof globalThis.fetch;
33
+ /** Recorded for logs / diagnostics; the adapter does not validate it. */
34
+ readonly region?: string;
35
+ /** Forwarded to `withRetry()` for both phases. */
36
+ readonly retry?: RetryOptions;
37
+ /** Aborts the in-flight presign + PUT (and any retry sleeps). */
38
+ readonly signal?: AbortSignal;
39
+ /** Extra headers applied to the presign POST (e.g. auth). */
40
+ readonly headers?: Record<string, string>;
41
+ /** Override the asset id generator. Default: `crypto.randomUUID()`. */
42
+ readonly idGenerator?: () => string;
43
+ }
44
+ /**
45
+ * Shape returned by `presignEndpoint`. Hosts may return additional
46
+ * fields — the adapter ignores them.
47
+ */
48
+ export interface S3PresignResponse {
49
+ /** Presigned PUT URL. */
50
+ readonly url: string;
51
+ /** Canonical URL recorded on the asset. Defaults to a stripped `url`. */
52
+ readonly publicUrl?: string;
53
+ /** Optional headers to forward on the PUT (e.g. `x-amz-*`). */
54
+ readonly headers?: Record<string, string>;
55
+ /** Optional asset id; falls back to `idGenerator()`. */
56
+ readonly id?: string;
57
+ }
58
+ export declare function s3PresignedAdapter(options: S3PresignedAdapterOptions): UploadAdapter;
59
+ //# sourceMappingURL=s3-presigned.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3-presigned.d.cts","sourceRoot":"","sources":["../../src/adapters/s3-presigned.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAkB,KAAK,YAAY,EAAa,MAAM,aAAa,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAgB,MAAM,aAAa,CAAC;AAE/D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,yBAAyB;IACzC,iEAAiE;IACjE,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,GAAG,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACzC,yEAAyE;IACzE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,kDAAkD;IAClD,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,uEAAuE;IACvE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,MAAM,CAAC;CACpC;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IACjC,yBAAyB;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,wDAAwD;IACxD,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,yBAAyB,GAChC,aAAa,CAkCf"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * @file S3 presigned adapter — production-ready upload backend that
3
+ * fronts an arbitrary "presign + PUT" service.
4
+ *
5
+ * Pipeline:
6
+ * 1. POST `presignEndpoint` with `{ name, type, size }`.
7
+ * 2. Receive `{ url, publicUrl?, fields?, headers?, id? }`.
8
+ * 3. PUT the file body to `url` with `Content-Type: file.type`.
9
+ * 4. Return a validated `UploadResult` for the registry.
10
+ *
11
+ * Both phases are wrapped in `withRetry()` — 5xx and network failures
12
+ * throw `RetryableError`; 4xx and shape errors throw
13
+ * `AssetValidationError("UPLOAD_FAILED")` (no retry).
14
+ *
15
+ * The adapter never logs file contents — only `name`, `size`, and
16
+ * `mimeType` are safe to log.
17
+ *
18
+ * @experimental Public surface may change before v1.0.
19
+ */
20
+ import { type RetryOptions } from "../retry.js";
21
+ import type { UploadAdapter } from "../types.js";
22
+ export { RetryableError } from "../retry.js";
23
+ export type { RetryOptions } from "../retry.js";
24
+ export interface S3PresignedAdapterOptions {
25
+ /** Endpoint that returns a presigned PUT target for the file. */
26
+ readonly presignEndpoint: string | URL;
27
+ /**
28
+ * Injectable `fetch` implementation. Defaults to `globalThis.fetch`.
29
+ * Tests pass a fake; production callers can wire in a custom fetch
30
+ * for instrumentation or redirects.
31
+ */
32
+ readonly fetch?: typeof globalThis.fetch;
33
+ /** Recorded for logs / diagnostics; the adapter does not validate it. */
34
+ readonly region?: string;
35
+ /** Forwarded to `withRetry()` for both phases. */
36
+ readonly retry?: RetryOptions;
37
+ /** Aborts the in-flight presign + PUT (and any retry sleeps). */
38
+ readonly signal?: AbortSignal;
39
+ /** Extra headers applied to the presign POST (e.g. auth). */
40
+ readonly headers?: Record<string, string>;
41
+ /** Override the asset id generator. Default: `crypto.randomUUID()`. */
42
+ readonly idGenerator?: () => string;
43
+ }
44
+ /**
45
+ * Shape returned by `presignEndpoint`. Hosts may return additional
46
+ * fields — the adapter ignores them.
47
+ */
48
+ export interface S3PresignResponse {
49
+ /** Presigned PUT URL. */
50
+ readonly url: string;
51
+ /** Canonical URL recorded on the asset. Defaults to a stripped `url`. */
52
+ readonly publicUrl?: string;
53
+ /** Optional headers to forward on the PUT (e.g. `x-amz-*`). */
54
+ readonly headers?: Record<string, string>;
55
+ /** Optional asset id; falls back to `idGenerator()`. */
56
+ readonly id?: string;
57
+ }
58
+ export declare function s3PresignedAdapter(options: S3PresignedAdapterOptions): UploadAdapter;
59
+ //# sourceMappingURL=s3-presigned.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3-presigned.d.ts","sourceRoot":"","sources":["../../src/adapters/s3-presigned.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAkB,KAAK,YAAY,EAAa,MAAM,aAAa,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAgB,MAAM,aAAa,CAAC;AAE/D,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,yBAAyB;IACzC,iEAAiE;IACjE,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,GAAG,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACzC,yEAAyE;IACzE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,kDAAkD;IAClD,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,uEAAuE;IACvE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,MAAM,CAAC;CACpC;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IACjC,yBAAyB;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,wDAAwD;IACxD,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,yBAAyB,GAChC,aAAa,CAkCf"}
@@ -0,0 +1,129 @@
1
+ import { AssetValidationError } from "../errors.js";
2
+ import { RetryableError, withRetry } from "../retry.js";
3
+ function s3PresignedAdapter(options) {
4
+ const fetchImpl = options.fetch ?? globalThis.fetch;
5
+ if ("function" != typeof fetchImpl) throw new Error("s3PresignedAdapter: no `fetch` implementation available. Pass `options.fetch`.");
6
+ const generateId = options.idGenerator ?? defaultIdGenerator;
7
+ return async (file)=>{
8
+ const presign = await withRetry(()=>requestPresign(file, fetchImpl, options), {
9
+ ...options.retry ?? {},
10
+ signal: options.signal
11
+ });
12
+ await withRetry(()=>putToS3(file, presign, fetchImpl, options.signal), {
13
+ ...options.retry ?? {},
14
+ signal: options.signal
15
+ });
16
+ const id = presign.id ?? generateId();
17
+ const publicUrl = presign.publicUrl ?? stripQueryAndFragment(presign.url);
18
+ const result = {
19
+ id,
20
+ url: publicUrl,
21
+ ...file.name ? {
22
+ name: file.name
23
+ } : {},
24
+ meta: {
25
+ size: file.size,
26
+ ...file.type ? {
27
+ mimeType: file.type
28
+ } : {}
29
+ }
30
+ };
31
+ return result;
32
+ };
33
+ }
34
+ async function requestPresign(file, fetchImpl, options) {
35
+ const url = "string" == typeof options.presignEndpoint ? options.presignEndpoint : options.presignEndpoint.toString();
36
+ const body = JSON.stringify({
37
+ name: file.name,
38
+ type: file.type,
39
+ size: file.size
40
+ });
41
+ let response;
42
+ try {
43
+ response = await fetchImpl(url, {
44
+ method: "POST",
45
+ headers: {
46
+ "Content-Type": "application/json",
47
+ ...options.headers ?? {}
48
+ },
49
+ body,
50
+ ...options.signal ? {
51
+ signal: options.signal
52
+ } : {}
53
+ });
54
+ } catch (cause) {
55
+ throw new RetryableError(`s3PresignedAdapter: presign request failed (${describeError(cause)}).`, {
56
+ cause
57
+ });
58
+ }
59
+ if (response.status >= 500) throw new RetryableError(`s3PresignedAdapter: presign returned ${response.status}.`, {
60
+ retryAfterMs: parseRetryAfter(response.headers.get("retry-after"))
61
+ });
62
+ if (!response.ok) throw new AssetValidationError("UPLOAD_FAILED", `s3PresignedAdapter: presign returned ${response.status}.`);
63
+ let payload;
64
+ try {
65
+ payload = await response.json();
66
+ } catch (cause) {
67
+ throw new AssetValidationError("UPLOAD_FAILED", "s3PresignedAdapter: presign response was not JSON.", {
68
+ cause
69
+ });
70
+ }
71
+ if (!isPresignResponse(payload)) throw new AssetValidationError("UPLOAD_FAILED", "s3PresignedAdapter: presign response missing `url`.");
72
+ return payload;
73
+ }
74
+ async function putToS3(file, presign, fetchImpl, signal) {
75
+ const headers = {
76
+ ...file.type ? {
77
+ "Content-Type": file.type
78
+ } : {},
79
+ ...presign.headers ?? {}
80
+ };
81
+ let response;
82
+ try {
83
+ response = await fetchImpl(presign.url, {
84
+ method: "PUT",
85
+ body: file,
86
+ headers,
87
+ ...signal ? {
88
+ signal
89
+ } : {}
90
+ });
91
+ } catch (cause) {
92
+ throw new RetryableError(`s3PresignedAdapter: PUT failed (${describeError(cause)}).`, {
93
+ cause
94
+ });
95
+ }
96
+ if (response.status >= 500) throw new RetryableError(`s3PresignedAdapter: PUT returned ${response.status}.`, {
97
+ retryAfterMs: parseRetryAfter(response.headers.get("retry-after"))
98
+ });
99
+ if (!response.ok) throw new AssetValidationError("UPLOAD_FAILED", `s3PresignedAdapter: PUT returned ${response.status}.`);
100
+ }
101
+ function isPresignResponse(value) {
102
+ return null !== value && "object" == typeof value && "string" == typeof value.url && value.url.length > 0;
103
+ }
104
+ function stripQueryAndFragment(url) {
105
+ const queryIdx = url.indexOf("?");
106
+ const fragmentIdx = url.indexOf("#");
107
+ const cuts = [
108
+ queryIdx,
109
+ fragmentIdx
110
+ ].filter((i)=>-1 !== i);
111
+ if (0 === cuts.length) return url;
112
+ return url.slice(0, Math.min(...cuts));
113
+ }
114
+ function parseRetryAfter(header) {
115
+ if (null === header) return;
116
+ const seconds = Number(header);
117
+ if (Number.isFinite(seconds) && seconds >= 0) return 1000 * seconds;
118
+ const date = Date.parse(header);
119
+ if (!Number.isNaN(date)) return Math.max(0, date - Date.now());
120
+ }
121
+ function describeError(error) {
122
+ if (error instanceof Error) return error.message || error.name;
123
+ return String(error);
124
+ }
125
+ function defaultIdGenerator() {
126
+ if (void 0 !== globalThis.crypto && "function" == typeof globalThis.crypto.randomUUID) return globalThis.crypto.randomUUID();
127
+ return `asset-${Math.random().toString(36).slice(2)}-${Date.now()}`;
128
+ }
129
+ export { RetryableError, s3PresignedAdapter };
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ createAssetReference: ()=>createAssetReference
28
+ });
29
+ function createAssetReference(id) {
30
+ return `asset://${id}`;
31
+ }
32
+ exports.createAssetReference = __webpack_exports__.createAssetReference;
33
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
34
+ "createAssetReference"
35
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
36
+ Object.defineProperty(exports, '__esModule', {
37
+ value: true
38
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @file Canonical `asset://${id}` reference helper.
3
+ *
4
+ * Lives in its own file so both `plugin.ts` (which dispatches asset
5
+ * references into Puck data) and `studio-asset-source.ts` (which
6
+ * surfaces the URL to the sidebar's `image` module) can reuse it
7
+ * without creating a circular import between them.
8
+ */
9
+ export declare function createAssetReference(id: string): string;
10
+ //# sourceMappingURL=asset-reference.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-reference.d.cts","sourceRoot":"","sources":["../src/asset-reference.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEvD"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @file Canonical `asset://${id}` reference helper.
3
+ *
4
+ * Lives in its own file so both `plugin.ts` (which dispatches asset
5
+ * references into Puck data) and `studio-asset-source.ts` (which
6
+ * surfaces the URL to the sidebar's `image` module) can reuse it
7
+ * without creating a circular import between them.
8
+ */
9
+ export declare function createAssetReference(id: string): string;
10
+ //# sourceMappingURL=asset-reference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-reference.d.ts","sourceRoot":"","sources":["../src/asset-reference.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEvD"}
@@ -0,0 +1,4 @@
1
+ function createAssetReference(id) {
2
+ return `asset://${id}`;
3
+ }
4
+ export { createAssetReference };
package/dist/csp.cjs ADDED
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.d = (exports1, definition)=>{
5
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
+ enumerable: true,
7
+ get: definition[key]
8
+ });
9
+ };
10
+ })();
11
+ (()=>{
12
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.r = (exports1)=>{
16
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
17
+ value: 'Module'
18
+ });
19
+ Object.defineProperty(exports1, '__esModule', {
20
+ value: true
21
+ });
22
+ };
23
+ })();
24
+ var __webpack_exports__ = {};
25
+ __webpack_require__.r(__webpack_exports__);
26
+ __webpack_require__.d(__webpack_exports__, {
27
+ getRequiredCsp: ()=>getRequiredCsp
28
+ });
29
+ function getRequiredCsp(options = {}) {
30
+ const connectSrc = new Set();
31
+ const imgSrc = new Set();
32
+ const mediaSrc = new Set();
33
+ if (true === options.dataUrl) {
34
+ imgSrc.add("data:");
35
+ mediaSrc.add("data:");
36
+ }
37
+ if (true === options.inMemory) {
38
+ imgSrc.add("blob:");
39
+ mediaSrc.add("blob:");
40
+ }
41
+ const s3List = Array.isArray(options.s3) ? options.s3 : options.s3 ? [
42
+ options.s3
43
+ ] : [];
44
+ for (const entry of s3List){
45
+ const presignOrigin = parseOrigin(entry.presignEndpoint);
46
+ if (void 0 !== presignOrigin) connectSrc.add(presignOrigin);
47
+ const publicOrigin = void 0 !== entry.publicHost ? parseOrigin(entry.publicHost) : void 0;
48
+ if (void 0 !== publicOrigin) {
49
+ connectSrc.add(publicOrigin);
50
+ imgSrc.add(publicOrigin);
51
+ mediaSrc.add(publicOrigin);
52
+ } else if (void 0 !== presignOrigin) {
53
+ imgSrc.add(presignOrigin);
54
+ mediaSrc.add(presignOrigin);
55
+ }
56
+ }
57
+ return Object.freeze({
58
+ connectSrc: Object.freeze([
59
+ ...connectSrc
60
+ ]),
61
+ imgSrc: Object.freeze([
62
+ ...imgSrc
63
+ ]),
64
+ mediaSrc: Object.freeze([
65
+ ...mediaSrc
66
+ ])
67
+ });
68
+ }
69
+ function parseOrigin(value) {
70
+ const raw = "string" == typeof value ? value : value.toString();
71
+ try {
72
+ return new URL(raw).origin;
73
+ } catch {
74
+ return;
75
+ }
76
+ }
77
+ exports.getRequiredCsp = __webpack_exports__.getRequiredCsp;
78
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
79
+ "getRequiredCsp"
80
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
81
+ Object.defineProperty(exports, '__esModule', {
82
+ value: true
83
+ });
package/dist/csp.d.cts ADDED
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @file CSP advisor — computes the minimum `connect-src` / `img-src` /
3
+ * `media-src` directives a host page needs in order for the
4
+ * configured adapters and their served URLs to load. Pure static
5
+ * derivation; no network or DOM dependencies.
6
+ *
7
+ * The host calls this once at boot with a description of which
8
+ * adapters are wired in, then merges the result into its existing CSP
9
+ * builder.
10
+ *
11
+ * @example
12
+ * const csp = getRequiredCsp({
13
+ * dataUrl: true,
14
+ * s3: { presignEndpoint: "https://uploads.example.com/sign" },
15
+ * });
16
+ * // csp.connectSrc → ["https://uploads.example.com"]
17
+ * // csp.imgSrc → ["data:"]
18
+ * // csp.mediaSrc → ["data:"]
19
+ */
20
+ export interface S3CspOptions {
21
+ /** The presign endpoint configured on `s3PresignedAdapter`. */
22
+ readonly presignEndpoint: string | URL;
23
+ /**
24
+ * Optional public-bucket origin (e.g. `https://cdn.example.com`).
25
+ * If your `publicUrl` returned by the presign service points at a
26
+ * different host than the presign endpoint itself, list it here so
27
+ * the advisor can include it in `img-src` / `media-src`.
28
+ */
29
+ readonly publicHost?: string;
30
+ }
31
+ export interface RequiredCspOptions {
32
+ /** True if `dataUrlUploader` is mounted. Defaults to false. */
33
+ readonly dataUrl?: boolean;
34
+ /** True if `inMemoryUploader` is mounted. Defaults to false. */
35
+ readonly inMemory?: boolean;
36
+ /** Description of any `s3PresignedAdapter` instance(s). */
37
+ readonly s3?: S3CspOptions | readonly S3CspOptions[];
38
+ }
39
+ export interface RequiredCsp {
40
+ readonly connectSrc: readonly string[];
41
+ readonly imgSrc: readonly string[];
42
+ readonly mediaSrc: readonly string[];
43
+ }
44
+ export declare function getRequiredCsp(options?: RequiredCspOptions): RequiredCsp;
45
+ //# sourceMappingURL=csp.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csp.d.cts","sourceRoot":"","sources":["../src/csp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,YAAY;IAC5B,+DAA+D;IAC/D,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,GAAG,CAAC;IACvC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IAClC,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,CAAC,EAAE,YAAY,GAAG,SAAS,YAAY,EAAE,CAAC;CACrD;AAED,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,wBAAgB,cAAc,CAAC,OAAO,GAAE,kBAAuB,GAAG,WAAW,CA4C5E"}