@stacksjs/bun-router 0.0.21 → 0.0.23

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/cli.js CHANGED
@@ -2047,7 +2047,7 @@ function getPrefix() {
2047
2047
  }
2048
2048
  var prefix = getPrefix();
2049
2049
  // package.json
2050
- var version = "0.0.21";
2050
+ var version = "0.0.23";
2051
2051
 
2052
2052
  // src/cli/middleware.ts
2053
2053
  import fs2 from "fs/promises";
package/dist/index.js CHANGED
@@ -3074,7 +3074,19 @@ function findPrebuiltView(stxFilePath, viewsDir) {
3074
3074
  }
3075
3075
  return null;
3076
3076
  }
3077
- async function renderStxFile(filePath, viewsDir, data, routingConfig) {
3077
+ function parseCookies(header) {
3078
+ const jar = {};
3079
+ for (const part of String(header ?? "").split(";")) {
3080
+ const cut = part.indexOf("=");
3081
+ if (cut < 0)
3082
+ continue;
3083
+ try {
3084
+ jar[part.slice(0, cut).trim()] = decodeURIComponent(part.slice(cut + 1).trim());
3085
+ } catch {}
3086
+ }
3087
+ return jar;
3088
+ }
3089
+ async function renderStxFile(filePath, viewsDir, data, routingConfig, request2) {
3078
3090
  const stxModule = "@stacksjs/stx";
3079
3091
  const stx = await import(stxModule);
3080
3092
  if (!stx.processDirectives || !stx.extractVariables) {
@@ -3085,11 +3097,42 @@ async function renderStxFile(filePath, viewsDir, data, routingConfig) {
3085
3097
  const scriptContent = scriptMatch ? scriptMatch[1] : "";
3086
3098
  let templateContent = scriptMatch ? content.replace(/<script\s+server\s*>[\s\S]*?<\/script>/i, "") : content;
3087
3099
  templateContent = templateContent.replace(/<script\s+client\s*>/gi, "<script>");
3100
+ const asked = { status: 200, headers: {} };
3101
+ const headerBag = data.headers ?? {};
3102
+ const cookieHeader = String(headerBag.cookie ?? headerBag.Cookie ?? "");
3103
+ const requestUrl = String(data.url ?? "");
3104
+ const search = requestUrl.includes("?") ? requestUrl.slice(requestUrl.indexOf("?")) : "";
3105
+ const serveContext = {
3106
+ url: requestUrl,
3107
+ path: String(data.path ?? ""),
3108
+ search,
3109
+ host: String(headerBag.host ?? ""),
3110
+ cookieHeader,
3111
+ cookies: parseCookies(cookieHeader),
3112
+ ip: "",
3113
+ locale: null,
3114
+ params: data.params ?? {},
3115
+ method: String(data.method ?? "GET"),
3116
+ request: request2
3117
+ };
3088
3118
  const context = {
3089
3119
  __filename: filePath,
3090
3120
  __dirname: dirname(filePath),
3091
3121
  props: data,
3092
- ...data
3122
+ ...data,
3123
+ __stxServeContext: serveContext,
3124
+ setResponseStatus: (status) => {
3125
+ if (Number.isInteger(status) && status >= 100 && status <= 599)
3126
+ asked.status = status;
3127
+ },
3128
+ notFound: (status = 404) => {
3129
+ asked.status = Number.isInteger(status) && status >= 400 && status <= 599 ? status : 404;
3130
+ },
3131
+ setResponseHeader: (name, value) => {
3132
+ if (name)
3133
+ asked.headers[String(name)] = String(value);
3134
+ },
3135
+ definePageMeta: () => {}
3093
3136
  };
3094
3137
  if (scriptContent) {
3095
3138
  await stx.extractVariables(scriptContent, context, filePath);
@@ -3103,7 +3146,14 @@ async function renderStxFile(filePath, viewsDir, data, routingConfig) {
3103
3146
  layoutsDir: resolvedLayoutsDir,
3104
3147
  partialsDir: resolvedPartialsDir
3105
3148
  };
3106
- return stx.processDirectives(templateContent, context, filePath, config2, new Set);
3149
+ const html = await stx.processDirectives(templateContent, context, filePath, config2, new Set);
3150
+ const recordedStatus = Number(context.__stxResponseStatus);
3151
+ const recordedHeaders = context.__stxResponseHeaders;
3152
+ return {
3153
+ html,
3154
+ status: Number.isInteger(recordedStatus) && recordedStatus >= 100 && recordedStatus <= 599 ? recordedStatus : asked.status,
3155
+ headers: { ...asked.headers, ...recordedHeaders ?? {} }
3156
+ };
3107
3157
  }
3108
3158
  function createViewHandler(filePath, viewsDir, config2, queryPreservationConfig) {
3109
3159
  return async (req) => {
@@ -3133,11 +3183,11 @@ function createViewHandler(filePath, viewsDir, config2, queryPreservationConfig)
3133
3183
  });
3134
3184
  }
3135
3185
  if (filePath.endsWith(".stx")) {
3136
- let html = await renderStxFile(filePath, viewsDir, data, config2);
3137
- html = applyQueryPreservation(html);
3186
+ const rendered = await renderStxFile(filePath, viewsDir, data, config2, req);
3187
+ const html = applyQueryPreservation(rendered.html);
3138
3188
  return new Response(html, {
3139
- status: 200,
3140
- headers: { "Content-Type": "text/html; charset=utf-8" }
3189
+ status: rendered.status,
3190
+ headers: { "Content-Type": "text/html; charset=utf-8", ...rendered.headers }
3141
3191
  });
3142
3192
  }
3143
3193
  let content = await Bun.file(filePath).text();
@@ -6752,6 +6802,12 @@ class RouteTrie {
6752
6802
  type: "wildcard",
6753
6803
  value: "*"
6754
6804
  });
6805
+ } else if (/^\{[^{}/]+\}\*$/.test(part)) {
6806
+ segments.push({
6807
+ type: "wildcard",
6808
+ value: part,
6809
+ paramName: part.slice(1, part.indexOf("}"))
6810
+ });
6755
6811
  } else if (part.startsWith("{") && part.endsWith("}")) {
6756
6812
  const paramPart = part.slice(1, -1);
6757
6813
  const [paramName, pattern] = paramPart.split(":");
@@ -6837,6 +6893,7 @@ class RouteTrie {
6837
6893
  case "wildcard":
6838
6894
  if (!node.wildcardChild) {
6839
6895
  node.wildcardChild = new TrieNode;
6896
+ node.wildcardChild.paramName = segment.paramName ?? null;
6840
6897
  }
6841
6898
  this.insertRoute(node.wildcardChild, compiled, segmentIndex + 1);
6842
6899
  break;
@@ -6886,7 +6943,7 @@ class RouteTrie {
6886
6943
  if (node.wildcardChild) {
6887
6944
  const compiled = node.wildcardChild.getRoute(method);
6888
6945
  if (compiled) {
6889
- params.wildcard = segments.slice(segmentIndex).join("/");
6946
+ params[node.wildcardChild.paramName ?? "wildcard"] = segments.slice(segmentIndex).join("/");
6890
6947
  return {
6891
6948
  route: compiled.route,
6892
6949
  params,
@@ -7029,8 +7086,18 @@ class RouteCompiler {
7029
7086
  }
7030
7087
  }
7031
7088
  convertToURLPattern(path, constraints) {
7089
+ let staticPath = path;
7090
+ let catchAll = null;
7091
+ const namedCatchAll = staticPath.match(/\{([^{}/]+)\}\*$/);
7092
+ if (namedCatchAll) {
7093
+ catchAll = namedCatchAll[1];
7094
+ staticPath = staticPath.slice(0, namedCatchAll.index);
7095
+ } else if (staticPath.endsWith("*")) {
7096
+ catchAll = "wildcard";
7097
+ staticPath = staticPath.slice(0, -1);
7098
+ }
7032
7099
  const paramTokens = [];
7033
- const withTokens = path.replace(/\{([^}]+)\}/g, (_m, inner) => {
7100
+ const withTokens = staticPath.replace(/\{([^}]+)\}/g, (_m, inner) => {
7034
7101
  paramTokens.push(inner);
7035
7102
  return `\x00${paramTokens.length - 1}\x00`;
7036
7103
  });
@@ -7047,6 +7114,8 @@ class RouteCompiler {
7047
7114
  const leadingSlash = token.startsWith("/") ? "/" : "";
7048
7115
  return isOptional ? `(?:${leadingSlash}${group})?` : `${leadingSlash}${group}`;
7049
7116
  });
7117
+ if (catchAll !== null)
7118
+ regexPattern += `(?<${catchAll}>${constraints?.[catchAll] ?? ".+"})`;
7050
7119
  const regex = new RegExp(`^${regexPattern}$`);
7051
7120
  return {
7052
7121
  exec: (pathname) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/bun-router",
3
3
  "type": "module",
4
- "version": "0.0.21",
4
+ "version": "0.0.23",
5
5
  "description": "A fast, type-safe router for Bun.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",