hono 4.5.10 → 4.6.0

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.
Files changed (49) hide show
  1. package/README.md +1 -1
  2. package/dist/adapter/cloudflare-pages/handler.js +1 -1
  3. package/dist/cjs/adapter/cloudflare-pages/handler.js +1 -1
  4. package/dist/cjs/context.js +25 -16
  5. package/dist/cjs/helper/streaming/sse.js +4 -2
  6. package/dist/cjs/jsx/base.js +1 -1
  7. package/dist/cjs/jsx/components.js +10 -5
  8. package/dist/cjs/jsx/intrinsic-element/components.js +15 -3
  9. package/dist/cjs/middleware/basic-auth/index.js +15 -24
  10. package/dist/cjs/middleware/bearer-auth/index.js +32 -21
  11. package/dist/cjs/middleware/context-storage/index.js +43 -0
  12. package/dist/cjs/middleware/jsx-renderer/index.js +1 -0
  13. package/dist/cjs/middleware/secure-headers/permissions-policy.js +16 -0
  14. package/dist/cjs/middleware/secure-headers/secure-headers.js +30 -1
  15. package/dist/cjs/middleware/serve-static/index.js +32 -15
  16. package/dist/cjs/utils/basic-auth.js +46 -0
  17. package/dist/cjs/utils/body.js +1 -1
  18. package/dist/cjs/utils/html.js +8 -0
  19. package/dist/context.js +25 -16
  20. package/dist/helper/streaming/sse.js +4 -2
  21. package/dist/jsx/base.js +1 -1
  22. package/dist/jsx/components.js +10 -5
  23. package/dist/jsx/intrinsic-element/components.js +15 -3
  24. package/dist/middleware/basic-auth/index.js +15 -24
  25. package/dist/middleware/bearer-auth/index.js +32 -21
  26. package/dist/middleware/context-storage/index.js +19 -0
  27. package/dist/middleware/jsx-renderer/index.js +1 -0
  28. package/dist/middleware/secure-headers/permissions-policy.js +0 -0
  29. package/dist/middleware/secure-headers/secure-headers.js +30 -1
  30. package/dist/middleware/serve-static/index.js +32 -15
  31. package/dist/types/adapter/bun/websocket.d.ts +4 -6
  32. package/dist/types/adapter/cloudflare-pages/handler.d.ts +6 -2
  33. package/dist/types/adapter/cloudflare-workers/websocket.d.ts +1 -1
  34. package/dist/types/adapter/deno/websocket.d.ts +1 -1
  35. package/dist/types/helper/streaming/sse.d.ts +1 -1
  36. package/dist/types/helper/websocket/index.d.ts +8 -8
  37. package/dist/types/middleware/basic-auth/index.d.ts +4 -0
  38. package/dist/types/middleware/bearer-auth/index.d.ts +10 -0
  39. package/dist/types/middleware/context-storage/index.d.ts +39 -0
  40. package/dist/types/middleware/jsx-renderer/index.d.ts +2 -2
  41. package/dist/types/middleware/secure-headers/permissions-policy.d.ts +14 -0
  42. package/dist/types/middleware/secure-headers/secure-headers.d.ts +5 -0
  43. package/dist/types/middleware/serve-static/index.d.ts +2 -0
  44. package/dist/types/utils/basic-auth.d.ts +5 -0
  45. package/dist/types/utils/html.d.ts +1 -1
  46. package/dist/utils/basic-auth.js +23 -0
  47. package/dist/utils/body.js +1 -1
  48. package/dist/utils/html.js +8 -0
  49. package/package.json +9 -1
package/README.md CHANGED
@@ -71,7 +71,7 @@ Contributions Welcome! You can contribute in the following ways.
71
71
  - Create an Issue - Propose a new feature. Report a bug.
72
72
  - Pull Request - Fix a bug and typo. Refactor the code.
73
73
  - Create third-party middleware - Instruct below.
74
- - Share - Share your thoughts on the Blog, Twitter, and others.
74
+ - Share - Share your thoughts on the Blog, X, and others.
75
75
  - Make your application - Please try to use Hono.
76
76
 
77
77
  For more details, see [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md).
