@getstrata/core 2.0.4 → 2.0.6

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,13 @@
1
1
  # @getstrata/core changelog
2
2
 
3
+ ## 2.0.6
4
+
5
+ - Dispatch registered Bun route preflights through CORS
6
+
7
+ ## 2.0.5
8
+
9
+ - Allow explicitly approved application headers in CORS
10
+
3
11
  ## 2.0.4
4
12
 
5
13
  - Place cache rollout guidance beside outbox docs without overlapping API additions
@@ -5,6 +5,11 @@ interface CorsConfig {
5
5
  allowedHeaders: string[];
6
6
  maxAgeSeconds: number;
7
7
  }
8
- declare function resolveCorsConfig(): CorsConfig;
9
- declare function createCorsMiddleware(): Middleware;
8
+ /** Additional application-approved request headers; defaults and origins remain unchanged. */
9
+ interface CorsOptions {
10
+ additionalAllowedHeaders?: readonly string[];
11
+ }
12
+ declare function resolveCorsConfig(options?: CorsOptions): CorsConfig;
13
+ declare function createCorsMiddleware(options?: CorsOptions): Middleware;
14
+ export type { CorsConfig, CorsOptions };
10
15
  export { createCorsMiddleware, resolveCorsConfig };
@@ -86,10 +86,28 @@ function sdkClientClassName() {
86
86
  }
87
87
 
88
88
  // ../../src/core/http/corsMiddleware.ts
89
+ function additionalHeaders(options) {
90
+ const configured = process.env.CORS_ADDITIONAL_ALLOWED_HEADERS?.trim();
91
+ const supplied = options.additionalAllowedHeaders === undefined ? [] : options.additionalAllowedHeaders;
92
+ if (!Array.isArray(supplied)) {
93
+ throw new TypeError("CORS additionalAllowedHeaders must be an array of header names.");
94
+ }
95
+ const names = [
96
+ ...configured ? configured.split(",").map((name) => name.trim()) : [],
97
+ ...supplied
98
+ ];
99
+ return names.map((name) => {
100
+ if (typeof name !== "string" || name === "*" || !/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)) {
101
+ throw new TypeError("CORS additional headers must be explicit valid HTTP header names; wildcards are not allowed.");
102
+ }
103
+ return name;
104
+ });
105
+ }
89
106
  function defaultAllowedOrigins() {
90
107
  return appUrl();
91
108
  }
92
- function resolveCorsConfig() {
109
+ function resolveCorsConfig(options = {}) {
110
+ const extraHeaders = additionalHeaders(options);
93
111
  return {
94
112
  allowedOrigins: (process.env.CORS_ALLOWED_ORIGINS ?? defaultAllowedOrigins()).split(",").map((origin) => origin.trim()).filter(Boolean),
95
113
  allowedMethods: ["GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS"],
@@ -102,22 +120,27 @@ function resolveCorsConfig() {
102
120
  "X-Authenticated-User-Role",
103
121
  "If-Match",
104
122
  "If-None-Match",
105
- "X-CSRF-Token"
106
- ],
123
+ "X-CSRF-Token",
124
+ ...extraHeaders
125
+ ].filter((name, index, names) => names.findIndex((other) => other.toLowerCase() === name.toLowerCase()) === index),
107
126
  maxAgeSeconds: 86400
108
127
  };
109
128
  }
