emdash 0.40.0 → 0.40.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/dist/api/route-utils.mjs +6 -6
- package/dist/api/schemas/index.d.mts +1 -1
- package/dist/{api-cFFZpyS0.mjs → api-CuyNEHZG.mjs} +3 -3
- package/dist/{api-cFFZpyS0.mjs.map → api-CuyNEHZG.mjs.map} +1 -1
- package/dist/{apply-CoT603H3.mjs → apply-npY1TAhG.mjs} +237 -34
- package/dist/apply-npY1TAhG.mjs.map +1 -0
- package/dist/astro/index.d.mts +3 -3
- package/dist/astro/index.mjs +9 -9
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/middleware/auth.d.mts +3 -3
- package/dist/astro/middleware/redirect.mjs +1 -1
- package/dist/astro/middleware.d.mts +3 -3
- package/dist/astro/middleware.d.mts.map +1 -1
- package/dist/astro/middleware.mjs +19 -18
- package/dist/astro/middleware.mjs.map +1 -1
- package/dist/astro/routes/api/admin/media-usage/progress.mjs +2 -2
- package/dist/astro/routes/api/admin/media-usage/repair.mjs +2 -2
- package/dist/astro/routes/api/admin/plugins/_id_/index.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/_id_/settings.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/_id_/uninstall.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/_id_/update.mjs +11 -11
- package/dist/astro/routes/api/admin/plugins/index.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/index.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/marketplace/_id_/install.mjs +11 -11
- package/dist/astro/routes/api/admin/plugins/marketplace/index.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/registry/_id_/uninstall.mjs +8 -8
- package/dist/astro/routes/api/admin/plugins/registry/_id_/update.mjs +12 -12
- package/dist/astro/routes/api/admin/plugins/registry/install.mjs +9 -9
- package/dist/astro/routes/api/admin/plugins/registry/verify.mjs +9 -9
- package/dist/astro/routes/api/admin/plugins/updates.mjs +8 -8
- package/dist/astro/routes/api/admin/themes/marketplace/_id_/index.mjs +8 -8
- package/dist/astro/routes/api/admin/themes/marketplace/index.mjs +8 -8
- package/dist/astro/routes/api/admin/transfer/approvals/_id_/approve.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/approvals/_id_/deny.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/approvals/index.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/capabilities.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/_id_/advance.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/_id_/archive.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/_id_/files/_...path_.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/_id_/index.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/_id_/manifest.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/exports/index.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/abandon.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/advance.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/analyze.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/cancel.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/execute.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/files/_...path_.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/index.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/missing.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/plan.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/_id_/receipt.mjs +3 -3
- package/dist/astro/routes/api/admin/transfer/imports/index.mjs +3 -3
- package/dist/astro/routes/api/dashboard.mjs +2 -2
- package/dist/astro/routes/api/health.mjs +1 -1
- package/dist/astro/routes/api/import/probe.mjs +3 -3
- package/dist/astro/routes/api/import/wordpress/execute.d.mts +3 -3
- package/dist/astro/routes/api/import/wordpress/media.d.mts +3 -3
- package/dist/astro/routes/api/import/wordpress/rewrite-urls.d.mts +3 -3
- package/dist/astro/routes/api/import/wordpress-plugin/analyze.mjs +3 -3
- package/dist/astro/routes/api/import/wordpress-plugin/execute.d.mts +3 -3
- package/dist/astro/routes/api/import/wordpress-plugin/execute.mjs +3 -3
- package/dist/astro/routes/api/manifest.mjs +1 -1
- package/dist/astro/routes/api/mcp.mjs +13 -33
- package/dist/astro/routes/api/mcp.mjs.map +1 -1
- package/dist/astro/routes/api/media/_id_/confirm.mjs +1 -1
- package/dist/astro/routes/api/media/_id_/upload.mjs +1 -1
- package/dist/astro/routes/api/media/_id_/usage.mjs +2 -2
- package/dist/astro/routes/api/media/_id_.mjs +2 -2
- package/dist/astro/routes/api/media/asset/_id_/_filename_.mjs +1 -1
- package/dist/astro/routes/api/media/upload-url.mjs +1 -1
- package/dist/astro/routes/api/media.mjs +2 -2
- package/dist/astro/routes/api/redirects/404s/index.mjs +2 -2
- package/dist/astro/routes/api/redirects/404s/summary.mjs +2 -2
- package/dist/astro/routes/api/redirects/_id_.mjs +2 -2
- package/dist/astro/routes/api/redirects/index.mjs +2 -2
- package/dist/astro/routes/api/schema/block-types/_slug_/index.mjs +8 -8
- package/dist/astro/routes/api/schema/block-types/_slug_/versions/_version_/activate.mjs +8 -8
- package/dist/astro/routes/api/schema/block-types/index.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/_slug_/fields/_fieldSlug_.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/_slug_/fields/index.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/_slug_/fields/reorder.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/_slug_/index.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/index.mjs +8 -8
- package/dist/astro/routes/api/schema/collections/reorder.mjs +8 -8
- package/dist/astro/routes/api/schema/orphans/_slug_.mjs +8 -8
- package/dist/astro/routes/api/schema/orphans/index.mjs +8 -8
- package/dist/astro/routes/api/settings/backups/archives/_name_.mjs +2 -2
- package/dist/astro/routes/api/settings/backups/archives/index.mjs +2 -2
- package/dist/astro/routes/api/settings/backups/export.mjs +2 -2
- package/dist/astro/routes/api/settings/backups/index.mjs +2 -2
- package/dist/astro/routes/api/settings.mjs +3 -3
- package/dist/astro/routes/api/setup/dev-bypass.mjs +4 -4
- package/dist/astro/routes/api/setup/index.d.mts.map +1 -1
- package/dist/astro/routes/api/setup/index.mjs +35 -10
- package/dist/astro/routes/api/setup/index.mjs.map +1 -1
- package/dist/astro/routes/api/snapshot.mjs +2 -2
- package/dist/astro/routes/api/snapshot.mjs.map +1 -1
- package/dist/astro/routes/robots.txt.mjs +2 -2
- package/dist/astro/routes/sitemap-_collection_.xml.mjs +2 -2
- package/dist/astro/routes/sitemap.xml.mjs +2 -2
- package/dist/astro/types.d.mts +7 -3
- package/dist/astro/types.d.mts.map +1 -1
- package/dist/{backup-CBu0LrXf.mjs → backup-BQRj3afm.mjs} +3 -3
- package/dist/{backup-CBu0LrXf.mjs.map → backup-BQRj3afm.mjs.map} +1 -1
- package/dist/cli/index.mjs +6 -6
- package/dist/client/index.d.mts +14 -14
- package/dist/{config-5ii2haYu.d.mts → config-BjEm_3xL.d.mts} +25 -16
- package/dist/config-BjEm_3xL.d.mts.map +1 -0
- package/dist/{context-BeGccdUX.mjs → context-BV_7t-Ua.mjs} +6 -6
- package/dist/{context-BeGccdUX.mjs.map → context-BV_7t-Ua.mjs.map} +1 -1
- package/dist/{dashboard-DjrB-xxZ.mjs → dashboard-BP-iJqpd.mjs} +2 -2
- package/dist/{dashboard-DjrB-xxZ.mjs.map → dashboard-BP-iJqpd.mjs.map} +1 -1
- package/dist/db/libsql-migrations.mjs +2 -2
- package/dist/db/postgres-migrations.mjs +2 -2
- package/dist/db/sqlite-migrations.mjs +2 -2
- package/dist/{direct-executor-BmuFJJZV.mjs → direct-executor-D2lNadwo.mjs} +2 -2
- package/dist/{direct-executor-BmuFJJZV.mjs.map → direct-executor-D2lNadwo.mjs.map} +1 -1
- package/dist/{emdash-runtime-DAjghyEG.mjs → emdash-runtime-CtfnZmPi.mjs} +56 -56
- package/dist/emdash-runtime-CtfnZmPi.mjs.map +1 -0
- package/dist/{entry-lock-DVhCt49A.d.mts → entry-lock-Cf5OoIgN.d.mts} +18 -18
- package/dist/{entry-lock-DVhCt49A.d.mts.map → entry-lock-Cf5OoIgN.d.mts.map} +1 -1
- package/dist/{hreflang-DMFBL2eb.mjs → hreflang-CbVI4paR.mjs} +2 -2
- package/dist/{hreflang-DMFBL2eb.mjs.map → hreflang-CbVI4paR.mjs.map} +1 -1
- package/dist/{identity-IyNWwXr6.mjs → identity-Be5kzutJ.mjs} +2 -2
- package/dist/{identity-IyNWwXr6.mjs.map → identity-Be5kzutJ.mjs.map} +1 -1
- package/dist/{import-C3JZplWq.mjs → import-YzfKCaco.mjs} +2 -2
- package/dist/{import-C3JZplWq.mjs.map → import-YzfKCaco.mjs.map} +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +16 -16
- package/dist/{manifest-builder-B8zR-73J.mjs → manifest-builder-BmSAOOEF.mjs} +2 -2
- package/dist/{manifest-builder-B8zR-73J.mjs.map → manifest-builder-BmSAOOEF.mjs.map} +1 -1
- package/dist/media/local-runtime.d.mts +3 -3
- package/dist/media/local-runtime.mjs +3 -3
- package/dist/{media-Qp5S3eH3.mjs → media-CPXTbb1R.mjs} +20 -2
- package/dist/media-CPXTbb1R.mjs.map +1 -0
- package/dist/{media-h7tOiDLj.mjs → media-JXRWDhw3.mjs} +77 -4
- package/dist/media-JXRWDhw3.mjs.map +1 -0
- package/dist/{media-usage-My-Vi-ex.mjs → media-usage-De-zeR9E.mjs} +2 -2
- package/dist/{media-usage-My-Vi-ex.mjs.map → media-usage-De-zeR9E.mjs.map} +1 -1
- package/dist/{menus-TsSGmSR4.mjs → menus-Cm68rsAq.mjs} +4 -4
- package/dist/{menus-TsSGmSR4.mjs.map → menus-Cm68rsAq.mjs.map} +1 -1
- package/dist/migrations/index.mjs +2 -2
- package/dist/page/index.mjs +2 -2
- package/dist/plugin-test-runtime.d.mts +3 -3
- package/dist/plugin-test-runtime.mjs +18 -18
- package/dist/plugin-utils.d.mts +3 -3
- package/dist/plugins/adapt-sandbox-entry.d.mts +3 -3
- package/dist/plugins/host.d.mts +1 -1
- package/dist/plugins/host.mjs +6 -6
- package/dist/{redirect-iJf5fltD.mjs → redirect-B29UjdUn.mjs} +10 -14
- package/dist/redirect-B29UjdUn.mjs.map +1 -0
- package/dist/{redirects-CYu1u1aK.mjs → redirects-Ca2MPfEG.mjs} +2 -2
- package/dist/{redirects-CYu1u1aK.mjs.map → redirects-Ca2MPfEG.mjs.map} +1 -1
- package/dist/{routes-CUsM40Cc.mjs → routes-vrR95WR6.mjs} +2 -2
- package/dist/{routes-CUsM40Cc.mjs.map → routes-vrR95WR6.mjs.map} +1 -1
- package/dist/runtime.d.mts +3 -3
- package/dist/seed/index.mjs +4 -4
- package/dist/seo/index.mjs +3 -3
- package/dist/{settings-CgjRWGW9.mjs → settings-B4vDl1fK.mjs} +2 -2
- package/dist/{settings-CgjRWGW9.mjs.map → settings-B4vDl1fK.mjs.map} +1 -1
- package/dist/{settings-2xNYr90o.mjs → settings-Dyj6v8C4.mjs} +2 -2
- package/dist/{settings-2xNYr90o.mjs.map → settings-Dyj6v8C4.mjs.map} +1 -1
- package/dist/{snapshot-CugwUqKQ.mjs → snapshot-PawoX-WG.mjs} +180 -3
- package/dist/snapshot-PawoX-WG.mjs.map +1 -0
- package/dist/{storage-query-Bdv7A62k.d.mts → storage-query-FXJH5pI8.d.mts} +6 -1
- package/dist/storage-query-FXJH5pI8.d.mts.map +1 -0
- package/dist/{transfer-BhUCrgDc.mjs → transfer-DgeCvHnj.mjs} +4 -4
- package/dist/{transfer-BhUCrgDc.mjs.map → transfer-DgeCvHnj.mjs.map} +1 -1
- package/dist/validate-CM79QXYE.d.mts.map +1 -1
- package/dist/version-DXsxRrob.mjs +7 -0
- package/dist/{version-DUQDHgrf.mjs.map → version-DXsxRrob.mjs.map} +1 -1
- package/package.json +5 -5
- package/src/api/handlers/index.ts +1 -0
- package/src/api/handlers/media-upload.ts +17 -71
- package/src/api/handlers/media.ts +97 -3
- package/src/api/handlers/snapshot.ts +257 -3
- package/src/astro/integration/vite-config.ts +10 -4
- package/src/astro/middleware.ts +1 -0
- package/src/astro/routes/api/setup/index.ts +66 -23
- package/src/astro/routes/api/snapshot.ts +2 -1
- package/src/astro/types.ts +5 -0
- package/src/cleanup.ts +10 -0
- package/src/components/Columns.astro +3 -1
- package/src/components/Cover.astro +3 -1
- package/src/database/repositories/media.ts +40 -1
- package/src/database/repositories/redirect.ts +17 -34
- package/src/emdash-runtime.ts +28 -0
- package/src/index.ts +1 -0
- package/src/mcp/server.ts +13 -48
- package/src/seed/apply.ts +322 -41
- package/dist/apply-CoT603H3.mjs.map +0 -1
- package/dist/config-5ii2haYu.d.mts.map +0 -1
- package/dist/emdash-runtime-DAjghyEG.mjs.map +0 -1
- package/dist/media-Qp5S3eH3.mjs.map +0 -1
- package/dist/media-h7tOiDLj.mjs.map +0 -1
- package/dist/redirect-iJf5fltD.mjs.map +0 -1
- package/dist/snapshot-CugwUqKQ.mjs.map +0 -1
- package/dist/storage-query-Bdv7A62k.d.mts.map +0 -1
- package/dist/version-DUQDHgrf.mjs +0 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-CM79QXYE.d.mts","names":[],"sources":["../src/seed/types.ts","../src/seed/apply.ts","../src/seed/default.ts","../src/seed/load.ts","../src/seed/validate.ts"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"validate-CM79QXYE.d.mts","names":[],"sources":["../src/seed/types.ts","../src/seed/apply.ts","../src/seed/default.ts","../src/seed/load.ts","../src/seed/validate.ts"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version-
|
|
1
|
+
{"version":3,"file":"version-DXsxRrob.mjs","names":[],"sources":["../src/version.ts"],"sourcesContent":["/**\n * Build-time version constants, replaced by tsdown/Vite `define`.\n * Falls back to \"dev\" when running uncompiled (tests, dev).\n */\n\ndeclare const __EMDASH_VERSION__: string;\ndeclare const __EMDASH_COMMIT__: string;\n\nexport const VERSION: string =\n\ttypeof __EMDASH_VERSION__ !== \"undefined\" ? __EMDASH_VERSION__ : \"dev\";\n\nexport const COMMIT: string = typeof __EMDASH_COMMIT__ !== \"undefined\" ? __EMDASH_COMMIT__ : \"dev\";\n"],"mappings":";AAQA,MAAa;AAGb,MAAa"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emdash",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.1",
|
|
4
4
|
"description": "Astro-native CMS with WordPress migration support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -288,10 +288,10 @@
|
|
|
288
288
|
"upng-js": "^2.1.0",
|
|
289
289
|
"zod": "4.5.4",
|
|
290
290
|
"@atcute/client": "^5.0.0",
|
|
291
|
-
"@emdash-cms/admin": "0.40.
|
|
292
|
-
"@emdash-cms/auth": "0.40.
|
|
293
|
-
"@emdash-cms/blocks": "0.40.
|
|
294
|
-
"@emdash-cms/gutenberg-to-portable-text": "0.40.
|
|
291
|
+
"@emdash-cms/admin": "0.40.1",
|
|
292
|
+
"@emdash-cms/auth": "0.40.1",
|
|
293
|
+
"@emdash-cms/blocks": "0.40.1",
|
|
294
|
+
"@emdash-cms/gutenberg-to-portable-text": "0.40.1",
|
|
295
295
|
"@emdash-cms/plugin-types": "0.4.0",
|
|
296
296
|
"@emdash-cms/registry-client": "0.6.1",
|
|
297
297
|
"@emdash-cms/registry-lexicons": "0.6.0",
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Programmatic media upload handler (MCP `media_upload` tool).
|
|
3
3
|
*
|
|
4
|
-
* Accepts file bytes as base64
|
|
5
|
-
*
|
|
6
|
-
* upload route: allowlist + size validation, content-hash deduplication,
|
|
4
|
+
* Accepts file bytes as base64, then runs the same pipeline as the multipart
|
|
5
|
+
* REST upload route: allowlist + size validation, content-hash deduplication,
|
|
7
6
|
* storage upload, image metadata enrichment, and record creation.
|
|
8
7
|
*/
|
|
9
8
|
|
|
@@ -16,7 +15,6 @@ import { MediaRepository, type MediaItem } from "../../database/repositories/med
|
|
|
16
15
|
import type { Database } from "../../database/types.js";
|
|
17
16
|
import { enrichImageMetadata } from "../../media/enrich.js";
|
|
18
17
|
import { matchesMimeAllowlist, normalizeMime } from "../../media/mime.js";
|
|
19
|
-
import { SsrfError, ssrfSafeFetch } from "../../security/ssrf.js";
|
|
20
18
|
import type { Storage } from "../../storage/types.js";
|
|
21
19
|
import { decodeBase64Bytes } from "../../utils/base64.js";
|
|
22
20
|
import { computeContentHash } from "../../utils/hash.js";
|
|
@@ -27,15 +25,10 @@ import { GLOBAL_UPLOAD_ALLOWLIST } from "./media-allowlist.js";
|
|
|
27
25
|
export interface MediaUploadInput {
|
|
28
26
|
/** Original filename (e.g. 'logo.png'); the extension is kept on the storage key. */
|
|
29
27
|
filename: string;
|
|
30
|
-
/** Base64-encoded file contents.
|
|
31
|
-
base64
|
|
32
|
-
/**
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* MIME type. Required with `base64`; optional with `url` (falls back to
|
|
36
|
-
* the response's Content-Type header).
|
|
37
|
-
*/
|
|
38
|
-
contentType?: string;
|
|
28
|
+
/** Base64-encoded file contents. */
|
|
29
|
+
base64: string;
|
|
30
|
+
/** MIME type of the decoded bytes. */
|
|
31
|
+
contentType: string;
|
|
39
32
|
/** Alt text stored on the media record. */
|
|
40
33
|
alt?: string;
|
|
41
34
|
authorId?: string;
|
|
@@ -65,75 +58,28 @@ function withUrl(item: MediaItem): MediaItem & { url: string } {
|
|
|
65
58
|
return { ...item, url: `/_emdash/api/media/file/${item.storageKey}` };
|
|
66
59
|
}
|
|
67
60
|
|
|
68
|
-
/**
|
|
69
|
-
function bareMime(headerValue: string): string {
|
|
70
|
-
return (headerValue.split(";")[0] ?? "").trim();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Acquire the file bytes and MIME type from either the base64 payload or
|
|
75
|
-
* the external URL. Returns an error result on any validation failure.
|
|
76
|
-
*/
|
|
61
|
+
/** Decode the file bytes after rejecting an oversized encoded payload. */
|
|
77
62
|
async function acquireBytes(
|
|
78
63
|
input: MediaUploadInput,
|
|
79
64
|
maxUploadSize: number,
|
|
80
65
|
): Promise<{ bytes: Uint8Array; mimeType: string } | MediaUploadResult> {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
// Cheap size precheck on the encoded string (decoded size is ~3/4 of
|
|
86
|
-
// the base64 length) before allocating the decoded buffer.
|
|
87
|
-
if ((input.base64.length * 3) / 4 > maxUploadSize) {
|
|
88
|
-
return fail(
|
|
89
|
-
"PAYLOAD_TOO_LARGE",
|
|
90
|
-
`File exceeds maximum size of ${formatFileSize(maxUploadSize)}`,
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
try {
|
|
94
|
-
return { bytes: decodeBase64Bytes(input.base64), mimeType: input.contentType };
|
|
95
|
-
} catch {
|
|
96
|
-
return fail("VALIDATION_ERROR", "Invalid base64 data");
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// url mode — the caller guarantees exactly one source, so url is set here
|
|
101
|
-
const url = input.url;
|
|
102
|
-
if (!url) {
|
|
103
|
-
return fail("VALIDATION_ERROR", "Provide exactly one of 'base64' or 'url'");
|
|
104
|
-
}
|
|
105
|
-
let response: Response;
|
|
106
|
-
try {
|
|
107
|
-
response = await ssrfSafeFetch(url, { headers: { accept: "*/*" } });
|
|
108
|
-
} catch (error) {
|
|
109
|
-
if (error instanceof SsrfError) {
|
|
110
|
-
return fail("VALIDATION_ERROR", `URL not allowed: ${error.message}`);
|
|
111
|
-
}
|
|
112
|
-
return fail("FETCH_ERROR", "Failed to fetch file from URL");
|
|
113
|
-
}
|
|
114
|
-
if (!response.ok) {
|
|
115
|
-
return fail("FETCH_ERROR", `Failed to fetch file from URL (HTTP ${response.status})`);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const contentLength = response.headers.get("Content-Length");
|
|
119
|
-
if (contentLength && parseInt(contentLength, 10) > maxUploadSize) {
|
|
66
|
+
// Cheap size precheck on the encoded string (decoded size is ~3/4 of
|
|
67
|
+
// the base64 length) before allocating the decoded buffer.
|
|
68
|
+
if ((input.base64.length * 3) / 4 > maxUploadSize) {
|
|
120
69
|
return fail(
|
|
121
70
|
"PAYLOAD_TOO_LARGE",
|
|
122
71
|
`File exceeds maximum size of ${formatFileSize(maxUploadSize)}`,
|
|
123
72
|
);
|
|
124
73
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
return fail("VALIDATION_ERROR", "
|
|
74
|
+
try {
|
|
75
|
+
return { bytes: decodeBase64Bytes(input.base64), mimeType: input.contentType };
|
|
76
|
+
} catch {
|
|
77
|
+
return fail("VALIDATION_ERROR", "Invalid base64 data");
|
|
129
78
|
}
|
|
130
|
-
|
|
131
|
-
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
132
|
-
return { bytes, mimeType };
|
|
133
79
|
}
|
|
134
80
|
|
|
135
81
|
/**
|
|
136
|
-
* Upload a media file from base64 data
|
|
82
|
+
* Upload a media file from base64 data.
|
|
137
83
|
*
|
|
138
84
|
* Mirrors the REST `POST /_emdash/api/media` route: global MIME allowlist,
|
|
139
85
|
* size limit, content-hash dedupe (returns the existing item with
|
|
@@ -146,8 +92,8 @@ export async function handleMediaUpload(
|
|
|
146
92
|
input: MediaUploadInput,
|
|
147
93
|
hooks: MediaUploadHooks = {},
|
|
148
94
|
): Promise<MediaUploadResult> {
|
|
149
|
-
if (!input.base64
|
|
150
|
-
return fail("VALIDATION_ERROR", "
|
|
95
|
+
if (!input.base64 || !input.contentType) {
|
|
96
|
+
return fail("VALIDATION_ERROR", "base64 and contentType are required");
|
|
151
97
|
}
|
|
152
98
|
|
|
153
99
|
const rawMax = input.maxUploadSize ?? DEFAULT_MAX_UPLOAD_SIZE;
|
|
@@ -173,6 +173,91 @@ export async function handleMediaCreate(
|
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Confirm an upload minted by the signed upload URL endpoint.
|
|
178
|
+
*
|
|
179
|
+
* The storage key must belong to a pending media row created for the same
|
|
180
|
+
* user; arbitrary keys never reach storage this way.
|
|
181
|
+
*/
|
|
182
|
+
export async function handleMediaRegisterUpload(
|
|
183
|
+
db: Kysely<Database>,
|
|
184
|
+
storage: Storage,
|
|
185
|
+
input: {
|
|
186
|
+
storageKey: string;
|
|
187
|
+
authorId?: string;
|
|
188
|
+
},
|
|
189
|
+
): Promise<ApiResult<MediaResponse>> {
|
|
190
|
+
const invalidKey: ApiResult<never> = {
|
|
191
|
+
success: false,
|
|
192
|
+
error: {
|
|
193
|
+
code: "INVALID_STORAGE_KEY",
|
|
194
|
+
message:
|
|
195
|
+
"storageKey does not match a pending upload created for this user; request a signed upload URL first",
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
try {
|
|
199
|
+
const repo = new MediaRepository(db);
|
|
200
|
+
const pending = await repo.findPendingByStorageKey(input.storageKey);
|
|
201
|
+
if (!pending) return invalidKey;
|
|
202
|
+
if ((pending.authorId ?? null) !== (input.authorId ?? null)) return invalidKey;
|
|
203
|
+
if (pending.size === null) {
|
|
204
|
+
return {
|
|
205
|
+
success: false,
|
|
206
|
+
error: {
|
|
207
|
+
code: "INVALID_STATE",
|
|
208
|
+
message: "Pending upload has no expected size",
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
if (!(await storage.exists(input.storageKey))) {
|
|
213
|
+
return {
|
|
214
|
+
success: false,
|
|
215
|
+
error: { code: "FILE_NOT_FOUND", message: "File was not uploaded to storage" },
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const storedFile = await storage.download(input.storageKey);
|
|
220
|
+
try {
|
|
221
|
+
if (storedFile.size !== pending.size) {
|
|
222
|
+
return {
|
|
223
|
+
success: false,
|
|
224
|
+
error: {
|
|
225
|
+
code: "UPLOAD_SIZE_MISMATCH",
|
|
226
|
+
message: "Stored file size does not match the pending upload",
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
} finally {
|
|
231
|
+
try {
|
|
232
|
+
await storedFile.body.cancel();
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.error("[media] upload confirmation cancellation failed:", error);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const item = await repo.confirmUpload(pending.id, undefined, input.storageKey);
|
|
239
|
+
if (!item) return invalidKey;
|
|
240
|
+
try {
|
|
241
|
+
await repo.deleteUploadAttempt(input.storageKey);
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.error("[media] upload attempt cleanup failed:", error);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
success: true,
|
|
248
|
+
data: { item },
|
|
249
|
+
};
|
|
250
|
+
} catch {
|
|
251
|
+
return {
|
|
252
|
+
success: false,
|
|
253
|
+
error: {
|
|
254
|
+
code: "MEDIA_REGISTER_ERROR",
|
|
255
|
+
message: "Failed to register the upload",
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
176
261
|
/**
|
|
177
262
|
* Update media metadata
|
|
178
263
|
*/
|
|
@@ -291,9 +376,18 @@ export async function handleMediaDelete(
|
|
|
291
376
|
const storageKey = await repo.deleteWithStorageKey(id);
|
|
292
377
|
if (!storageKey) return notFound;
|
|
293
378
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
379
|
+
let storageDeleted = false;
|
|
380
|
+
if (storage) {
|
|
381
|
+
if (await repo.isStorageKeyReferenced(storageKey)) {
|
|
382
|
+
// A legacy or concurrently-created row still owns this object. Remove
|
|
383
|
+
// the cleanup marker created above so scheduled cleanup cannot delete it.
|
|
384
|
+
await repo.deleteUploadAttempt(storageKey);
|
|
385
|
+
} else {
|
|
386
|
+
storageDeleted = await removeUploadAttempt(storage, repo, storageKey, {
|
|
387
|
+
allowUntracked: true,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
297
391
|
|
|
298
392
|
return {
|
|
299
393
|
success: true,
|
|
@@ -80,6 +80,8 @@ export function parsePreviewSignatureHeader(
|
|
|
80
80
|
// ── Media URL rewriting ─────────────────────────────────────────
|
|
81
81
|
|
|
82
82
|
const MEDIA_FILE_PREFIX = "/_emdash/api/media/file/";
|
|
83
|
+
const MEDIA_FILE_SUFFIX = /[?#]/;
|
|
84
|
+
const EXTERNAL_OR_ROOT_URL = /^(?:[a-z][a-z0-9+.-]*:|\/)/i;
|
|
83
85
|
|
|
84
86
|
/**
|
|
85
87
|
* Parse a JSON string value and inject `src` for local media objects.
|
|
@@ -179,12 +181,59 @@ const SYSTEM_TABLES = [
|
|
|
179
181
|
"_emdash_widgets",
|
|
180
182
|
"_emdash_seo",
|
|
181
183
|
"_emdash_migrations",
|
|
182
|
-
"taxonomies",
|
|
183
184
|
"content_taxonomies",
|
|
184
|
-
"
|
|
185
|
+
"taxonomies",
|
|
185
186
|
"options",
|
|
187
|
+
"media",
|
|
186
188
|
"revisions",
|
|
187
|
-
];
|
|
189
|
+
] as const;
|
|
190
|
+
|
|
191
|
+
type SystemTable = (typeof SYSTEM_TABLES)[number];
|
|
192
|
+
type PublishedTablePolicy = "full" | "content-linked" | "omit";
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Published snapshots are available to preview-signature callers, so every
|
|
196
|
+
* system table must be classified explicitly. Adding a snapshot table without
|
|
197
|
+
* choosing a policy is a type error rather than an accidental data export.
|
|
198
|
+
*/
|
|
199
|
+
const PUBLISHED_TABLE_POLICIES: Record<SystemTable, PublishedTablePolicy> = {
|
|
200
|
+
_emdash_block_types: "full",
|
|
201
|
+
_emdash_block_type_versions: "full",
|
|
202
|
+
_emdash_collections: "full",
|
|
203
|
+
_emdash_fields: "full",
|
|
204
|
+
_emdash_taxonomy_defs: "full",
|
|
205
|
+
_emdash_taxonomy_def_groups: "full",
|
|
206
|
+
_emdash_menus: "full",
|
|
207
|
+
_emdash_menu_items: "content-linked",
|
|
208
|
+
_emdash_sections: "full",
|
|
209
|
+
_emdash_widget_areas: "full",
|
|
210
|
+
_emdash_widgets: "full",
|
|
211
|
+
_emdash_seo: "content-linked",
|
|
212
|
+
_emdash_migrations: "full",
|
|
213
|
+
content_taxonomies: "content-linked",
|
|
214
|
+
taxonomies: "content-linked",
|
|
215
|
+
options: "content-linked",
|
|
216
|
+
media: "content-linked",
|
|
217
|
+
revisions: "omit",
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const SYSTEM_TABLE_SET: ReadonlySet<string> = new Set(SYSTEM_TABLES);
|
|
221
|
+
|
|
222
|
+
function isSystemTable(tableName: string): tableName is SystemTable {
|
|
223
|
+
return SYSTEM_TABLE_SET.has(tableName);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
interface PublishedContentReferences {
|
|
227
|
+
ids: Set<string>;
|
|
228
|
+
translationGroups: Set<string>;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
interface PublishedProjection {
|
|
232
|
+
content: Map<string, PublishedContentReferences>;
|
|
233
|
+
taxonomyGroups: Set<string>;
|
|
234
|
+
mediaIds: Set<string>;
|
|
235
|
+
mediaStorageKeys: Set<string>;
|
|
236
|
+
}
|
|
188
237
|
|
|
189
238
|
/**
|
|
190
239
|
* Table name prefixes excluded from snapshots (auth/security data).
|
|
@@ -256,6 +305,191 @@ function normalizeRows(
|
|
|
256
305
|
return rows;
|
|
257
306
|
}
|
|
258
307
|
|
|
308
|
+
function createPublishedProjection(): PublishedProjection {
|
|
309
|
+
return {
|
|
310
|
+
content: new Map(),
|
|
311
|
+
taxonomyGroups: new Set(),
|
|
312
|
+
mediaIds: new Set(),
|
|
313
|
+
mediaStorageKeys: new Set(),
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function readString(value: unknown): string | null {
|
|
318
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function recordPublishedContent(
|
|
322
|
+
projection: PublishedProjection,
|
|
323
|
+
tableName: string,
|
|
324
|
+
rows: Record<string, unknown>[],
|
|
325
|
+
): void {
|
|
326
|
+
const references: PublishedContentReferences = {
|
|
327
|
+
ids: new Set(),
|
|
328
|
+
translationGroups: new Set(),
|
|
329
|
+
};
|
|
330
|
+
for (const row of rows) {
|
|
331
|
+
const id = readString(row.id);
|
|
332
|
+
const translationGroup = readString(row.translation_group);
|
|
333
|
+
if (id) references.ids.add(id);
|
|
334
|
+
if (translationGroup) references.translationGroups.add(translationGroup);
|
|
335
|
+
}
|
|
336
|
+
projection.content.set(tableName.slice("ec_".length), references);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function isPublishedContentReference(
|
|
340
|
+
projection: PublishedProjection,
|
|
341
|
+
collection: unknown,
|
|
342
|
+
reference: unknown,
|
|
343
|
+
): boolean {
|
|
344
|
+
const collectionName = readString(collection);
|
|
345
|
+
const id = readString(reference);
|
|
346
|
+
if (!collectionName || !id) return false;
|
|
347
|
+
const references = projection.content.get(collectionName);
|
|
348
|
+
return references?.ids.has(id) === true || references?.translationGroups.has(id) === true;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function collectMediaReferences(value: unknown, projection: PublishedProjection): void {
|
|
352
|
+
if (typeof value === "string") {
|
|
353
|
+
if (value.startsWith(MEDIA_FILE_PREFIX)) {
|
|
354
|
+
const storageKey = value.slice(MEDIA_FILE_PREFIX.length).split(MEDIA_FILE_SUFFIX, 1)[0];
|
|
355
|
+
if (storageKey) projection.mediaStorageKeys.add(storageKey);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const trimmed = value.trimStart();
|
|
359
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
360
|
+
try {
|
|
361
|
+
collectMediaReferences(JSON.parse(value), projection);
|
|
362
|
+
} catch {
|
|
363
|
+
// Not structured data.
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (Array.isArray(value)) {
|
|
369
|
+
for (const item of value) collectMediaReferences(item, projection);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (!isRecord(value)) return;
|
|
373
|
+
|
|
374
|
+
const mediaId = readString(value.mediaId);
|
|
375
|
+
if (mediaId) projection.mediaIds.add(mediaId);
|
|
376
|
+
|
|
377
|
+
const provider = readString(value.provider);
|
|
378
|
+
const meta = isRecord(value.meta) ? value.meta : null;
|
|
379
|
+
const localMediaId = readString(value.id);
|
|
380
|
+
if ((provider === "local" || (!provider && meta)) && localMediaId) {
|
|
381
|
+
projection.mediaIds.add(localMediaId);
|
|
382
|
+
}
|
|
383
|
+
const storageKey = readString(value.storageKey) ?? readString(meta?.storageKey);
|
|
384
|
+
if (storageKey) projection.mediaStorageKeys.add(storageKey);
|
|
385
|
+
|
|
386
|
+
const asset = isRecord(value.asset) ? value.asset : null;
|
|
387
|
+
const assetRef = readString(asset?._ref);
|
|
388
|
+
if (assetRef) {
|
|
389
|
+
projection.mediaIds.add(assetRef);
|
|
390
|
+
projection.mediaStorageKeys.add(assetRef);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
for (const child of Object.values(value)) collectMediaReferences(child, projection);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function projectTaxonomyRows(
|
|
397
|
+
rows: Record<string, unknown>[],
|
|
398
|
+
projection: PublishedProjection,
|
|
399
|
+
): Record<string, unknown>[] {
|
|
400
|
+
const includedGroups = new Set(projection.taxonomyGroups);
|
|
401
|
+
const includedIds = new Set(projection.taxonomyGroups);
|
|
402
|
+
let changed = true;
|
|
403
|
+
while (changed) {
|
|
404
|
+
changed = false;
|
|
405
|
+
for (const row of rows) {
|
|
406
|
+
const id = readString(row.id);
|
|
407
|
+
const group = readString(row.translation_group);
|
|
408
|
+
if (!id) continue;
|
|
409
|
+
if (includedIds.has(id) || (group && includedGroups.has(group))) {
|
|
410
|
+
if (!includedIds.has(id)) {
|
|
411
|
+
includedIds.add(id);
|
|
412
|
+
changed = true;
|
|
413
|
+
}
|
|
414
|
+
if (group && !includedGroups.has(group)) {
|
|
415
|
+
includedGroups.add(group);
|
|
416
|
+
changed = true;
|
|
417
|
+
}
|
|
418
|
+
const parentId = readString(row.parent_id);
|
|
419
|
+
if (parentId && !includedIds.has(parentId)) {
|
|
420
|
+
includedIds.add(parentId);
|
|
421
|
+
changed = true;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return rows.filter((row) => {
|
|
427
|
+
const id = readString(row.id);
|
|
428
|
+
const group = readString(row.translation_group);
|
|
429
|
+
return (id && includedIds.has(id)) || (group && includedGroups.has(group));
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function projectPublishedRows(
|
|
434
|
+
tableName: SystemTable,
|
|
435
|
+
rows: Record<string, unknown>[],
|
|
436
|
+
projection: PublishedProjection,
|
|
437
|
+
): Record<string, unknown>[] {
|
|
438
|
+
switch (tableName) {
|
|
439
|
+
case "_emdash_seo":
|
|
440
|
+
return rows.filter((row) => {
|
|
441
|
+
const published = isPublishedContentReference(projection, row.collection, row.content_id);
|
|
442
|
+
if (!published) return false;
|
|
443
|
+
const image = readString(row.seo_image);
|
|
444
|
+
if (image) {
|
|
445
|
+
collectMediaReferences(image, projection);
|
|
446
|
+
if (!EXTERNAL_OR_ROOT_URL.test(image)) {
|
|
447
|
+
projection.mediaIds.add(image);
|
|
448
|
+
projection.mediaStorageKeys.add(image);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return true;
|
|
452
|
+
});
|
|
453
|
+
case "content_taxonomies":
|
|
454
|
+
return rows.filter((row) => {
|
|
455
|
+
const published = isPublishedContentReference(projection, row.collection, row.entry_id);
|
|
456
|
+
const taxonomyGroup = readString(row.taxonomy_id);
|
|
457
|
+
if (published && taxonomyGroup) projection.taxonomyGroups.add(taxonomyGroup);
|
|
458
|
+
return published;
|
|
459
|
+
});
|
|
460
|
+
case "taxonomies":
|
|
461
|
+
return projectTaxonomyRows(rows, projection);
|
|
462
|
+
case "_emdash_menu_items":
|
|
463
|
+
return rows.filter((row) => {
|
|
464
|
+
const type = readString(row.type);
|
|
465
|
+
const referenceId = readString(row.reference_id);
|
|
466
|
+
if (!referenceId || type === "custom") return true;
|
|
467
|
+
if (type === "taxonomy") {
|
|
468
|
+
projection.taxonomyGroups.add(referenceId);
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
if (type === "collection" && !row.reference_id) return true;
|
|
472
|
+
const collection =
|
|
473
|
+
readString(row.reference_collection) ??
|
|
474
|
+
(type === "post" || type === "page" ? `${type}s` : null);
|
|
475
|
+
return isPublishedContentReference(projection, collection, referenceId);
|
|
476
|
+
});
|
|
477
|
+
case "media":
|
|
478
|
+
return rows.filter((row) => {
|
|
479
|
+
const id = readString(row.id);
|
|
480
|
+
const storageKey = readString(row.storage_key);
|
|
481
|
+
return (
|
|
482
|
+
(id !== null && projection.mediaIds.has(id)) ||
|
|
483
|
+
(storageKey !== null && projection.mediaStorageKeys.has(storageKey))
|
|
484
|
+
);
|
|
485
|
+
});
|
|
486
|
+
case "options":
|
|
487
|
+
return rows;
|
|
488
|
+
default:
|
|
489
|
+
return [];
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
259
493
|
export interface GenerateSnapshotOptions {
|
|
260
494
|
/** Include draft and scheduled content (default: false) */
|
|
261
495
|
includeDrafts?: boolean;
|
|
@@ -288,6 +522,8 @@ export async function generateSnapshot(
|
|
|
288
522
|
const includeTrashed = options?.includeTrashed ?? false;
|
|
289
523
|
const optionPrefixes = options?.optionPrefixes ?? SAFE_OPTIONS_PREFIXES;
|
|
290
524
|
const optionKeys = new Set(options?.optionKeys);
|
|
525
|
+
const publishedOnly = !includeDrafts && !includeTrashed;
|
|
526
|
+
const publishedProjection = createPublishedProjection();
|
|
291
527
|
|
|
292
528
|
const contentTables = await listTablesLike(db, "ec_%");
|
|
293
529
|
|
|
@@ -340,6 +576,12 @@ export async function generateSnapshot(
|
|
|
340
576
|
`.execute(db)
|
|
341
577
|
).rows;
|
|
342
578
|
}
|
|
579
|
+
} else if (
|
|
580
|
+
publishedOnly &&
|
|
581
|
+
isSystemTable(tableName) &&
|
|
582
|
+
PUBLISHED_TABLE_POLICIES[tableName] === "omit"
|
|
583
|
+
) {
|
|
584
|
+
rows = [];
|
|
343
585
|
} else if (tableName === "options") {
|
|
344
586
|
rows = (
|
|
345
587
|
await sql<Record<string, unknown>>`
|
|
@@ -357,6 +599,18 @@ export async function generateSnapshot(
|
|
|
357
599
|
).rows;
|
|
358
600
|
}
|
|
359
601
|
|
|
602
|
+
if (publishedOnly) {
|
|
603
|
+
if (tableName.startsWith("ec_")) {
|
|
604
|
+
recordPublishedContent(publishedProjection, tableName, rows);
|
|
605
|
+
} else if (
|
|
606
|
+
isSystemTable(tableName) &&
|
|
607
|
+
PUBLISHED_TABLE_POLICIES[tableName] === "content-linked"
|
|
608
|
+
) {
|
|
609
|
+
rows = projectPublishedRows(tableName, rows, publishedProjection);
|
|
610
|
+
}
|
|
611
|
+
for (const row of rows) collectMediaReferences(row, publishedProjection);
|
|
612
|
+
}
|
|
613
|
+
|
|
360
614
|
if (rows.length > 0) {
|
|
361
615
|
tables[tableName] = normalizeRows(rows, types);
|
|
362
616
|
}
|
|
@@ -631,14 +631,20 @@ export function createViteConfig(
|
|
|
631
631
|
// When using dist, pre-bundle to avoid re-optimization on first hydration.
|
|
632
632
|
// lowlight pulls in a CommonJS highlight.js entry, so the inline Portable
|
|
633
633
|
// Text editor requires these to be pre-bundled with ESM interop in dev.
|
|
634
|
+
// Bare ids would not resolve on pnpm sites, which have no top-level copy.
|
|
634
635
|
include: useSource
|
|
635
|
-
? [
|
|
636
|
+
? [
|
|
637
|
+
"@astrojs/react/client.js",
|
|
638
|
+
"emdash > lowlight",
|
|
639
|
+
"emdash > highlight.js",
|
|
640
|
+
"emdash > highlight.js/lib/core",
|
|
641
|
+
]
|
|
636
642
|
: [
|
|
637
643
|
"@emdash-cms/admin",
|
|
638
644
|
"@astrojs/react/client.js",
|
|
639
|
-
"lowlight",
|
|
640
|
-
"highlight.js",
|
|
641
|
-
"highlight.js/lib/core",
|
|
645
|
+
"emdash > lowlight",
|
|
646
|
+
"emdash > highlight.js",
|
|
647
|
+
"emdash > highlight.js/lib/core",
|
|
642
648
|
],
|
|
643
649
|
exclude: cloudflare ? ["virtual:emdash"] : [...NODE_NATIVE_EXTERNALS, "virtual:emdash"],
|
|
644
650
|
},
|
package/src/astro/middleware.ts
CHANGED
|
@@ -947,6 +947,7 @@ export const onRequest = defineMiddleware(async (context, next) => {
|
|
|
947
947
|
handleMediaGet: runtime.handleMediaGet.bind(runtime),
|
|
948
948
|
handleMediaUpload: runtime.handleMediaUpload.bind(runtime),
|
|
949
949
|
handleMediaCreate: runtime.handleMediaCreate.bind(runtime),
|
|
950
|
+
handleMediaRegisterUpload: runtime.handleMediaRegisterUpload.bind(runtime),
|
|
950
951
|
handleMediaUpdate: runtime.handleMediaUpdate.bind(runtime),
|
|
951
952
|
...(runtime.handleMediaReplaceMetadata
|
|
952
953
|
? {
|