@stnd/modules 0.7.0 → 0.8.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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @stnd/folios
2
2
 
3
+ ## 0.8.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @stnd/server@0.9.0
9
+
10
+ ## 0.8.0
11
+
12
+ ### Minor Changes
13
+
14
+ - Auto-bumped @stnd/modules due to modified code.
15
+
3
16
  ## 0.7.0
4
17
 
5
18
  ### Minor Changes
@@ -6,12 +6,15 @@
6
6
  * Policy lives in `@stnd/server/security`; an app extends it with
7
7
  * `standard({ security: { csp: { extend: { "script-src": [...] } } } })`.
8
8
  * - `/api/csp-report`: where the policy's violations are sent.
9
+ * - Build: the same headers appended to `_headers` for prerendered files.
9
10
  * - `/_headers`: the same basics as a Cloudflare headers file.
10
11
  */
11
12
  export default {
12
13
  id: "stnd-headers",
13
14
  name: "Stnd :: Headers",
14
15
  description: "Security headers on every response, and the CSP violation report endpoint.",
16
+ // Prerendered pages never reach the Worker: they get the same headers from the build's _headers file.
17
+ hooks: { "astro:build:done": [{ action: "./static-headers.js" }] },
15
18
  middleware: [{ entrypoint: "./middleware.js", order: -1000 }],
16
19
  routes: [
17
20
  {
@@ -0,0 +1,39 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import Log from "@stnd/log";
5
+
6
+ const log = Log({ scope: "Headers" });
7
+ const MARKER = "# stnd:security-headers";
8
+
9
+ /**
10
+ * astro:build:done — the middleware only sees responses the Worker produces.
11
+ * Prerendered pages are static files served straight from Cloudflare's asset
12
+ * store, never reaching it, so they get the same headers through the build's
13
+ * `_headers` file. Headers the app's own `_headers` already sets are left alone
14
+ * (Cloudflare would otherwise send both values).
15
+ */
16
+ export default async function writeStaticHeaders({ dir }) {
17
+ const options = globalThis.__STANDARD_CONFIG__?.security || {};
18
+ if (options.enabled === false) return;
19
+
20
+ const { securityHeaders } = await import("@stnd/server/security");
21
+ const wanted = securityHeaders(new URL("https://static.invalid/"), "text/html", options);
22
+
23
+ const outDir = fileURLToPath(dir);
24
+ const file = path.join(outDir, "_headers");
25
+ const existing = fs.existsSync(file) ? fs.readFileSync(file, "utf8") : "";
26
+ if (existing.includes(MARKER)) return;
27
+
28
+ const declared = new Set(
29
+ [...existing.matchAll(/^[ \t]+([A-Za-z0-9-]+):/gm)].map((m) => m[1].toLowerCase()),
30
+ );
31
+ const lines = Object.entries(wanted)
32
+ .filter(([name]) => !declared.has(name.toLowerCase()))
33
+ .map(([name, value]) => ` ${name}: ${value}`);
34
+ if (!lines.length) return;
35
+
36
+ const block = `${existing && !existing.endsWith("\n") ? "\n" : ""}\n${MARKER}\n/*\n${lines.join("\n")}\n`;
37
+ fs.writeFileSync(file, existing + block);
38
+ log.info(`Security headers added to ${path.relative(process.cwd(), file) || file} for prerendered files`);
39
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stnd/modules",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "type": "module",
5
5
  "svelte": "./stripe/views/CheckoutView.svelte",
6
6
  "author": "Francis Fontaine",
@@ -21,7 +21,7 @@
21
21
  "@stnd/icon": "0.7.0",
22
22
  "@stnd/layout": "0.7.7",
23
23
  "@stnd/log": "0.5.2",
24
- "@stnd/server": "0.8.0",
24
+ "@stnd/server": "0.9.0",
25
25
  "@stnd/styles": "0.13.0",
26
26
  "@stnd/utils": "0.7.3"
27
27
  },