@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,258 @@
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
+ uploadAsset: ()=>uploadAsset,
28
+ validateSelectedFile: ()=>validateSelectedFile,
29
+ createAssetManagerPlugin: ()=>createAssetManagerPlugin,
30
+ getAssetRegistry: ()=>getAssetRegistry,
31
+ createAssetReference: ()=>external_asset_reference_cjs_namespaceObject.createAssetReference
32
+ });
33
+ const external_asset_reference_cjs_namespaceObject = require("./asset-reference.cjs");
34
+ const external_errors_cjs_namespaceObject = require("./errors.cjs");
35
+ const external_header_action_cjs_namespaceObject = require("./header-action.cjs");
36
+ const external_infer_kind_cjs_namespaceObject = require("./infer-kind.cjs");
37
+ const external_registry_cjs_namespaceObject = require("./registry.cjs");
38
+ const external_resolver_cjs_namespaceObject = require("./resolver.cjs");
39
+ const external_studio_asset_source_cjs_namespaceObject = require("./studio-asset-source.cjs");
40
+ const external_validate_upload_result_cjs_namespaceObject = require("./validate-upload-result.cjs");
41
+ const META = {
42
+ id: "anvilkit-plugin-asset-manager",
43
+ name: "Asset Manager",
44
+ version: "1.0.0",
45
+ coreVersion: "^0.1.0-alpha",
46
+ description: "Headless asset upload plugin with host-provided persistence and a separate React UI subpath."
47
+ };
48
+ const stateByToken = new WeakMap();
49
+ const tokenByContext = new WeakMap();
50
+ function createAssetManagerPlugin(options) {
51
+ const token = {};
52
+ const registry = (0, external_registry_cjs_namespaceObject.createAssetRegistry)();
53
+ const normalizedOptions = normalizeOptions(options);
54
+ const assetResolver = (0, external_resolver_cjs_namespaceObject.createIRAssetResolver)({
55
+ registry,
56
+ dataUrlAllowlistOptIn: normalizedOptions.dataUrlAllowlistOptIn,
57
+ allowMixedScriptHostnames: normalizedOptions.allowMixedScriptHostnames
58
+ });
59
+ return {
60
+ meta: META,
61
+ register (_ctx) {
62
+ const registration = {
63
+ meta: META,
64
+ headerActions: [
65
+ external_header_action_cjs_namespaceObject.uploadAssetAction
66
+ ],
67
+ hooks: {
68
+ onInit (initCtx) {
69
+ const cleanups = [];
70
+ stateByToken.set(token, {
71
+ options: normalizedOptions,
72
+ registry,
73
+ cleanups
74
+ });
75
+ tokenByContext.set(initCtx, token);
76
+ initCtx.registerAssetResolver(assetResolver);
77
+ const studioAssetSource = (0, external_studio_asset_source_cjs_namespaceObject.createStudioAssetSource)({
78
+ registry,
79
+ upload: (file)=>uploadAsset(initCtx, file),
80
+ ...normalizedOptions.getThumbnail ? {
81
+ getThumbnail: normalizedOptions.getThumbnail
82
+ } : {}
83
+ });
84
+ const unregisterAssetSource = initCtx.registerAssetSource?.(studioAssetSource);
85
+ if (void 0 !== unregisterAssetSource) cleanups.push(unregisterAssetSource);
86
+ },
87
+ onDestroy (destroyCtx) {
88
+ const state = stateByToken.get(token);
89
+ if (void 0 !== state) for (const cleanup of state.cleanups)cleanup();
90
+ tokenByContext.delete(destroyCtx);
91
+ stateByToken.delete(token);
92
+ }
93
+ }
94
+ };
95
+ return registration;
96
+ }
97
+ };
98
+ }
99
+ function getAssetRegistry(ctx) {
100
+ const token = tokenByContext.get(ctx);
101
+ return token ? stateByToken.get(token)?.registry : void 0;
102
+ }
103
+ async function uploadAsset(ctx, file) {
104
+ const state = getRuntimeState(ctx);
105
+ const { options, registry } = state;
106
+ try {
107
+ validateSelectedFile(file, options);
108
+ const uploadResult = await options.uploader(file);
109
+ const validated = (0, external_validate_upload_result_cjs_namespaceObject.validateUploadResult)(mergeUploadMeta(uploadResult, file), options);
110
+ const tagged = withDerivedTags(validated, file);
111
+ const stored = registry.register(tagged);
112
+ dispatchAssetReference(ctx, stored);
113
+ ctx.emit("asset-manager:uploaded", {
114
+ asset: stored,
115
+ reference: (0, external_asset_reference_cjs_namespaceObject.createAssetReference)(stored.id)
116
+ });
117
+ return stored;
118
+ } catch (error) {
119
+ const normalizedError = error instanceof external_errors_cjs_namespaceObject.AssetValidationError ? error : new external_errors_cjs_namespaceObject.AssetValidationError("UPLOAD_FAILED", error instanceof Error ? error.message : String(error), {
120
+ cause: error
121
+ });
122
+ ctx.emit("asset-manager:error", {
123
+ code: normalizedError.code,
124
+ message: normalizedError.message
125
+ });
126
+ ctx.log("error", normalizedError.message, {
127
+ code: normalizedError.code
128
+ });
129
+ throw normalizedError;
130
+ }
131
+ }
132
+ function validateSelectedFile(file, options) {
133
+ if (void 0 !== options.maxFileSize && file.size > options.maxFileSize) throw new external_errors_cjs_namespaceObject.AssetValidationError("FILE_TOO_LARGE", `File size ${file.size} bytes exceeds the configured maxFileSize of ${options.maxFileSize} bytes.`);
134
+ if (options.acceptedMimeTypes && options.acceptedMimeTypes.length > 0 && !mimeTypeMatches(file.type, options.acceptedMimeTypes)) {
135
+ const mimeType = file.type || "unknown";
136
+ throw new external_errors_cjs_namespaceObject.AssetValidationError("UNSUPPORTED_MIME_TYPE", `File MIME type "${mimeType}" is not in acceptedMimeTypes.`);
137
+ }
138
+ }
139
+ function getRuntimeState(ctx) {
140
+ const token = tokenByContext.get(ctx);
141
+ const state = token ? stateByToken.get(token) : void 0;
142
+ if (!state) throw new Error("createAssetManagerPlugin: uploadAsset called before the plugin runtime was initialized.");
143
+ return state;
144
+ }
145
+ function normalizeOptions(options) {
146
+ return {
147
+ ...options,
148
+ ...options.acceptedMimeTypes ? {
149
+ acceptedMimeTypes: Object.freeze([
150
+ ...options.acceptedMimeTypes
151
+ ])
152
+ } : {}
153
+ };
154
+ }
155
+ function withDerivedTags(asset, file) {
156
+ if (void 0 !== asset.tags && asset.tags.length > 0) return asset;
157
+ const tags = new Set();
158
+ const kind = (0, external_infer_kind_cjs_namespaceObject.inferAssetKind)(asset);
159
+ if ("other" !== kind) tags.add(kind);
160
+ for (const token of filenameTokens(file.name)){
161
+ if (tags.size >= 3) break;
162
+ tags.add(token);
163
+ }
164
+ if (0 === tags.size) return asset;
165
+ return {
166
+ ...asset,
167
+ tags: Object.freeze([
168
+ ...tags
169
+ ])
170
+ };
171
+ }
172
+ function filenameTokens(name) {
173
+ const stem = name.replace(/\.[^./\\]+$/, "");
174
+ const tokens = stem.toLowerCase().split(/[^a-z0-9]+/).filter((token)=>token.length >= 2 && !/^\d+$/.test(token));
175
+ return tokens.slice(0, 2);
176
+ }
177
+ function mergeUploadMeta(result, file) {
178
+ const meta = {
179
+ size: file.size,
180
+ ...file.type ? {
181
+ mimeType: file.type
182
+ } : {},
183
+ ...result.meta ?? {}
184
+ };
185
+ return {
186
+ ...result,
187
+ ...void 0 === result.name && file.name ? {
188
+ name: file.name
189
+ } : {},
190
+ meta
191
+ };
192
+ }
193
+ function mimeTypeMatches(input, acceptedMimeTypes) {
194
+ if ("" === input) return false;
195
+ return acceptedMimeTypes.some((accepted)=>{
196
+ if (accepted.endsWith("/*")) {
197
+ const prefix = accepted.slice(0, accepted.length - 1);
198
+ return input.startsWith(prefix);
199
+ }
200
+ return input === accepted;
201
+ });
202
+ }
203
+ function dispatchAssetReference(ctx, asset) {
204
+ const currentData = ctx.getData();
205
+ const currentAssetsRaw = Array.isArray(currentData.assets) ? currentData.assets : [];
206
+ const assetEntry = toIRAsset(asset);
207
+ const nextAssets = [];
208
+ let replaced = false;
209
+ for (const entry of currentAssetsRaw)if (isRecord(entry) && "string" == typeof entry.id && entry.id === asset.id) {
210
+ nextAssets.push(assetEntry);
211
+ replaced = true;
212
+ } else nextAssets.push(entry);
213
+ if (!replaced) nextAssets.push(assetEntry);
214
+ const nextData = {
215
+ ...currentData,
216
+ assets: nextAssets
217
+ };
218
+ ctx.getPuckApi().dispatch({
219
+ type: "setData",
220
+ data: nextData
221
+ });
222
+ }
223
+ function toIRAsset(asset) {
224
+ return {
225
+ id: asset.id,
226
+ kind: inferIRAssetKind(asset.meta?.mimeType, asset.url),
227
+ url: (0, external_asset_reference_cjs_namespaceObject.createAssetReference)(asset.id),
228
+ ...asset.meta ? {
229
+ meta: asset.meta
230
+ } : {}
231
+ };
232
+ }
233
+ function inferIRAssetKind(mimeType, url) {
234
+ if (mimeType?.startsWith("image/")) return "image";
235
+ if (mimeType?.startsWith("video/")) return "video";
236
+ if (mimeType?.startsWith("font/") || /\.(?:woff2?|ttf|otf)(?:$|[?#])/i.test(url)) return "font";
237
+ if ("text/css" === mimeType) return "style";
238
+ if ("application/javascript" === mimeType || "text/javascript" === mimeType) return "script";
239
+ return "other";
240
+ }
241
+ function isRecord(value) {
242
+ return "object" == typeof value && null !== value;
243
+ }
244
+ exports.createAssetManagerPlugin = __webpack_exports__.createAssetManagerPlugin;
245
+ exports.createAssetReference = __webpack_exports__.createAssetReference;
246
+ exports.getAssetRegistry = __webpack_exports__.getAssetRegistry;
247
+ exports.uploadAsset = __webpack_exports__.uploadAsset;
248
+ exports.validateSelectedFile = __webpack_exports__.validateSelectedFile;
249
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
250
+ "createAssetManagerPlugin",
251
+ "createAssetReference",
252
+ "getAssetRegistry",
253
+ "uploadAsset",
254
+ "validateSelectedFile"
255
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
256
+ Object.defineProperty(exports, '__esModule', {
257
+ value: true
258
+ });
@@ -0,0 +1,9 @@
1
+ import type { StudioPlugin, StudioPluginContext } from "@anvilkit/core/types";
2
+ import { createAssetReference } from "./asset-reference.js";
3
+ import type { AssetManagerOptions, AssetRegistry, UploadResult } from "./types.js";
4
+ export { createAssetReference };
5
+ export declare function createAssetManagerPlugin(options: AssetManagerOptions): StudioPlugin;
6
+ export declare function getAssetRegistry(ctx: StudioPluginContext): AssetRegistry | undefined;
7
+ export declare function uploadAsset(ctx: StudioPluginContext, file: File): Promise<UploadResult>;
8
+ export declare function validateSelectedFile(file: File, options: Pick<AssetManagerOptions, "acceptedMimeTypes" | "maxFileSize">): void;
9
+ //# sourceMappingURL=plugin.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.cts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEX,YAAY,EACZ,mBAAmB,EAEnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAO5D,OAAO,KAAK,EACX,mBAAmB,EAEnB,aAAa,EACb,YAAY,EACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAwBhC,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,mBAAmB,GAC1B,YAAY,CAwDd;AAED,wBAAgB,gBAAgB,CAC/B,GAAG,EAAE,mBAAmB,GACtB,aAAa,GAAG,SAAS,CAG3B;AAED,wBAAsB,WAAW,CAChC,GAAG,EAAE,mBAAmB,EACxB,IAAI,EAAE,IAAI,GACR,OAAO,CAAC,YAAY,CAAC,CAwCvB;AAED,wBAAgB,oBAAoB,CACnC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,mBAAmB,GAAG,aAAa,CAAC,GACrE,IAAI,CAmBN"}
@@ -0,0 +1,9 @@
1
+ import type { StudioPlugin, StudioPluginContext } from "@anvilkit/core/types";
2
+ import { createAssetReference } from "./asset-reference.js";
3
+ import type { AssetManagerOptions, AssetRegistry, UploadResult } from "./types.js";
4
+ export { createAssetReference };
5
+ export declare function createAssetManagerPlugin(options: AssetManagerOptions): StudioPlugin;
6
+ export declare function getAssetRegistry(ctx: StudioPluginContext): AssetRegistry | undefined;
7
+ export declare function uploadAsset(ctx: StudioPluginContext, file: File): Promise<UploadResult>;
8
+ export declare function validateSelectedFile(file: File, options: Pick<AssetManagerOptions, "acceptedMimeTypes" | "maxFileSize">): void;
9
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEX,YAAY,EACZ,mBAAmB,EAEnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAO5D,OAAO,KAAK,EACX,mBAAmB,EAEnB,aAAa,EACb,YAAY,EACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAwBhC,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,mBAAmB,GAC1B,YAAY,CAwDd;AAED,wBAAgB,gBAAgB,CAC/B,GAAG,EAAE,mBAAmB,GACtB,aAAa,GAAG,SAAS,CAG3B;AAED,wBAAsB,WAAW,CAChC,GAAG,EAAE,mBAAmB,EACxB,IAAI,EAAE,IAAI,GACR,OAAO,CAAC,YAAY,CAAC,CAwCvB;AAED,wBAAgB,oBAAoB,CACnC,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,IAAI,CAAC,mBAAmB,EAAE,mBAAmB,GAAG,aAAa,CAAC,GACrE,IAAI,CAmBN"}
package/dist/plugin.js ADDED
@@ -0,0 +1,212 @@
1
+ import { createAssetReference } from "./asset-reference.js";
2
+ import { AssetValidationError } from "./errors.js";
3
+ import { uploadAssetAction } from "./header-action.js";
4
+ import { inferAssetKind } from "./infer-kind.js";
5
+ import { createAssetRegistry } from "./registry.js";
6
+ import { createIRAssetResolver } from "./resolver.js";
7
+ import { createStudioAssetSource } from "./studio-asset-source.js";
8
+ import { validateUploadResult } from "./validate-upload-result.js";
9
+ const META = {
10
+ id: "anvilkit-plugin-asset-manager",
11
+ name: "Asset Manager",
12
+ version: "1.0.0",
13
+ coreVersion: "^0.1.0-alpha",
14
+ description: "Headless asset upload plugin with host-provided persistence and a separate React UI subpath."
15
+ };
16
+ const stateByToken = new WeakMap();
17
+ const tokenByContext = new WeakMap();
18
+ function createAssetManagerPlugin(options) {
19
+ const token = {};
20
+ const registry = createAssetRegistry();
21
+ const normalizedOptions = normalizeOptions(options);
22
+ const assetResolver = createIRAssetResolver({
23
+ registry,
24
+ dataUrlAllowlistOptIn: normalizedOptions.dataUrlAllowlistOptIn,
25
+ allowMixedScriptHostnames: normalizedOptions.allowMixedScriptHostnames
26
+ });
27
+ return {
28
+ meta: META,
29
+ register (_ctx) {
30
+ const registration = {
31
+ meta: META,
32
+ headerActions: [
33
+ uploadAssetAction
34
+ ],
35
+ hooks: {
36
+ onInit (initCtx) {
37
+ const cleanups = [];
38
+ stateByToken.set(token, {
39
+ options: normalizedOptions,
40
+ registry,
41
+ cleanups
42
+ });
43
+ tokenByContext.set(initCtx, token);
44
+ initCtx.registerAssetResolver(assetResolver);
45
+ const studioAssetSource = createStudioAssetSource({
46
+ registry,
47
+ upload: (file)=>uploadAsset(initCtx, file),
48
+ ...normalizedOptions.getThumbnail ? {
49
+ getThumbnail: normalizedOptions.getThumbnail
50
+ } : {}
51
+ });
52
+ const unregisterAssetSource = initCtx.registerAssetSource?.(studioAssetSource);
53
+ if (void 0 !== unregisterAssetSource) cleanups.push(unregisterAssetSource);
54
+ },
55
+ onDestroy (destroyCtx) {
56
+ const state = stateByToken.get(token);
57
+ if (void 0 !== state) for (const cleanup of state.cleanups)cleanup();
58
+ tokenByContext.delete(destroyCtx);
59
+ stateByToken.delete(token);
60
+ }
61
+ }
62
+ };
63
+ return registration;
64
+ }
65
+ };
66
+ }
67
+ function getAssetRegistry(ctx) {
68
+ const token = tokenByContext.get(ctx);
69
+ return token ? stateByToken.get(token)?.registry : void 0;
70
+ }
71
+ async function uploadAsset(ctx, file) {
72
+ const state = getRuntimeState(ctx);
73
+ const { options, registry } = state;
74
+ try {
75
+ validateSelectedFile(file, options);
76
+ const uploadResult = await options.uploader(file);
77
+ const validated = validateUploadResult(mergeUploadMeta(uploadResult, file), options);
78
+ const tagged = withDerivedTags(validated, file);
79
+ const stored = registry.register(tagged);
80
+ dispatchAssetReference(ctx, stored);
81
+ ctx.emit("asset-manager:uploaded", {
82
+ asset: stored,
83
+ reference: createAssetReference(stored.id)
84
+ });
85
+ return stored;
86
+ } catch (error) {
87
+ const normalizedError = error instanceof AssetValidationError ? error : new AssetValidationError("UPLOAD_FAILED", error instanceof Error ? error.message : String(error), {
88
+ cause: error
89
+ });
90
+ ctx.emit("asset-manager:error", {
91
+ code: normalizedError.code,
92
+ message: normalizedError.message
93
+ });
94
+ ctx.log("error", normalizedError.message, {
95
+ code: normalizedError.code
96
+ });
97
+ throw normalizedError;
98
+ }
99
+ }
100
+ function validateSelectedFile(file, options) {
101
+ if (void 0 !== options.maxFileSize && file.size > options.maxFileSize) throw new AssetValidationError("FILE_TOO_LARGE", `File size ${file.size} bytes exceeds the configured maxFileSize of ${options.maxFileSize} bytes.`);
102
+ if (options.acceptedMimeTypes && options.acceptedMimeTypes.length > 0 && !mimeTypeMatches(file.type, options.acceptedMimeTypes)) {
103
+ const mimeType = file.type || "unknown";
104
+ throw new AssetValidationError("UNSUPPORTED_MIME_TYPE", `File MIME type "${mimeType}" is not in acceptedMimeTypes.`);
105
+ }
106
+ }
107
+ function getRuntimeState(ctx) {
108
+ const token = tokenByContext.get(ctx);
109
+ const state = token ? stateByToken.get(token) : void 0;
110
+ if (!state) throw new Error("createAssetManagerPlugin: uploadAsset called before the plugin runtime was initialized.");
111
+ return state;
112
+ }
113
+ function normalizeOptions(options) {
114
+ return {
115
+ ...options,
116
+ ...options.acceptedMimeTypes ? {
117
+ acceptedMimeTypes: Object.freeze([
118
+ ...options.acceptedMimeTypes
119
+ ])
120
+ } : {}
121
+ };
122
+ }
123
+ function withDerivedTags(asset, file) {
124
+ if (void 0 !== asset.tags && asset.tags.length > 0) return asset;
125
+ const tags = new Set();
126
+ const kind = inferAssetKind(asset);
127
+ if ("other" !== kind) tags.add(kind);
128
+ for (const token of filenameTokens(file.name)){
129
+ if (tags.size >= 3) break;
130
+ tags.add(token);
131
+ }
132
+ if (0 === tags.size) return asset;
133
+ return {
134
+ ...asset,
135
+ tags: Object.freeze([
136
+ ...tags
137
+ ])
138
+ };
139
+ }
140
+ function filenameTokens(name) {
141
+ const stem = name.replace(/\.[^./\\]+$/, "");
142
+ const tokens = stem.toLowerCase().split(/[^a-z0-9]+/).filter((token)=>token.length >= 2 && !/^\d+$/.test(token));
143
+ return tokens.slice(0, 2);
144
+ }
145
+ function mergeUploadMeta(result, file) {
146
+ const meta = {
147
+ size: file.size,
148
+ ...file.type ? {
149
+ mimeType: file.type
150
+ } : {},
151
+ ...result.meta ?? {}
152
+ };
153
+ return {
154
+ ...result,
155
+ ...void 0 === result.name && file.name ? {
156
+ name: file.name
157
+ } : {},
158
+ meta
159
+ };
160
+ }
161
+ function mimeTypeMatches(input, acceptedMimeTypes) {
162
+ if ("" === input) return false;
163
+ return acceptedMimeTypes.some((accepted)=>{
164
+ if (accepted.endsWith("/*")) {
165
+ const prefix = accepted.slice(0, accepted.length - 1);
166
+ return input.startsWith(prefix);
167
+ }
168
+ return input === accepted;
169
+ });
170
+ }
171
+ function dispatchAssetReference(ctx, asset) {
172
+ const currentData = ctx.getData();
173
+ const currentAssetsRaw = Array.isArray(currentData.assets) ? currentData.assets : [];
174
+ const assetEntry = toIRAsset(asset);
175
+ const nextAssets = [];
176
+ let replaced = false;
177
+ for (const entry of currentAssetsRaw)if (isRecord(entry) && "string" == typeof entry.id && entry.id === asset.id) {
178
+ nextAssets.push(assetEntry);
179
+ replaced = true;
180
+ } else nextAssets.push(entry);
181
+ if (!replaced) nextAssets.push(assetEntry);
182
+ const nextData = {
183
+ ...currentData,
184
+ assets: nextAssets
185
+ };
186
+ ctx.getPuckApi().dispatch({
187
+ type: "setData",
188
+ data: nextData
189
+ });
190
+ }
191
+ function toIRAsset(asset) {
192
+ return {
193
+ id: asset.id,
194
+ kind: inferIRAssetKind(asset.meta?.mimeType, asset.url),
195
+ url: createAssetReference(asset.id),
196
+ ...asset.meta ? {
197
+ meta: asset.meta
198
+ } : {}
199
+ };
200
+ }
201
+ function inferIRAssetKind(mimeType, url) {
202
+ if (mimeType?.startsWith("image/")) return "image";
203
+ if (mimeType?.startsWith("video/")) return "video";
204
+ if (mimeType?.startsWith("font/") || /\.(?:woff2?|ttf|otf)(?:$|[?#])/i.test(url)) return "font";
205
+ if ("text/css" === mimeType) return "style";
206
+ if ("application/javascript" === mimeType || "text/javascript" === mimeType) return "script";
207
+ return "other";
208
+ }
209
+ function isRecord(value) {
210
+ return "object" == typeof value && null !== value;
211
+ }
212
+ export { createAssetManagerPlugin, createAssetReference, getAssetRegistry, uploadAsset, validateSelectedFile };