@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.
- package/LICENSE +21 -0
- package/README.md +159 -0
- package/dist/adapters/data-url.cjs +78 -0
- package/dist/adapters/data-url.d.cts +6 -0
- package/dist/adapters/data-url.d.cts.map +1 -0
- package/dist/adapters/data-url.d.ts +6 -0
- package/dist/adapters/data-url.d.ts.map +1 -0
- package/dist/adapters/data-url.js +44 -0
- package/dist/adapters/extract-image-dimensions.cjs +69 -0
- package/dist/adapters/extract-image-dimensions.d.cts +21 -0
- package/dist/adapters/extract-image-dimensions.d.cts.map +1 -0
- package/dist/adapters/extract-image-dimensions.d.ts +21 -0
- package/dist/adapters/extract-image-dimensions.d.ts.map +1 -0
- package/dist/adapters/extract-image-dimensions.js +35 -0
- package/dist/adapters/in-memory.cjs +62 -0
- package/dist/adapters/in-memory.d.cts +3 -0
- package/dist/adapters/in-memory.d.cts.map +1 -0
- package/dist/adapters/in-memory.d.ts +3 -0
- package/dist/adapters/in-memory.d.ts.map +1 -0
- package/dist/adapters/in-memory.js +28 -0
- package/dist/adapters/s3-presigned.cjs +166 -0
- package/dist/adapters/s3-presigned.d.cts +59 -0
- package/dist/adapters/s3-presigned.d.cts.map +1 -0
- package/dist/adapters/s3-presigned.d.ts +59 -0
- package/dist/adapters/s3-presigned.d.ts.map +1 -0
- package/dist/adapters/s3-presigned.js +129 -0
- package/dist/asset-reference.cjs +38 -0
- package/dist/asset-reference.d.cts +10 -0
- package/dist/asset-reference.d.cts.map +1 -0
- package/dist/asset-reference.d.ts +10 -0
- package/dist/asset-reference.d.ts.map +1 -0
- package/dist/asset-reference.js +4 -0
- package/dist/csp.cjs +83 -0
- package/dist/csp.d.cts +45 -0
- package/dist/csp.d.cts.map +1 -0
- package/dist/csp.d.ts +45 -0
- package/dist/csp.d.ts.map +1 -0
- package/dist/csp.js +49 -0
- package/dist/errors.cjs +58 -0
- package/dist/errors.d.cts +15 -0
- package/dist/errors.d.cts.map +1 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +21 -0
- package/dist/header-action.cjs +44 -0
- package/dist/header-action.d.cts +3 -0
- package/dist/header-action.d.cts.map +1 -0
- package/dist/header-action.d.ts +3 -0
- package/dist/header-action.d.ts.map +1 -0
- package/dist/header-action.js +10 -0
- package/dist/index.cjs +90 -0
- package/dist/index.d.cts +18 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/infer-kind.cjs +45 -0
- package/dist/infer-kind.d.cts +8 -0
- package/dist/infer-kind.d.cts.map +1 -0
- package/dist/infer-kind.d.ts +8 -0
- package/dist/infer-kind.d.ts.map +1 -0
- package/dist/infer-kind.js +11 -0
- package/dist/plugin.cjs +258 -0
- package/dist/plugin.d.cts +9 -0
- package/dist/plugin.d.cts.map +1 -0
- package/dist/plugin.d.ts +9 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +212 -0
- package/dist/registry.cjs +198 -0
- package/dist/registry.d.cts +3 -0
- package/dist/registry.d.cts.map +1 -0
- package/dist/registry.d.ts +3 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +164 -0
- package/dist/resolver.cjs +185 -0
- package/dist/resolver.d.cts +10 -0
- package/dist/resolver.d.cts.map +1 -0
- package/dist/resolver.d.ts +10 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +148 -0
- package/dist/retry.cjs +123 -0
- package/dist/retry.d.cts +71 -0
- package/dist/retry.d.cts.map +1 -0
- package/dist/retry.d.ts +71 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +86 -0
- package/dist/studio-asset-source.cjs +211 -0
- package/dist/studio-asset-source.d.cts +52 -0
- package/dist/studio-asset-source.d.cts.map +1 -0
- package/dist/studio-asset-source.d.ts +52 -0
- package/dist/studio-asset-source.d.ts.map +1 -0
- package/dist/studio-asset-source.js +171 -0
- package/dist/testing/index.cjs +66 -0
- package/dist/testing/index.d.cts +24 -0
- package/dist/testing/index.d.cts.map +1 -0
- package/dist/testing/index.d.ts +24 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +29 -0
- package/dist/types.cjs +18 -0
- package/dist/types.d.cts +132 -0
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.ts +132 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +0 -0
- package/dist/ui/AssetBrowser.cjs +271 -0
- package/dist/ui/AssetBrowser.d.cts +45 -0
- package/dist/ui/AssetBrowser.d.cts.map +1 -0
- package/dist/ui/AssetBrowser.d.ts +45 -0
- package/dist/ui/AssetBrowser.d.ts.map +1 -0
- package/dist/ui/AssetBrowser.js +237 -0
- package/dist/ui/AssetCommandPalette.cjs +135 -0
- package/dist/ui/AssetCommandPalette.d.cts +21 -0
- package/dist/ui/AssetCommandPalette.d.cts.map +1 -0
- package/dist/ui/AssetCommandPalette.d.ts +21 -0
- package/dist/ui/AssetCommandPalette.d.ts.map +1 -0
- package/dist/ui/AssetCommandPalette.js +101 -0
- package/dist/ui/AssetManagerUI.cjs +169 -0
- package/dist/ui/AssetManagerUI.d.cts +15 -0
- package/dist/ui/AssetManagerUI.d.cts.map +1 -0
- package/dist/ui/AssetManagerUI.d.ts +15 -0
- package/dist/ui/AssetManagerUI.d.ts.map +1 -0
- package/dist/ui/AssetManagerUI.js +135 -0
- package/dist/ui/DeleteAssetDialog.cjs +70 -0
- package/dist/ui/DeleteAssetDialog.d.cts +22 -0
- package/dist/ui/DeleteAssetDialog.d.cts.map +1 -0
- package/dist/ui/DeleteAssetDialog.d.ts +22 -0
- package/dist/ui/DeleteAssetDialog.d.ts.map +1 -0
- package/dist/ui/DeleteAssetDialog.js +36 -0
- package/dist/ui/MetadataPanel.cjs +147 -0
- package/dist/ui/MetadataPanel.d.cts +21 -0
- package/dist/ui/MetadataPanel.d.cts.map +1 -0
- package/dist/ui/MetadataPanel.d.ts +21 -0
- package/dist/ui/MetadataPanel.d.ts.map +1 -0
- package/dist/ui/MetadataPanel.js +113 -0
- package/dist/ui/ReplaceAssetDialog.cjs +125 -0
- package/dist/ui/ReplaceAssetDialog.d.cts +14 -0
- package/dist/ui/ReplaceAssetDialog.d.cts.map +1 -0
- package/dist/ui/ReplaceAssetDialog.d.ts +14 -0
- package/dist/ui/ReplaceAssetDialog.d.ts.map +1 -0
- package/dist/ui/ReplaceAssetDialog.js +91 -0
- package/dist/ui/UploadButton.cjs +189 -0
- package/dist/ui/UploadButton.d.cts +17 -0
- package/dist/ui/UploadButton.d.cts.map +1 -0
- package/dist/ui/UploadButton.d.ts +17 -0
- package/dist/ui/UploadButton.d.ts.map +1 -0
- package/dist/ui/UploadButton.js +155 -0
- package/dist/ui/index.cjs +60 -0
- package/dist/ui/index.d.cts +15 -0
- package/dist/ui/index.d.cts.map +1 -0
- package/dist/ui/index.d.ts +15 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +7 -0
- package/dist/validate-upload-result.cjs +149 -0
- package/dist/validate-upload-result.d.cts +9 -0
- package/dist/validate-upload-result.d.cts.map +1 -0
- package/dist/validate-upload-result.d.ts +9 -0
- package/dist/validate-upload-result.d.ts.map +1 -0
- package/dist/validate-upload-result.js +115 -0
- package/package.json +131 -0
package/dist/plugin.cjs
ADDED
|
@@ -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"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -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 };
|