110
- function createCorsMiddleware() {
129
+ function createCorsMiddleware(options = {}) {
130
+ resolveCorsConfig(options);
131
+ const captured = {
132
+ additionalAllowedHeaders: [...options.additionalAllowedHeaders ?? []]
133
+ };
111
134
  return async (request, next) => {
112
135
  if (request.method === "OPTIONS") {
113
136
  return new Response(null, {
114
137
  status: 204,
115
- headers: buildCorsHeaders(request)
138
+ headers: buildCorsHeaders(request, captured)
116
139
  });
117
140
  }
118
141
  const response = await next();
119
142
  const headers = new Headers(response.headers);
120
- for (const [key, value] of buildCorsHeaders(request)) {
143
+ for (const [key, value] of buildCorsHeaders(request, captured)) {
121
144
  headers.set(key, value);
122
145
  }
123
146
  return new Response(response.body, {
@@ -127,10 +150,10 @@ function createCorsMiddleware() {
127
150
  });
128
151
  };
129
152
  }
130
- function buildCorsHeaders(request) {
153
+ function buildCorsHeaders(request, options) {
131
154
  const headers = new Headers;
132
155
  const origin = request.headers.get("origin");
133
- const corsConfig = resolveCorsConfig();
156
+ const corsConfig = resolveCorsConfig(options);
134
157
  const allowedOrigins = corsConfig.allowedOrigins;
135
158
  headers.set("Vary", "Origin");
136
159
  const allowOrigin = allowedOrigins.includes("*") ? "*" : origin && allowedOrigins.includes(origin) ? origin : null;
@@ -81,7 +81,7 @@ export { conditionalJsonResponse } from "../core/http/conditionalResponse.ts";
81
81
  export type { ContentSecurityPolicyDirectives, ContentSecurityPolicyOptions, } from "../core/http/contentSecurityPolicy.ts";
82
82
  export { configureContentSecurityPolicy, generateCspNonce, HTMX_2_0_4_INDICATOR_STYLE_HASH, resetContentSecurityPolicyForTests, resolveContentSecurityPolicy, resolveHtmlContentSecurityPolicy, serverHtmxContentSecurityPolicy, spaContentSecurityPolicy, strictApiContentSecurityPolicy, } from "../core/http/contentSecurityPolicy.ts";
83
83
  export { readBunRequestCookie, readRequestCookie } from "../core/http/cookies.ts";
84
- export { createCorsMiddleware } from "../core/http/corsMiddleware.ts";
84
+ export { type CorsConfig, type CorsOptions, createCorsMiddleware, } from "../core/http/corsMiddleware.ts";
85
85
  export { createCsrfMiddleware } from "../core/http/csrfMiddleware.ts";
86
86
  export { createCsrfProtection } from "../core/http/csrfProtection.ts";
87
87
  export { createCsrfTokenCookie, readSubmittedCsrfToken, readSubmittedCsrfTokenFromBody, resolveCsrfToken, resolveCsrfTokenForRequest, verifyCsrfToken, } from "../core/http/csrfToken.ts";
package/dist/index.js CHANGED
@@ -9466,10 +9466,28 @@ function readBunRequestCookie(request, name) {
9466
9466
  return request.cookies.get(name) ?? readRequestCookie(request, name);
9467
9467
  }
9468
9468
  // ../../src/core/http/corsMiddleware.ts
9469
+ function additionalHeaders(options) {
9470
+ const configured = process.env.CORS_ADDITIONAL_ALLOWED_HEADERS?.trim();
9471
+ const supplied = options.additionalAllowedHeaders === undefined ? [] : options.additionalAllowedHeaders;
9472
+ if (!Array.isArray(supplied)) {
9473
+ throw new TypeError("CORS additionalAllowedHeaders must be an array of header names.");
9474
+ }
9475
+ const names = [
9476
+ ...configured ? configured.split(",").map((name) => name.trim()) : [],
9477
+ ...supplied
9478
+ ];
9479
+ return names.map((name) => {
9480
+ if (typeof name !== "string" || name === "*" || !/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)) {
9481
+ throw new TypeError("CORS additional headers must be explicit valid HTTP header names; wildcards are not allowed.");
9482
+ }
9483
+ return name;
9484
+ });
9485
+ }
9469
9486
  function defaultAllowedOrigins() {
9470
9487
  return appUrl();
9471
9488
  }
9472
- function resolveCorsConfig() {
9489
+ function resolveCorsConfig(options = {}) {
9490
+ const extraHeaders = additionalHeaders(options);
9473
9491
  return {
9474
9492
  allowedOrigins: (process.env.CORS_ALLOWED_ORIGINS ?? defaultAllowedOrigins()).split(",").map((origin) => origin.trim()).filter(Boolean),
9475
9493
  allowedMethods: ["GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS"],
@@ -9482,22 +9500,27 @@ function resolveCorsConfig() {
9482
9500
  "X-Authenticated-User-Role",
9483
9501
  "If-Match",
9484
9502
  "If-None-Match",
9485
- "X-CSRF-Token"
9486
- ],
9503
+ "X-CSRF-Token",
9504
+ ...extraHeaders
9505
+ ].filter((name, index, names) => names.findIndex((other) => other.toLowerCase() === name.toLowerCase()) === index),
9487
9506
  maxAgeSeconds: 86400
9488
9507
  };
9489
9508
  }
9490
- function createCorsMiddleware() {
9509
+ function createCorsMiddleware(options = {}) {
9510
+ resolveCorsConfig(options);
9511
+ const captured = {
9512
+ additionalAllowedHeaders: [...options.additionalAllowedHeaders ?? []]
9513
+ };
9491
9514
  return async (request, next) => {
9492
9515
  if (request.method === "OPTIONS") {
9493
9516
  return new Response(null, {
9494
9517
  status: 204,
9495
- headers: buildCorsHeaders(request)
9518
+ headers: buildCorsHeaders(request, captured)
9496
9519
  });
9497
9520
  }
9498
9521
  const response = await next();
9499
9522
  const headers = new Headers(response.headers);
9500
- for (const [key, value] of buildCorsHeaders(request)) {
9523
+ for (const [key, value] of buildCorsHeaders(request, captured)) {
9501
9524
  headers.set(key, value);
9502
9525
  }
9503
9526
  return new Response(response.body, {
@@ -9507,10 +9530,10 @@ function createCorsMiddleware() {
9507
9530
  });
9508
9531
  };
9509
9532
  }
9510
- function buildCorsHeaders(request) {
9533
+ function buildCorsHeaders(request, options) {
9511
9534
  const headers = new Headers;
9512
9535
  const origin = request.headers.get("origin");
9513
- const corsConfig = resolveCorsConfig();
9536
+ const corsConfig = resolveCorsConfig(options);
9514
9537
  const allowedOrigins = corsConfig.allowedOrigins;
9515
9538
  headers.set("Vary", "Origin");
9516
9539
  const allowOrigin = allowedOrigins.includes("*") ? "*" : origin && allowedOrigins.includes(origin) ? origin : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/core",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "description": "Strata Bun framework public API",
5
5
  "type": "module",
6
6
  "license": "MIT",