@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 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.13 \
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.13` on `next` only after the exact Styleflow prerelease is
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
@@ -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;AAuDD,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,EAClB,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,QAAQ,CAAC,CAMnB;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,QAAQ,CAAC,CAMnB"}
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}${request.pathname}`;
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":"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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 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(request.url, upstreamUrl, options.prefix, options.preserveUpstreamPath);\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 rewriteLocation: (location) => rewriteFlowpressBackendUrl(location, backendUrl)\n });\n}\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 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): 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 target = new URL(upstream.origin);\n const upstreamPath = preserveUpstreamPath ? upstream.pathname.replace(/\\/+$/, \"\") : \"\";\n target.pathname = `${upstreamPath}${request.pathname}`;\n target.search = request.search;\n return target;\n}\n"]}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@styleflow.app/flowpress-astro-core",
3
- "version": "1.0.0-beta.14",
3
+ "version": "1.0.0-beta.15",
4
4
  "description": "Project-agnostic WordPress routing, templates, SEO and GraphQL runtime for FlowPress Astro apps.",
5
5
  "type": "module",
6
6
  "private": false,