@styleflow.app/flowpress-astro-core 1.0.0-beta.14 → 1.0.0-beta.15
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/CHANGELOG.md +11 -0
- package/README.md +2 -2
- package/dist/media.d.ts +6 -0
- package/dist/media.d.ts.map +1 -1
- package/dist/media.js +14 -3
- package/dist/media.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 1.0.0-beta.15
|
|
6
|
+
|
|
7
|
+
- Fixed `proxyFlowpressMedia` forwarding the `/media/` route prefix to the
|
|
8
|
+
upstream `FLOWPRESS_MEDIA` origin. `/media/` is a frontend-only routing
|
|
9
|
+
marker with no equivalent on either upstream (an offload CDN root, or the
|
|
10
|
+
WordPress backend as a fallback), so it is now stripped before the request
|
|
11
|
+
is forwarded: `/media/2026/09/x.jpg` resolves to `<origin>/2026/09/x.jpg`
|
|
12
|
+
instead of `<origin>/media/2026/09/x.jpg`, which 404s.
|
|
13
|
+
- `proxyFlowpressUpload` (`/app/uploads/`) is unaffected: that prefix mirrors
|
|
14
|
+
a real path on the WordPress backend and continues to be forwarded as-is.
|
|
15
|
+
|
|
5
16
|
## 1.0.0-beta.14
|
|
6
17
|
|
|
7
18
|
- Adopted `@styleflow.app/astro@1.0.0-beta.7` and Typography vNext contract
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ The package must not import from an application or project plugin.
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
pnpm add @styleflow.app/flowpress-astro-core@1.0.0-beta.
|
|
12
|
+
pnpm add @styleflow.app/flowpress-astro-core@1.0.0-beta.15 \
|
|
13
13
|
@styleflow.app/astro@1.0.0-beta.6
|
|
14
14
|
```
|
|
15
15
|
|
|
@@ -246,7 +246,7 @@ The TypeScript template-hierarchy suite is self-contained. Its additional PHP
|
|
|
246
246
|
parity test runs when the sibling `flowpress-wordpress-core` checkout is
|
|
247
247
|
available locally and is skipped in a standalone package checkout.
|
|
248
248
|
|
|
249
|
-
Publish `1.0.0-beta.
|
|
249
|
+
Publish `1.0.0-beta.15` on `next` only after the exact Styleflow prerelease is
|
|
250
250
|
available. Inspect `npm view`, `gitHead`, integrity and the downloaded tarball.
|
|
251
251
|
Promote to `latest` only after the DMO canary and rollback gate.
|
|
252
252
|
|
package/dist/media.d.ts
CHANGED
|
@@ -4,5 +4,11 @@ export declare function isFlowpressUploadPath(pathname: string): boolean;
|
|
|
4
4
|
export declare function isFlowpressMediaPath(pathname: string): boolean;
|
|
5
5
|
export declare function rewriteFlowpressBackendUrl(value: string, backendUrl: string, frontendOrigin?: string): string;
|
|
6
6
|
export declare function proxyFlowpressUpload(request: Request, backendUrl: string, fetchImpl?: typeof fetch): Promise<Response>;
|
|
7
|
+
/**
|
|
8
|
+
* Proxy /media/<path> to the current FLOWPRESS_MEDIA origin (an offload CDN,
|
|
9
|
+
* or the WordPress backend as a fallback). /media/ is a frontend-only routing
|
|
10
|
+
* marker with no equivalent on either upstream, so it is stripped before the
|
|
11
|
+
* request is forwarded: /media/2026/09/x.jpg becomes <origin>/2026/09/x.jpg.
|
|
12
|
+
*/
|
|
7
13
|
export declare function proxyFlowpressMedia(request: Request, mediaUrl: string, fetchImpl?: typeof fetch): Promise<Response>;
|
|
8
14
|
//# sourceMappingURL=media.d.ts.map
|
package/dist/media.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AACxD,eAAO,MAAM,sBAAsB,YAAY,CAAC;AAwBhD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG/D;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG9D;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,cAAc,SAAK,GAClB,MAAM,CASR;
|
|
1
|
+
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AACxD,eAAO,MAAM,sBAAsB,YAAY,CAAC;AAwBhD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG/D;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG9D;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,cAAc,SAAK,GAClB,MAAM,CASR;AA8DD,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,QAAQ,CAAC,CAOnB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,QAAQ,CAAC,CAOnB"}
|
package/dist/media.js
CHANGED
|
@@ -49,7 +49,7 @@ async function proxyFlowpressAsset(request, upstreamUrl, fetchImpl, options) {
|
|
|
49
49
|
}
|
|
50
50
|
let target;
|
|
51
51
|
try {
|
|
52
|
-
target = assetTarget(request.url, upstreamUrl, options.prefix, options.preserveUpstreamPath);
|
|
52
|
+
target = assetTarget(request.url, upstreamUrl, options.prefix, options.preserveUpstreamPath, options.stripPrefix);
|
|
53
53
|
}
|
|
54
54
|
catch {
|
|
55
55
|
return new Response("Bad Gateway", { status: 502 });
|
|
@@ -89,17 +89,25 @@ export function proxyFlowpressUpload(request, backendUrl, fetchImpl = fetch) {
|
|
|
89
89
|
return proxyFlowpressAsset(request, backendUrl, fetchImpl, {
|
|
90
90
|
prefix: FLOWPRESS_UPLOADS_PREFIX,
|
|
91
91
|
preserveUpstreamPath: false,
|
|
92
|
+
stripPrefix: false,
|
|
92
93
|
rewriteLocation: (location) => rewriteFlowpressBackendUrl(location, backendUrl)
|
|
93
94
|
});
|
|
94
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Proxy /media/<path> to the current FLOWPRESS_MEDIA origin (an offload CDN,
|
|
98
|
+
* or the WordPress backend as a fallback). /media/ is a frontend-only routing
|
|
99
|
+
* marker with no equivalent on either upstream, so it is stripped before the
|
|
100
|
+
* request is forwarded: /media/2026/09/x.jpg becomes <origin>/2026/09/x.jpg.
|
|
101
|
+
*/
|
|
95
102
|
export function proxyFlowpressMedia(request, mediaUrl, fetchImpl = fetch) {
|
|
96
103
|
return proxyFlowpressAsset(request, mediaUrl, fetchImpl, {
|
|
97
104
|
prefix: FLOWPRESS_MEDIA_PREFIX,
|
|
98
105
|
preserveUpstreamPath: true,
|
|
106
|
+
stripPrefix: true,
|
|
99
107
|
rewriteLocation: (location, target) => new URL(location, target).toString()
|
|
100
108
|
});
|
|
101
109
|
}
|
|
102
|
-
function assetTarget(requestUrl, upstreamUrl, prefix, preserveUpstreamPath) {
|
|
110
|
+
function assetTarget(requestUrl, upstreamUrl, prefix, preserveUpstreamPath, stripPrefix) {
|
|
103
111
|
const request = new URL(requestUrl);
|
|
104
112
|
if (request.pathname !== prefix.slice(0, -1) && !request.pathname.startsWith(prefix)) {
|
|
105
113
|
throw new Error("Unsupported FlowPress asset path.");
|
|
@@ -112,9 +120,12 @@ function assetTarget(requestUrl, upstreamUrl, prefix, preserveUpstreamPath) {
|
|
|
112
120
|
|| upstream.hash) {
|
|
113
121
|
throw new Error("Invalid FlowPress asset upstream.");
|
|
114
122
|
}
|
|
123
|
+
const requestPath = stripPrefix
|
|
124
|
+
? request.pathname.slice(prefix.length - 1) || "/"
|
|
125
|
+
: request.pathname;
|
|
115
126
|
const target = new URL(upstream.origin);
|
|
116
127
|
const upstreamPath = preserveUpstreamPath ? upstream.pathname.replace(/\/+$/, "") : "";
|
|
117
|
-
target.pathname = `${upstreamPath}${
|
|
128
|
+
target.pathname = `${upstreamPath}${requestPath}`;
|
|
118
129
|
target.search = request.search;
|
|
119
130
|
return target;
|
|
120
131
|
}
|
package/dist/media.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media.js","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,wBAAwB,GAAG,eAAe,CAAC;AACxD,MAAM,CAAC,MAAM,sBAAsB,GAAG,SAAS,CAAC;AAEhD,MAAM,cAAc,GAAG;IACrB,QAAQ;IACR,UAAU;IACV,mBAAmB;IACnB,eAAe;IACf,qBAAqB;IACrB,OAAO;CACC,CAAC;AAEX,MAAM,eAAe,GAAG;IACtB,eAAe;IACf,eAAe;IACf,qBAAqB;IACrB,kBAAkB;IAClB,gBAAgB;IAChB,eAAe;IACf,cAAc;IACd,MAAM;IACN,eAAe;IACf,MAAM;CACE,CAAC;AAEX,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,OAAO,QAAQ,KAAK,wBAAwB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;WACpD,QAAQ,CAAC,UAAU,CAAC,wBAAwB,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAgB;IACnD,OAAO,QAAQ,KAAK,sBAAsB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;WAClD,QAAQ,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,0BAA0B,CACxC,KAAa,EACb,UAAkB,EAClB,cAAc,GAAG,EAAE;IAEnB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;QACpC,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QACnD,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAChG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;
|
|
1
|
+
{"version":3,"file":"media.js","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"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","sourcesContent":["export const FLOWPRESS_UPLOADS_PREFIX = \"/app/uploads/\";\nexport const FLOWPRESS_MEDIA_PREFIX = \"/media/\";\n\nconst requestHeaders = [\n \"accept\",\n \"if-match\",\n \"if-modified-since\",\n \"if-none-match\",\n \"if-unmodified-since\",\n \"range\"\n] as const;\n\nconst responseHeaders = [\n \"accept-ranges\",\n \"cache-control\",\n \"content-disposition\",\n \"content-encoding\",\n \"content-length\",\n \"content-range\",\n \"content-type\",\n \"etag\",\n \"last-modified\",\n \"vary\"\n] as const;\n\nexport function isFlowpressUploadPath(pathname: string): boolean {\n return pathname === FLOWPRESS_UPLOADS_PREFIX.slice(0, -1)\n || pathname.startsWith(FLOWPRESS_UPLOADS_PREFIX);\n}\n\nexport function isFlowpressMediaPath(pathname: string): boolean {\n return pathname === FLOWPRESS_MEDIA_PREFIX.slice(0, -1)\n || pathname.startsWith(FLOWPRESS_MEDIA_PREFIX);\n}\n\nexport function rewriteFlowpressBackendUrl(\n value: string,\n backendUrl: string,\n frontendOrigin = \"\"\n): string {\n try {\n const source = new URL(value);\n const backend = new URL(backendUrl);\n if (source.origin !== backend.origin) return value;\n return `${frontendOrigin.replace(/\\/$/, \"\")}${source.pathname}${source.search}${source.hash}`;\n } catch {\n return value;\n }\n}\n\ninterface AssetProxyOptions {\n prefix: string;\n preserveUpstreamPath: boolean;\n stripPrefix: boolean;\n rewriteLocation: (location: string, target: URL) => string;\n}\n\nasync function proxyFlowpressAsset(\n request: Request,\n upstreamUrl: string,\n fetchImpl: typeof fetch,\n options: AssetProxyOptions\n): Promise<Response> {\n if (request.method !== \"GET\" && request.method !== \"HEAD\") {\n return new Response(\"Method Not Allowed\", {\n status: 405,\n headers: { allow: \"GET, HEAD\" }\n });\n }\n let target: URL;\n try {\n target = assetTarget(\n request.url,\n upstreamUrl,\n options.prefix,\n options.preserveUpstreamPath,\n options.stripPrefix\n );\n } catch {\n return new Response(\"Bad Gateway\", { status: 502 });\n }\n\n const headers = new Headers();\n for (const name of requestHeaders) {\n const value = request.headers.get(name);\n if (value) headers.set(name, value);\n }\n try {\n const upstream = await fetchImpl(target, {\n method: request.method,\n headers,\n redirect: \"manual\"\n });\n const safeHeaders = new Headers();\n for (const name of responseHeaders) {\n const value = upstream.headers.get(name);\n if (value) safeHeaders.set(name, value);\n }\n const location = upstream.headers.get(\"location\");\n if (location) safeHeaders.set(\"location\", options.rewriteLocation(location, target));\n return new Response(request.method === \"HEAD\" ? null : upstream.body, {\n status: upstream.status,\n statusText: upstream.statusText,\n headers: safeHeaders\n });\n } catch {\n return new Response(\"Bad Gateway\", { status: 502 });\n }\n}\n\nexport function proxyFlowpressUpload(\n request: Request,\n backendUrl: string,\n fetchImpl: typeof fetch = fetch\n): Promise<Response> {\n return proxyFlowpressAsset(request, backendUrl, fetchImpl, {\n prefix: FLOWPRESS_UPLOADS_PREFIX,\n preserveUpstreamPath: false,\n stripPrefix: false,\n rewriteLocation: (location) => rewriteFlowpressBackendUrl(location, backendUrl)\n });\n}\n\n/**\n * Proxy /media/<path> to the current FLOWPRESS_MEDIA origin (an offload CDN,\n * or the WordPress backend as a fallback). /media/ is a frontend-only routing\n * marker with no equivalent on either upstream, so it is stripped before the\n * request is forwarded: /media/2026/09/x.jpg becomes <origin>/2026/09/x.jpg.\n */\nexport function proxyFlowpressMedia(\n request: Request,\n mediaUrl: string,\n fetchImpl: typeof fetch = fetch\n): Promise<Response> {\n return proxyFlowpressAsset(request, mediaUrl, fetchImpl, {\n prefix: FLOWPRESS_MEDIA_PREFIX,\n preserveUpstreamPath: true,\n stripPrefix: true,\n rewriteLocation: (location, target) => new URL(location, target).toString()\n });\n}\n\nfunction assetTarget(\n requestUrl: string,\n upstreamUrl: string,\n prefix: string,\n preserveUpstreamPath: boolean,\n stripPrefix: boolean\n): URL {\n const request = new URL(requestUrl);\n if (request.pathname !== prefix.slice(0, -1) && !request.pathname.startsWith(prefix)) {\n throw new Error(\"Unsupported FlowPress asset path.\");\n }\n const upstream = new URL(upstreamUrl);\n if (\n !/^https?:$/.test(upstream.protocol)\n || upstream.username\n || upstream.password\n || upstream.search\n || upstream.hash\n ) {\n throw new Error(\"Invalid FlowPress asset upstream.\");\n }\n const requestPath = stripPrefix\n ? request.pathname.slice(prefix.length - 1) || \"/\"\n : request.pathname;\n const target = new URL(upstream.origin);\n const upstreamPath = preserveUpstreamPath ? upstream.pathname.replace(/\\/+$/, \"\") : \"\";\n target.pathname = `${upstreamPath}${requestPath}`;\n target.search = request.search;\n return target;\n}\n"]}
|
package/package.json
CHANGED