@@ -14,7 +14,7 @@ var handle = (app) => (eventContext) => {
14
14
  function handleMiddleware(middleware) {
15
15
  return async (executionCtx) => {
16
16
  const context = new Context(executionCtx.request, {
17
- env: executionCtx.env,
17
+ env: { ...executionCtx.env, eventContext: executionCtx },
18
18
  executionCtx
19
19
  });
20
20
  let response = void 0;
@@ -38,7 +38,7 @@ const handle = (app) => (eventContext) => {
38
38
  function handleMiddleware(middleware) {
39
39
  return async (executionCtx) => {
40
40
  const context = new import_context.Context(executionCtx.request, {
41
- env: executionCtx.env,
41
+ env: { ...executionCtx.env, eventContext: executionCtx },
42
42
  executionCtx
43
43
  });
44
44
  let response = void 0;
@@ -82,16 +82,30 @@ class Context {
82
82
  set res(_res) {
83
83
  this.#isFresh = false;
84
84
  if (this.#res && _res) {
85
- this.#res.headers.delete("content-type");
86
- for (const [k, v] of this.#res.headers.entries()) {
87
- if (k === "set-cookie") {
88
- const cookies = this.#res.headers.getSetCookie();
89
- _res.headers.delete("set-cookie");
90
- for (const cookie of cookies) {
91
- _res.headers.append("set-cookie", cookie);
85
+ try {
86
+ for (const [k, v] of this.#res.headers.entries()) {
87
+ if (k === "content-type") {
88
+ continue;
92
89
  }
90
+ if (k === "set-cookie") {
91
+ const cookies = this.#res.headers.getSetCookie();
92
+ _res.headers.delete("set-cookie");
93
+ for (const cookie of cookies) {
94
+ _res.headers.append("set-cookie", cookie);
95
+ }
96
+ } else {
97
+ _res.headers.set(k, v);
98
+ }
99
+ }
100
+ } catch (e) {
101
+ if (e instanceof TypeError && e.message.includes("immutable")) {
102
+ this.res = new Response(_res.body, {
103
+ headers: _res.headers,
104
+ status: _res.status
105
+ });
106
+ return;
93
107
  } else {
94
- _res.headers.set(k, v);
108
+ throw e;
95
109
  }
96
110
  }
97
111
  }
@@ -235,14 +249,9 @@ class Context {
235
249
  this.#preparedHeaders ??= {};
236
250
  this.#preparedHeaders["content-type"] = "text/html; charset=UTF-8";
237
251
  if (typeof html === "object") {
238
- if (!(html instanceof Promise)) {
239
- html = html.toString();
240
- }
241
- if (html instanceof Promise) {
242
- return html.then((html2) => (0, import_html.resolveCallback)(html2, import_html.HtmlEscapedCallbackPhase.Stringify, false, {})).then((html2) => {
243
- return typeof arg === "number" ? this.newResponse(html2, arg, headers) : this.newResponse(html2, arg);
244
- });
245
- }
252
+ return (0, import_html.resolveCallback)(html, import_html.HtmlEscapedCallbackPhase.Stringify, false, {}).then((html2) => {
253
+ return typeof arg === "number" ? this.newResponse(html2, arg, headers) : this.newResponse(html2, arg);
254
+ });
246
255
  }
247
256
  return typeof arg === "number" ? this.newResponse(html, arg, headers) : this.newResponse(html, arg);
248
257
  };
@@ -23,17 +23,19 @@ __export(sse_exports, {
23
23
  });
24
24
  module.exports = __toCommonJS(sse_exports);
25
25
  var import_stream = require("../../utils/stream");
26
+ var import_html = require("../../utils/html");
26
27
  class SSEStreamingApi extends import_stream.StreamingApi {
27
28
  constructor(writable, readable) {
28
29
  super(writable, readable);
29
30
  }
30
31
  async writeSSE(message) {
31
- const data = message.data.split("\n").map((line) => {
32
+ const data = await (0, import_html.resolveCallback)(message.data, import_html.HtmlEscapedCallbackPhase.Stringify, false, {});
33
+ const dataLines = data.split("\n").map((line) => {
32
34
  return `data: ${line}`;
33
35
  }).join("\n");
34
36
  const sseData = [
35
37
  message.event && `event: ${message.event}`,
36
- data,
38
+ dataLines,
37
39
  message.id && `id: ${message.id}`,
38
40
  message.retry && `retry: ${message.retry}`
39
41
  ].filter(Boolean).join("\n") + "\n\n";
@@ -271,7 +271,7 @@ const jsxFn = (tag, props, children) => {
271
271
  props,
272
272
  children
273
273
  );
274
- } else if (tag === "svg") {
274
+ } else if (tag === "svg" || tag === "head") {
275
275
  nameSpaceContext ||= (0, import_context.createContext)("");
276
276
  return new JSXNode(tag, props, [
277
277
  new JSXFunctionNode(
@@ -71,11 +71,11 @@ const ErrorBoundary = async ({ children, fallback, fallbackRender, onError }) =>
71
71
  const index = errorBoundaryCounter++;
72
72
  const replaceRe = RegExp(`(<template id="E:${index}"></template>.*?)(.*?)(<!--E:${index}-->)`);
73
73
  const caught = false;
74
- const catchCallback = ({ error, buffer }) => {
74
+ const catchCallback = ({ error: error2, buffer }) => {
75
75
  if (caught) {
76
76
  return "";
77
77
  }
78
- const fallbackResString = fallbackRes(error);
78
+ const fallbackResString = fallbackRes(error2);
79
79
  if (buffer) {
80
80
  buffer[0] = buffer[0].replace(replaceRe, fallbackResString);
81
81
  }
@@ -89,12 +89,17 @@ d.replaceWith(c.content)
89
89
  })(document)
90
90
  <\/script>`;
91
91
  };
92
+ let error;
93
+ const promiseAll = Promise.all(resArray).catch((e) => error = e);
92
94
  return (0, import_html.raw)(`<template id="E:${index}"></template><!--E:${index}-->`, [
93
95
  ({ phase, buffer, context }) => {
94
96
  if (phase === import_html2.HtmlEscapedCallbackPhase.BeforeStream) {
95
97
  return;
96
98
  }
97
- return Promise.all(resArray).then(async (htmlArray) => {
99
+ return promiseAll.then(async (htmlArray) => {
100
+ if (error) {
101
+ throw error;
102
+ }
98
103
  htmlArray = htmlArray.flat();
99
104
  const content = htmlArray.join("");
100
105
  let html = buffer ? "" : `<template data-hono-target="E:${index}">${content}</template><script>
@@ -150,10 +155,10 @@ d.remove()
150
155
  <\/script>`),
151
156
  content2.callbacks
152
157
  );
153
- }).catch((error) => catchCallback({ error, buffer }))
158
+ }).catch((error2) => catchCallback({ error: error2, buffer }))
154
159
  );
155
160
  return (0, import_html.raw)(html, promises);
156
- }).catch((error) => catchCallback({ error, buffer }));
161
+ }).catch((error2) => catchCallback({ error: error2, buffer }));
157
162
  }
158
163
  ]);
159
164
  } else {
@@ -107,8 +107,15 @@ const documentMetadataTag = (tag, children, props, sort) => {
107
107
  };
108
108
  const title = ({ children, ...props }) => {
109
109
  const nameSpaceContext = (0, import_base.getNameSpaceContext)();
110
- if (nameSpaceContext && (0, import_context.useContext)(nameSpaceContext) === "svg") {
111
- new import_base.JSXNode("title", props, (0, import_children.toArray)(children ?? []));
110
+ if (nameSpaceContext) {
111
+ const context = (0, import_context.useContext)(nameSpaceContext);
112
+ if (context === "svg" || context === "head") {
113
+ return new import_base.JSXNode(
114
+ "title",
115
+ props,
116
+ (0, import_children.toArray)(children ?? [])
117
+ );
118
+ }
112
119
  }
113
120
  return documentMetadataTag("title", children, props, false);
114
121
  };
@@ -116,7 +123,8 @@ const script = ({
116
123
  children,
117
124
  ...props
118
125
  }) => {
119
- if (["src", "async"].some((k) => !props[k])) {
126
+ const nameSpaceContext = (0, import_base.getNameSpaceContext)();
127
+ if (["src", "async"].some((k) => !props[k]) || nameSpaceContext && (0, import_context.useContext)(nameSpaceContext) === "head") {
120
128
  return returnWithoutSpecialBehavior("script", children, props);
121
129
  }
122
130
  return documentMetadataTag("script", children, props, false);
@@ -139,6 +147,10 @@ const link = ({ children, ...props }) => {
139
147
  return documentMetadataTag("link", children, props, "precedence" in props);
140
148
  };
141
149
  const meta = ({ children, ...props }) => {
150
+ const nameSpaceContext = (0, import_base.getNameSpaceContext)();
151
+ if (nameSpaceContext && (0, import_context.useContext)(nameSpaceContext) === "head") {
152
+ return returnWithoutSpecialBehavior("meta", children, props);
153
+ }
142
154
  return documentMetadataTag("meta", children, props, false);
143
155
  };
144
156
  const newJSXNode = (tag, { children, ...props }) => new import_base.JSXNode(tag, props, (0, import_children.toArray)(children ?? []));
@@ -22,26 +22,8 @@ __export(basic_auth_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(basic_auth_exports);
24
24
  var import_http_exception = require("../../http-exception");
25
+ var import_basic_auth = require("../../utils/basic-auth");
25
26
  var import_buffer = require("../../utils/buffer");
26
- var import_encode = require("../../utils/encode");
27
- const CREDENTIALS_REGEXP = /^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([A-Za-z0-9._~+/-]+=*) *$/;
28
- const USER_PASS_REGEXP = /^([^:]*):(.*)$/;
29
- const utf8Decoder = new TextDecoder();
30
- const auth = (req) => {
31
- const match = CREDENTIALS_REGEXP.exec(req.header("Authorization") || "");
32
- if (!match) {
33
- return void 0;
34
- }
35
- let userPass = void 0;
36
- try {
37
- userPass = USER_PASS_REGEXP.exec(utf8Decoder.decode((0, import_encode.decodeBase64)(match[1])));
38
- } catch {
39
- }
40
- if (!userPass) {
41
- return void 0;
42
- }
43
- return { username: userPass[1], password: userPass[2] };
44
- };
45
27
  const basicAuth = (options, ...users) => {
46
28
  const usernamePasswordInOptions = "username" in options && "password" in options;
47
29
  const verifyUserInOptions = "verifyUser" in options;
@@ -53,11 +35,14 @@ const basicAuth = (options, ...users) => {
53
35
  if (!options.realm) {
54
36
  options.realm = "Secure Area";
55
37
  }
38
+ if (!options.invalidUserMessage) {
39
+ options.invalidUserMessage = "Unauthorized";
40
+ }
56
41
  if (usernamePasswordInOptions) {
57
42
  users.unshift({ username: options.username, password: options.password });
58
43
  }
59
44
  return async function basicAuth2(ctx, next) {
60
- const requestUser = auth(ctx.req);
45
+ const requestUser = (0, import_basic_auth.auth)(ctx.req.raw);
61
46
  if (requestUser) {
62
47
  if (verifyUserInOptions) {
63
48
  if (await options.verifyUser(requestUser.username, requestUser.password, ctx)) {
@@ -77,13 +62,19 @@ const basicAuth = (options, ...users) => {
77
62
  }
78
63
  }
79
64
  }
80
- const res = new Response("Unauthorized", {
81
- status: 401,
65
+ const status = 401;
66
+ const headers = {
67
+ "WWW-Authenticate": 'Basic realm="' + options.realm?.replace(/"/g, '\\"') + '"'
68
+ };
69
+ const responseMessage = typeof options.invalidUserMessage === "function" ? await options.invalidUserMessage(ctx) : options.invalidUserMessage;
70
+ const res = typeof responseMessage === "string" ? new Response(responseMessage, { status, headers }) : new Response(JSON.stringify(responseMessage), {
71
+ status,
82
72
  headers: {
83
- "WWW-Authenticate": 'Basic realm="' + options.realm?.replace(/"/g, '\\"') + '"'
73
+ ...headers,
74
+ "content-type": "application/json; charset=UTF-8"
84
75
  }
85
76
  });
86
- throw new import_http_exception.HTTPException(401, { res });
77
+ throw new import_http_exception.HTTPException(status, { res });
87
78
  };
88
79
  };
89
80
  // Annotate the CommonJS export names for ESM import in node:
@@ -40,26 +40,38 @@ const bearerAuth = (options) => {
40
40
  const prefixRegexStr = options.prefix === "" ? "" : `${options.prefix} +`;
41
41
  const regexp = new RegExp(`^${prefixRegexStr}(${TOKEN_STRINGS}) *$`);
42
42
  const wwwAuthenticatePrefix = options.prefix === "" ? "" : `${options.prefix} `;
43
+ const throwHTTPException = async (c, status, wwwAuthenticateHeader, messageOption) => {
44
+ const headers = {
45
+ "WWW-Authenticate": wwwAuthenticateHeader
46
+ };
47
+ const responseMessage = typeof messageOption === "function" ? await messageOption(c) : messageOption;
48
+ const res = typeof responseMessage === "string" ? new Response(responseMessage, { status, headers }) : new Response(JSON.stringify(responseMessage), {
49
+ status,
50
+ headers: {
51
+ ...headers,
52
+ "content-type": "application/json; charset=UTF-8"
53
+ }
54
+ });
55
+ throw new import_http_exception.HTTPException(status, { res });
56
+ };
43
57
  return async function bearerAuth2(c, next) {
44
58
  const headerToken = c.req.header(options.headerName || HEADER);
45
59
  if (!headerToken) {
46
- const res = new Response("Unauthorized", {
47
- status: 401,
48
- headers: {
49
- "WWW-Authenticate": `${wwwAuthenticatePrefix}realm="` + realm + '"'
50
- }
51
- });
52
- throw new import_http_exception.HTTPException(401, { res });
60
+ await throwHTTPException(
61
+ c,
62
+ 401,
63
+ `${wwwAuthenticatePrefix}realm="${realm}"`,
64
+ options.noAuthenticationHeaderMessage || "Unauthorized"
65
+ );
53
66
  } else {
54
67
  const match = regexp.exec(headerToken);
55
68
  if (!match) {
56
- const res = new Response("Bad Request", {
57
- status: 400,
58
- headers: {
59
- "WWW-Authenticate": `${wwwAuthenticatePrefix}error="invalid_request"`
60
- }
61
- });
62
- throw new import_http_exception.HTTPException(400, { res });
69
+ await throwHTTPException(
70
+ c,
71
+ 400,
72
+ `${wwwAuthenticatePrefix}error="invalid_request"`,
73
+ options.invalidAuthenticationHeaderMessage || "Bad Request"
74
+ );
63
75
  } else {
64
76
  let equal = false;
65
77
  if ("verifyToken" in options) {
@@ -75,13 +87,12 @@ const bearerAuth = (options) => {
75
87
  }
76
88
  }
77
89
  if (!equal) {
78
- const res = new Response("Unauthorized", {
79
- status: 401,
80
- headers: {
81
- "WWW-Authenticate": `${wwwAuthenticatePrefix}error="invalid_token"`
82
- }
83
- });
84
- throw new import_http_exception.HTTPException(401, { res });
90
+ await throwHTTPException(
91
+ c,
92
+ 401,
93
+ `${wwwAuthenticatePrefix}error="invalid_token"`,
94
+ options.invalidTokenMessage || "Unauthorized"
95
+ );
85
96
  }
86
97
  }
87
98
  }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var context_storage_exports = {};
20
+ __export(context_storage_exports, {
21
+ contextStorage: () => contextStorage,
22
+ getContext: () => getContext
23
+ });
24
+ module.exports = __toCommonJS(context_storage_exports);
25
+ var import_node_async_hooks = require("node:async_hooks");
26
+ const asyncLocalStorage = new import_node_async_hooks.AsyncLocalStorage();
27
+ const contextStorage = () => {
28
+ return async function contextStorage2(c, next) {
29
+ await asyncLocalStorage.run(c, next);
30
+ };
31
+ };
32
+ const getContext = () => {
33
+ const context = asyncLocalStorage.getStore();
34
+ if (!context) {
35
+ throw new Error("Context is not available");
36
+ }
37
+ return context;
38
+ };
39
+ // Annotate the CommonJS export names for ESM import in node:
40
+ 0 && (module.exports = {
41
+ contextStorage,
42
+ getContext
43
+ });
@@ -45,6 +45,7 @@ const createRenderer = (c, Layout, component, options) => (children, props) => {
45
45
  if (options.stream === true) {
46
46
  c.header("Transfer-Encoding", "chunked");
47
47
  c.header("Content-Type", "text/html; charset=UTF-8");
48
+ c.header("Content-Encoding", "Identity");
48
49
  } else {
49
50
  for (const [key, value] of Object.entries(options.stream)) {
50
51
  c.header(key, value);
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var permissions_policy_exports = {};
16
+ module.exports = __toCommonJS(permissions_policy_exports);
@@ -50,7 +50,8 @@ const DEFAULT_OPTIONS = {
50
50
  xFrameOptions: true,
51
51
  xPermittedCrossDomainPolicies: true,
52
52
  xXssProtection: true,
53
- removePoweredBy: true
53
+ removePoweredBy: true,
54
+ permissionsPolicy: {}
54
55
  };
55
56
  const generateNonce = () => {
56
57
  const buffer = new Uint8Array(16);
@@ -76,6 +77,12 @@ const secureHeaders = (customOptions) => {
76
77
  }
77
78
  headersToSet.push(["Content-Security-Policy", value]);
78
79
  }
80
+ if (options.permissionsPolicy && Object.keys(options.permissionsPolicy).length > 0) {
81
+ headersToSet.push([
82
+ "Permissions-Policy",
83
+ getPermissionsPolicyDirectives(options.permissionsPolicy)
84
+ ]);
85
+ }
79
86
  if (options.reportingEndpoints) {
80
87
  headersToSet.push(["Reporting-Endpoints", getReportingEndpoints(options.reportingEndpoints)]);
81
88
  }
@@ -135,6 +142,28 @@ function getCSPDirectives(contentSecurityPolicy) {
135
142
  resultValues
136
143
  ];
137
144
  }
145
+ function getPermissionsPolicyDirectives(policy) {
146
+ return Object.entries(policy).map(([directive, value]) => {
147
+ const kebabDirective = camelToKebab(directive);
148
+ if (typeof value === "boolean") {
149
+ return `${kebabDirective}=${value ? "*" : "none"}`;
150
+ }
151
+ if (Array.isArray(value)) {
152
+ if (value.length === 0) {
153
+ return `${kebabDirective}=()`;
154
+ }
155
+ if (value.length === 1 && (value[0] === "*" || value[0] === "none")) {
156
+ return `${kebabDirective}=${value[0]}`;
157
+ }
158
+ const allowlist = value.map((item) => ["self", "src"].includes(item) ? item : `"${item}"`);
159
+ return `${kebabDirective}=(${allowlist.join(" ")})`;
160
+ }
161
+ return "";
162
+ }).filter(Boolean).join(", ");
163
+ }
164
+ function camelToKebab(str) {
165
+ return str.replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase();
166
+ }
138
167
  function getReportingEndpoints(reportingEndpoints = []) {
139
168
  return reportingEndpoints.map((endpoint) => `${endpoint.name}="${endpoint.url}"`).join(", ");
140
169
  }
@@ -23,6 +23,11 @@ __export(serve_static_exports, {
23
23
  module.exports = __toCommonJS(serve_static_exports);
24
24
  var import_filepath = require("../../utils/filepath");
25
25
  var import_mime = require("../../utils/mime");
26
+ const ENCODINGS = {
27
+ br: ".br",
28
+ zstd: ".zst",
29
+ gzip: ".gz"
30
+ };
26
31
  const DEFAULT_DOCUMENT = "index.html";
27
32
  const defaultPathResolve = (path) => path;
28
33
  const serveStatic = (options) => {
@@ -40,7 +45,7 @@ const serveStatic = (options) => {
40
45
  root
41
46
  });
42
47
  if (path2 && await options.isDir(path2)) {
43
- filename = filename + "/";
48
+ filename += "/";
44
49
  }
45
50
  }
46
51
  let path = (0, import_filepath.getFilePath)({
@@ -56,34 +61,46 @@ const serveStatic = (options) => {
56
61
  path = pathResolve(path);
57
62
  let content = await getContent(path, c);
58
63
  if (!content) {
59
- let pathWithOutDefaultDocument = (0, import_filepath.getFilePathWithoutDefaultDocument)({
64
+ let pathWithoutDefaultDocument = (0, import_filepath.getFilePathWithoutDefaultDocument)({
60
65
  filename,
61
66
  root
62
67
  });
63
- if (!pathWithOutDefaultDocument) {
68
+ if (!pathWithoutDefaultDocument) {
64
69
  return await next();
65
70
  }
66
- pathWithOutDefaultDocument = pathResolve(pathWithOutDefaultDocument);
67
- if (pathWithOutDefaultDocument !== path) {
68
- content = await getContent(pathWithOutDefaultDocument, c);
71
+ pathWithoutDefaultDocument = pathResolve(pathWithoutDefaultDocument);
72
+ if (pathWithoutDefaultDocument !== path) {
73
+ content = await getContent(pathWithoutDefaultDocument, c);
69
74
  if (content) {
70
- path = pathWithOutDefaultDocument;
75
+ path = pathWithoutDefaultDocument;
71
76
  }
72
77
  }
73
78
  }
74
79
  if (content instanceof Response) {
75
80
  return c.newResponse(content.body, content);
76
81
  }
82
+ const mimeType = options.mimes ? (0, import_mime.getMimeType)(path, options.mimes) ?? (0, import_mime.getMimeType)(path) : (0, import_mime.getMimeType)(path);
83
+ if (mimeType) {
84
+ c.header("Content-Type", mimeType);
85
+ }
77
86
  if (content) {
78
- let mimeType;
79
- if (options.mimes) {
80
- mimeType = (0, import_mime.getMimeType)(path, options.mimes) ?? (0, import_mime.getMimeType)(path);
81
- } else {
82
- mimeType = (0, import_mime.getMimeType)(path);
83
- }
84
- if (mimeType) {
85
- c.header("Content-Type", mimeType);
87
+ if (options.precompressed) {
88
+ const acceptEncodings = c.req.header("Accept-Encoding")?.split(",").map((encoding) => encoding.trim()).filter(
89
+ (encoding) => Object.hasOwn(ENCODINGS, encoding)
90
+ ).sort(
91
+ (a, b) => Object.keys(ENCODINGS).indexOf(a) - Object.keys(ENCODINGS).indexOf(b)
92
+ ) ?? [];
93
+ for (const encoding of acceptEncodings) {
94
+ const compressedContent = await getContent(path + ENCODINGS[encoding], c);
95
+ if (compressedContent) {
96
+ content = compressedContent;
97
+ c.header("Content-Encoding", encoding);
98
+ c.header("Vary", "Accept-Encoding", { append: true });
99
+ break;
100
+ }
101
+ }
86
102
  }
103
+ await options.onFound?.(path, c);
87
104
  return c.body(content);
88
105
  }
89
106
  await options.onNotFound?.(path, c);
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var basic_auth_exports = {};
20
+ __export(basic_auth_exports, {
21
+ auth: () => auth
22
+ });
23
+ module.exports = __toCommonJS(basic_auth_exports);
24
+ var import_encode = require("./encode");
25
+ const CREDENTIALS_REGEXP = /^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([A-Za-z0-9._~+/-]+=*) *$/;
26
+ const USER_PASS_REGEXP = /^([^:]*):(.*)$/;
27
+ const utf8Decoder = new TextDecoder();
28
+ const auth = (req) => {
29
+ const match = CREDENTIALS_REGEXP.exec(req.headers.get("Authorization") || "");
30
+ if (!match) {
31
+ return void 0;
32
+ }
33
+ let userPass = void 0;
34
+ try {
35
+ userPass = USER_PASS_REGEXP.exec(utf8Decoder.decode((0, import_encode.decodeBase64)(match[1])));
36
+ } catch {
37
+ }
38
+ if (!userPass) {
39
+ return void 0;
40
+ }
41
+ return { username: userPass[1], password: userPass[2] };
42
+ };
43
+ // Annotate the CommonJS export names for ESM import in node:
44
+ 0 && (module.exports = {
45
+ auth
46
+ });
@@ -26,7 +26,7 @@ const parseBody = async (request, options = /* @__PURE__ */ Object.create(null))
26
26
  const { all = false, dot = false } = options;
27
27
  const headers = request instanceof import_request.HonoRequest ? request.raw.headers : request.headers;
28
28
  const contentType = headers.get("Content-Type");
29
- if (contentType !== null && contentType.startsWith("multipart/form-data") || contentType !== null && contentType.startsWith("application/x-www-form-urlencoded")) {
29
+ if (contentType?.startsWith("multipart/form-data") || contentType?.startsWith("application/x-www-form-urlencoded")) {
30
30
  return parseFormData(request, { all, dot });
31
31
  }
32
32
  return {};
@@ -112,6 +112,14 @@ const resolveCallbackSync = (str) => {
112
112
  return buffer[0];
113
113
  };
114
114
  const resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {
115
+ if (typeof str === "object" && !(str instanceof String)) {
116
+ if (!(str instanceof Promise)) {
117
+ str = str.toString();
118
+ }
119
+ if (str instanceof Promise) {
120
+ str = await str;
121
+ }
122
+ }
115
123
  const callbacks = str.callbacks;
116
124
  if (!callbacks?.length) {
117
125
  return Promise.resolve(str);