acrylblends 1.0.3 → 1.0.5

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 (256) hide show
  1. package/docs/framework/agents/index.md +6 -4
  2. package/docs/framework/workshop/README.md +3 -3
  3. package/docs/framework/workshop/facilitator.md +1 -0
  4. package/lib/accept-CNNHu4Cf.js +193 -0
  5. package/lib/{accepts-XBuZPVRI.js → accepts-Btq83xPd.js} +1 -1
  6. package/lib/accepts.js +1 -1
  7. package/lib/acryl-base.js +1 -331
  8. package/lib/adapter-CaZ6FZUU.js +37 -0
  9. package/lib/adapter.js +1 -36
  10. package/lib/{app-BM0rTCp7.js → app-CQcGWjrk.js} +4 -4
  11. package/lib/app-base-CpRMZL4v.js +332 -0
  12. package/lib/{audit-BXnYsLge.d.ts → audit-D5z9uwOP.d.ts} +14 -1
  13. package/lib/{base-NSN_SNv-.js → base-D_6kfOr-.js} +1 -1
  14. package/lib/basic-auth-C0HTRpXh.js +20 -0
  15. package/lib/basic-auth-DFLflXNe.js +95 -0
  16. package/lib/basic-auth.js +1 -94
  17. package/lib/bearer-auth-Cwxt-vgS.js +94 -0
  18. package/lib/bearer-auth.js +1 -93
  19. package/lib/body-D7f4e7w_.js +94 -0
  20. package/lib/body-limit-WmIjWRLt.js +66 -0
  21. package/lib/body-limit.js +1 -65
  22. package/lib/buffer-CkEOJCcY.js +51 -0
  23. package/lib/bun.js +3 -3
  24. package/lib/cache-B0bHH7s5.js +169 -0
  25. package/lib/cache.js +1 -168
  26. package/lib/{client-DDLXwhBo.js → client-BrMFnMEd.js} +1 -1
  27. package/lib/{client-modules-CLvWOjbd.js → client-modules-drj4mjhj.js} +4 -4
  28. package/lib/client.js +1 -1
  29. package/lib/color-COa3DplO.js +37 -0
  30. package/lib/combine-TWuGdcbC.js +137 -0
  31. package/lib/combine.js +1 -136
  32. package/lib/{components-cEW3ovK0.js → components-B2ka9Ax1.js} +4 -4
  33. package/lib/{components-JXVm45kK.js → components-PVXwaOaC.js} +2 -2
  34. package/lib/compress-BzWqsHF1.js +79 -0
  35. package/lib/compress-C-9vK4B2.d.ts +11 -0
  36. package/lib/compress-DCyrrQ5D.js +11 -0
  37. package/lib/compress.d.ts +1 -1
  38. package/lib/compress.js +2 -78
  39. package/lib/concurrent-DrF3d2c-.js +30 -0
  40. package/lib/conninfo-BCROmdnf.d.ts +11 -0
  41. package/lib/conninfo-CWJwieh4.js +19 -0
  42. package/lib/constants-CBSEudrb.js +7 -0
  43. package/lib/{context-DDO0cfuP.js → context-CkOh7Ygv.js} +2 -2
  44. package/lib/context-storage-iuPoI_CR.js +52 -0
  45. package/lib/context-storage.js +1 -51
  46. package/lib/cookie-B1LlkzOe.js +125 -0
  47. package/lib/cookie-BHIduy2T.js +82 -0
  48. package/lib/cookie.js +1 -81
  49. package/lib/cordis/db-sqlite.d.ts +1 -1
  50. package/lib/cordis/door.d.ts +17 -3
  51. package/lib/cordis/door.js +123 -16
  52. package/lib/cordis/inspect.js +4 -4
  53. package/lib/cordis/kv-db.d.ts +1 -1
  54. package/lib/cordis/kv-db.js +1 -1
  55. package/lib/cordis/kv-json.d.ts +1 -1
  56. package/lib/cordis/kv-json.js +1 -1
  57. package/lib/cordis/node-server.d.ts +1 -1
  58. package/lib/cordis/node-server.js +3 -116
  59. package/lib/cordis/router-http.d.ts +1 -1
  60. package/lib/cordis/router-http.js +2 -2
  61. package/lib/cordis/router.d.ts +1 -1
  62. package/lib/cordis/router.js +4 -4
  63. package/lib/cordis/state.js +7 -7
  64. package/lib/{core-CaY8XDZ6.js → core-BOu1jYua.js} +1 -1
  65. package/lib/cors-CbAQ-8qU.js +106 -0
  66. package/lib/cors.js +1 -105
  67. package/lib/crypto-iVP-N-tS.js +34 -0
  68. package/lib/csrf-DqqOZtgy.js +101 -0
  69. package/lib/csrf.js +1 -100
  70. package/lib/css-C5CPVMvM.js +134 -0
  71. package/lib/css.js +2 -133
  72. package/lib/db/sqlite.d.ts +1 -25
  73. package/lib/dev-CLgu-JRe.js +50 -0
  74. package/lib/dev.js +1 -49
  75. package/lib/dom-YjND9Jt3.js +74 -0
  76. package/lib/door/host.d.ts +2 -2
  77. package/lib/door/host.js +1 -1
  78. package/lib/door/page.d.ts +12 -1
  79. package/lib/door/page.js +22 -1
  80. package/lib/door/protocol.d.ts +2 -2
  81. package/lib/door/protocol.js +2 -197
  82. package/lib/encode-BhbNLrjp.js +33 -0
  83. package/lib/factory-CLdSf-xS.js +30 -0
  84. package/lib/factory.js +1 -29
  85. package/lib/handler-C0wZ0KwO.js +12 -0
  86. package/lib/helpers-BEjcdbgw.js +5 -0
  87. package/lib/{hooks-w72iLeFR.js → hooks-CfC07ri0.js} +1 -1
  88. package/lib/html-CgNa89xw.js +31 -0
  89. package/lib/html-DGExBFNg.js +103 -0
  90. package/lib/html.d.ts +2 -5
  91. package/lib/html.js +2 -30
  92. package/lib/http-exception-MjWkT89V.js +54 -0
  93. package/lib/http-exception.js +1 -53
  94. package/lib/{index-D4nVR3wu.d.ts → index-DMOtA-NJ.d.ts} +7 -1
  95. package/lib/index-dAzQsJG9.d.ts +6 -0
  96. package/lib/index.js +3 -3
  97. package/lib/ip-restriction-jKtKCRM_.js +166 -0
  98. package/lib/ip-restriction.js +1 -165
  99. package/lib/ipaddr-DCoIo528.js +238 -0
  100. package/lib/jsx/dom/client.js +1 -1
  101. package/lib/jsx/dom/jsx-dev-runtime.js +1 -1
  102. package/lib/jsx/dom/jsx-runtime.js +2 -1
  103. package/lib/jsx/dom/server.js +3 -3
  104. package/lib/jsx/dom.js +5 -73
  105. package/lib/jsx/jsx-dev-runtime.d.ts +2 -5
  106. package/lib/jsx/jsx-dev-runtime.js +2 -13
  107. package/lib/jsx/jsx-runtime.d.ts +2 -2
  108. package/lib/jsx/jsx-runtime.js +4 -4
  109. package/lib/jsx/streaming.js +1 -1
  110. package/lib/jsx-CUQgttxU.js +47 -0
  111. package/lib/jsx-dev-runtime-B4WTyq8r.js +14 -0
  112. package/lib/{jsx-dev-runtime-CWLRA2F3.js → jsx-dev-runtime-BLfA-EhI.js} +2 -2
  113. package/lib/jsx-dev-runtime-CGt4kNYr.d.ts +6 -0
  114. package/lib/jsx-renderer-CfifM4hT.js +99 -0
  115. package/lib/jsx-renderer.js +1 -98
  116. package/lib/jsx-runtime-COCRWBJ9.js +2 -0
  117. package/lib/jsx.js +5 -46
  118. package/lib/{jwk-LkzeoaU8.js → jwk-CDPpYQpD.js} +3 -3
  119. package/lib/jwk.js +1 -1
  120. package/lib/{jwt-BGJBw5AF.js → jwt-BXoIA2fQ.js} +2 -2
  121. package/lib/{jwt-CskdhEW9.js → jwt-lyS4Q3mT.js} +3 -3
  122. package/lib/jwt.js +2 -2
  123. package/lib/kv/db.d.ts +1 -10
  124. package/lib/kv/db.js +1 -56
  125. package/lib/kv/json.d.ts +1 -9
  126. package/lib/kv/json.js +1 -223
  127. package/lib/{language-D9a4zqvn.js → language-DeGRtLJt.js} +2 -2
  128. package/lib/language.js +1 -1
  129. package/lib/{linear-router-C3FPib6h.js → linear-router-CmZn_JLV.js} +2 -2
  130. package/lib/logger-Ckxu8diz.js +54 -0
  131. package/lib/logger.js +1 -53
  132. package/lib/loopback-only-BbasoF7R.js +90 -0
  133. package/lib/loopback-only.js +1 -89
  134. package/lib/method-not-allowed-TO2_hnfl.js +82 -0
  135. package/lib/method-not-allowed.js +1 -81
  136. package/lib/method-override-DnZKfJbV.js +103 -0
  137. package/lib/method-override.js +1 -102
  138. package/lib/middleware-BfG_fp8P.js +12 -0
  139. package/lib/mime-BnScXkQn.js +77 -0
  140. package/lib/mount-QkK_3NtJ.js +67 -0
  141. package/lib/mount.js +1 -66
  142. package/lib/node-server/conninfo.d.ts +1 -10
  143. package/lib/node-server/conninfo.js +1 -18
  144. package/lib/node-server/register.js +25 -2
  145. package/lib/node-server/serve-static.d.ts +1 -17
  146. package/lib/node-server/serve-static.js +1 -1
  147. package/lib/node-server.d.ts +2 -2
  148. package/lib/node-server.js +4 -4
  149. package/lib/open-BqK4D2Wn.d.ts +11 -0
  150. package/lib/open-But9E2rL.js +57 -0
  151. package/lib/open-CULVJgOc.d.ts +12 -0
  152. package/lib/open-Dp4sxspc.js +224 -0
  153. package/lib/open-T7kgbO2O.d.ts +27 -0
  154. package/lib/{pattern-router-HZxjDGEQ.js → pattern-router-CJBecJBd.js} +1 -1
  155. package/lib/powered-by-CXQNjNIX.js +24 -0
  156. package/lib/powered-by.js +1 -23
  157. package/lib/pretty-json-CeC1yvYt.js +39 -0
  158. package/lib/pretty-json.js +1 -38
  159. package/lib/{protocol-BizVt4J6.d.ts → protocol-C37gPePD.d.ts} +9 -2
  160. package/lib/protocol-Dt0AZBOz.js +213 -0
  161. package/lib/proxy-C2w_mH7k.js +121 -0
  162. package/lib/proxy.js +1 -120
  163. package/lib/quick.js +4 -4
  164. package/lib/{reg-exp-router-DC1R3qoY.js → reg-exp-router-C_WV84eV.js} +2 -2
  165. package/lib/request-DvMgGtlW.js +323 -0
  166. package/lib/request.js +1 -322
  167. package/lib/route-D8m-a2I1.js +126 -0
  168. package/lib/route.js +1 -125
  169. package/lib/router/linear-router.js +1 -1
  170. package/lib/router/pattern-router.js +1 -1
  171. package/lib/router/reg-exp-router.js +1 -1
  172. package/lib/router/smart-router.js +1 -1
  173. package/lib/router/trie-router.js +1 -1
  174. package/lib/router-Be_o6nFu.js +35 -0
  175. package/lib/router.js +1 -34
  176. package/lib/schema-validator-EK3cUUCt.js +22 -0
  177. package/lib/schema-validator.js +1 -21
  178. package/lib/{secure-headers-mP3j6Hm6.js → secure-headers-C5ZOcXqk.js} +1 -1
  179. package/lib/secure-headers.js +1 -1
  180. package/lib/serve-static-B7sqnftW.d.ts +18 -0
  181. package/lib/{serve-static-3uo_9rjF.js → serve-static-C1Nkj_xB.js} +2 -2
  182. package/lib/{serve-static-Cp5adzGo.js → serve-static-EiJf1zkO.js} +4 -4
  183. package/lib/serve-static.js +1 -1
  184. package/lib/{smart-router-C5CZ5XSM.js → smart-router-BjxkDQCE.js} +1 -1
  185. package/lib/{snapshot-text-DmE0YfeZ.js → snapshot-text-Bbx43mN0.js} +57 -9
  186. package/lib/{src-2Z4mJg0x.js → src-BTBm4OPD.js} +22 -6
  187. package/lib/src-LX0E4ec4.js +117 -0
  188. package/lib/{ssg-BwjcA-Ge.js → ssg-BEl7MHqg.js} +5 -5
  189. package/lib/ssg.js +1 -1
  190. package/lib/stream-CzM_SHkq.js +88 -0
  191. package/lib/{streaming-DHST3Scg.js → streaming-BlDaph8U.js} +3 -3
  192. package/lib/streaming.js +1 -1
  193. package/lib/{test-websocket-D8WWqE5G.js → test-websocket-BLeEvybj.js} +2 -2
  194. package/lib/testing-BQx5T_UX.js +17 -0
  195. package/lib/testing.js +1 -16
  196. package/lib/timeout-FeqZ9A-0.js +42 -0
  197. package/lib/timeout.js +1 -41
  198. package/lib/tiny.js +2 -2
  199. package/lib/trailing-slash-DwTx-1sk.js +85 -0
  200. package/lib/trailing-slash.js +1 -84
  201. package/lib/{trie-router-CfFbapzL.js → trie-router-JydLKUa2.js} +2 -2
  202. package/lib/url-BPh4d6j-.js +197 -0
  203. package/lib/utils/accept.js +1 -192
  204. package/lib/utils/basic-auth.js +1 -19
  205. package/lib/utils/body.js +1 -93
  206. package/lib/utils/buffer.js +1 -50
  207. package/lib/utils/color.js +1 -36
  208. package/lib/utils/compress.d.ts +1 -10
  209. package/lib/utils/compress.js +1 -10
  210. package/lib/utils/concurrent.js +1 -29
  211. package/lib/utils/constants.js +1 -6
  212. package/lib/utils/cookie.js +1 -124
  213. package/lib/utils/crypto.js +1 -33
  214. package/lib/utils/encode.js +1 -32
  215. package/lib/utils/handler.js +1 -11
  216. package/lib/utils/html.js +1 -102
  217. package/lib/utils/ipaddr.js +1 -237
  218. package/lib/utils/jwt.js +1 -1
  219. package/lib/utils/mime.js +1 -76
  220. package/lib/utils/stream.js +1 -87
  221. package/lib/utils/url.js +1 -196
  222. package/lib/{validator-D84mq-dO.js → validator-Btiyt5VJ.js} +3 -3
  223. package/lib/validator.js +1 -1
  224. package/lib/websocket-DsyBIKVA.js +56 -0
  225. package/lib/ws.js +1 -55
  226. package/package.json +1 -1
  227. package/plugins/acryl-door/src/cli/acryl.ts +5 -3
  228. package/plugins/acryl-door/src/host/channel.ts +36 -0
  229. package/plugins/acryl-door/src/host/chat-tools.ts +2 -0
  230. package/plugins/acryl-door/src/host/door.ts +44 -5
  231. package/plugins/acryl-door/src/host/shim.ts +23 -0
  232. package/plugins/acryl-door/src/host/verbs.ts +4 -3
  233. package/plugins/acryl-door/src/index.ts +39 -3
  234. package/plugins/acryl-door/src/page/driver/driver.ts +22 -1
  235. package/plugins/acryl-door/src/page/protocol.ts +13 -2
  236. package/plugins/acryl-node-server/src/register.ts +29 -2
  237. package/plugins/acryl-router/src/service.ts +19 -1
  238. package/plugins/agent/model.ts +6 -1
  239. package/plugins/agent/tools.ts +12 -2
  240. package/plugins/agent-panel/client/describe.ts +44 -0
  241. package/plugins/agent-panel/client/main.ts +67 -35
  242. package/plugins/agent-panel/index.ts +137 -35
  243. package/plugins/desktop/attach.ts +72 -0
  244. package/plugins/desktop/main.ts +17 -4
  245. package/plugins/extensions/index.ts +3 -4
  246. package/plugins/extensions/stage.ts +33 -5
  247. package/plugins/openai/index.ts +1 -1
  248. package/shared/DEFAULTS.md +4 -0
  249. package/templates/agent/AGENTS.md +2 -0
  250. package/templates/agent/blend.yaml +4 -1
  251. package/templates/agent/src/app.ts +2 -0
  252. package/templates/agent/src/routes.ts +1 -1
  253. package/templates/agent/template.json +1 -0
  254. package/templates/agent/test/builder.test.ts +26 -21
  255. package/lib/helpers-CvTauQT5.js +0 -5
  256. package/lib/middleware-CPQ8himN.js +0 -12
@@ -1,12 +1,13 @@
1
- import { t as Acryl } from "../app-BM0rTCp7.js";
2
- import { n as clientModules } from "../client-modules-CLvWOjbd.js";
3
- import { MAX_SNAPSHOT_NODES, MAX_WAIT_MS, UiControlError, parseUiRequest } from "../door/protocol.js";
4
- import { a as INVALID_JSON, c as operatorOnly, d as AuditLog, f as auditPath, i as DOOR_PREFIX, l as privateApi, o as error, p as UiChannel, r as pageChannelRoutes, s as jsonBody, t as renderSnapshot, u as readJson } from "../snapshot-text-DmE0YfeZ.js";
5
- import "../src-2Z4mJg0x.js";
1
+ import { t as Acryl } from "../app-CQcGWjrk.js";
2
+ import { n as clientModules } from "../client-modules-drj4mjhj.js";
3
+ import { a as MAX_WAIT_MS, l as UiControlError, p as parseUiRequest, r as MAX_SNAPSHOT_NODES } from "../protocol-Dt0AZBOz.js";
4
+ import { a as INVALID_JSON, c as operatorOnly, d as AuditLog, f as auditPath, i as DOOR_PREFIX, l as privateApi, o as error, p as UiChannel, r as pageChannelRoutes, s as jsonBody, t as renderSnapshot, u as readJson } from "../snapshot-text-Bbx43mN0.js";
5
+ import "../src-BTBm4OPD.js";
6
6
  import { isAbsolute, join, relative, resolve } from "node:path";
7
7
  import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
8
8
  import z from "@deepseek-ai/schemastery";
9
9
  import { randomBytes } from "node:crypto";
10
+ import { spawnSync } from "node:child_process";
10
11
  //#region ../../plugins/acryl-door/src/host/verbs.ts
11
12
  /**
12
13
  * The one verb list (spec 049 slice 13). Every face of the door reads it: the operator client (`acryl <verb>`, its `verbs` and `brief`),
@@ -56,8 +57,8 @@ const VERB_SPECS = {
56
57
  }
57
58
  },
58
59
  "act": {
59
- usage: "act click|type|select|press|scroll|wait ...: do one thing on the page (refs from the latest look)",
60
- description: "Do one thing on the page: click, type, select, press a key, scroll, or wait for text or a control. Look again before the next act.",
60
+ usage: "act click|type|select|press|scroll|wait|reload|navigate ...: do one thing on the page (refs from the latest look)",
61
+ description: "Do one thing on the page: click, type, select, press a key, scroll, wait for text or a control, reload the page, or navigate to another path of the app. Look again before the next act.",
61
62
  arguments: {
62
63
  type: "object",
63
64
  properties: {
@@ -69,9 +70,15 @@ const VERB_SPECS = {
69
70
  "select",
70
71
  "press",
71
72
  "scroll",
72
- "wait"
73
+ "wait",
74
+ "reload",
75
+ "navigate"
73
76
  ]
74
77
  },
78
+ path: {
79
+ type: "string",
80
+ description: "navigate: a path of this app, e.g. /demo (the page must load the door script to be controlled afterwards)"
81
+ },
75
82
  ref: {
76
83
  type: "string",
77
84
  description: "a ref from the latest look, e.g. 3.12"
@@ -211,6 +218,8 @@ const SELF_KNOWLEDGE = [
211
218
  "docs",
212
219
  "plugins"
213
220
  ];
221
+ /** How long a reload or a move may take before the new page has connected to the door again. */
222
+ const PAGE_BACK_MS = 15e3;
214
223
  var Door = class {
215
224
  deps;
216
225
  constructor(deps) {
@@ -235,7 +244,7 @@ var Door = class {
235
244
  text: `done${result.target === void 0 ? "" : `: ${result.target.role} "${result.target.name}"`}${result.detail === void 0 ? "" : ` (${result.detail})`}; look again before the next act`
236
245
  };
237
246
  }
238
- case "plugin-list": return this.plugin((extensions) => extensions.list());
247
+ case "plugin-list": return this.plugin((extensions) => extensions.list(), false);
239
248
  case "plugin-add": return this.plugin((extensions) => extensions.install(this.insideApp(input.folder)));
240
249
  case "plugin-remove": {
241
250
  const name = input.name;
@@ -286,9 +295,45 @@ var Door = class {
286
295
  });
287
296
  return this.callPage("look", request, signal);
288
297
  }
289
- act(input, signal) {
298
+ async act(input, signal) {
290
299
  if (input.op === "snapshot") throw new DoorError("invalid", "use look to see the page");
291
- return this.callPage(`act ${String(input.op)}`, this.parse(input), signal);
300
+ const request = this.parse(input);
301
+ if (request.op !== "reload" && request.op !== "navigate") return await this.callPage(`act ${String(input.op)}`, request, signal);
302
+ const back = this.deps.channel.expectAttach(this.deps.pageBackMs ?? PAGE_BACK_MS);
303
+ let answer;
304
+ try {
305
+ answer = await this.callPage(`act ${request.op}`, request, signal);
306
+ } catch (cause) {
307
+ if (!(cause instanceof UiControlError && cause.code === "disconnected")) throw cause;
308
+ answer = {
309
+ ok: true,
310
+ detail: request.op === "reload" ? "reloading" : `going to ${request.path}`
311
+ };
312
+ }
313
+ if (!await back) throw new UiControlError("timeout", request.op === "reload" ? "the page did not come back after the reload" : `the page did not come back after going to ${request.path}: is there a page at that path, and does it load the door's script?`);
314
+ const landed = request.op === "navigate" ? await this.statusAt(request.path) : void 0;
315
+ return {
316
+ ...answer,
317
+ detail: `${answer.detail ?? "done"}${landed === void 0 ? "" : `; ${landed}`}; the page is back: look again`
318
+ };
319
+ }
320
+ /**
321
+ * What the app answers at a path the page was sent to, so an agent that mistyped one is told ("404 at /nope") and not left to find out from a page it did not expect.
322
+ * A second GET of the same path (a page is read-only); nothing when the app has no address or does not answer in time.
323
+ */
324
+ async statusAt(path) {
325
+ const base = this.deps.app.url;
326
+ if (base === void 0) return void 0;
327
+ try {
328
+ const response = await fetch(new URL(path, base), {
329
+ headers: { accept: "text/html" },
330
+ signal: AbortSignal.timeout(3e3)
331
+ });
332
+ await response.body?.cancel();
333
+ return response.ok ? `${response.status} at ${path}` : `${response.status} at ${path}: the app serves no page there, look at what is shown and navigate to a path it does`;
334
+ } catch {
335
+ return;
336
+ }
292
337
  }
293
338
  parse(input) {
294
339
  try {
@@ -335,10 +380,12 @@ var Door = class {
335
380
  throw cause;
336
381
  }
337
382
  }
338
- async plugin(run) {
383
+ async plugin(run, mutates = true) {
339
384
  const extensions = this.deps.extensions();
340
385
  if (extensions === void 0) throw new DoorError("unavailable", "this app has no extensions plugin, so plugins cannot be installed while it runs");
341
386
  const value = await run(extensions);
387
+ const unchanged = typeof value === "object" && value !== null && value.action === "unchanged";
388
+ if (mutates && !unchanged && this.deps.reloadOnChange !== false) await this.deps.channel.reloadAll();
342
389
  return {
343
390
  value,
344
391
  text: JSON.stringify(value, null, 2)
@@ -398,6 +445,7 @@ function registerChatTools(tools, door) {
398
445
  try {
399
446
  for (const verb of VERBS) removers.push(tools.register(chatToolName(verb), {
400
447
  description: VERB_SPECS[verb].description,
448
+ parameters: VERB_SPECS[verb].arguments,
401
449
  run: async (input) => {
402
450
  try {
403
451
  return (await door.run(verb, input ?? {}, running.signal)).text;
@@ -631,6 +679,34 @@ function operatorRoutes(prefix, secret, door, reportError) {
631
679
  }
632
680
  });
633
681
  }
682
+ /** The oldest Node the client runs on (the framework's own floor). */
683
+ const MIN_NODE = [22, 19];
684
+ /**
685
+ * A real Node for the command, when the app runs inside Electron: Electron can run the client as Node, but on macOS every such call prints a code-signing complaint on stderr,
686
+ * which lands in an agent's transcript. A `node` on the PATH that is new enough is used instead; without one, Electron runs the client. `find` is replaceable for tests.
687
+ */
688
+ function realNode(find = findNodeOnPath) {
689
+ const found = find();
690
+ if (found === void 0) return void 0;
691
+ const [major = 0, minor = 0] = found.version.replace(/^v/u, "").split(".").map(Number);
692
+ return major > MIN_NODE[0] || major === MIN_NODE[0] && minor >= MIN_NODE[1] ? found.path : void 0;
693
+ }
694
+ function findNodeOnPath() {
695
+ const located = spawnSync(process.platform === "win32" ? "where" : "which", ["node"], {
696
+ encoding: "utf8",
697
+ env: {
698
+ ...process.env,
699
+ ELECTRON_RUN_AS_NODE: void 0
700
+ }
701
+ });
702
+ const path = located.status === 0 ? located.stdout.split(/\r?\n/u).find((line) => line.trim() !== "")?.trim() : void 0;
703
+ if (path === void 0) return void 0;
704
+ const asked = spawnSync(path, ["--version"], { encoding: "utf8" });
705
+ return asked.status === 0 ? {
706
+ path,
707
+ version: asked.stdout.trim()
708
+ } : void 0;
709
+ }
634
710
  /** A word for `sh`: single-quoted, with each `'` closed, escaped and reopened. */
635
711
  function shWord(value) {
636
712
  return `'${value.replaceAll("'", "'\\''")}'`;
@@ -698,6 +774,7 @@ const inject = [
698
774
  ];
699
775
  const Config = z.object({
700
776
  allowInProduction: z.boolean().default(false).description("Run in production too (NODE_ENV=production); ACRYL_DOOR=off still turns it off."),
777
+ reloadOnChange: z.boolean().default(true).description("Reload the open pages after a plugin was added, removed or reloaded while the app runs."),
701
778
  tools: z.object({ expose: z.array(z.string().pattern(/^(?!door_)[A-Za-z][A-Za-z0-9_-]{0,63}$/u)).description("App tools (from the tools registry) the door offers; none by default.") })
702
779
  });
703
780
  /** The page module, served compiled from source by the app's own TypeScript. */
@@ -707,11 +784,13 @@ const CLIENT = import.meta.url.endsWith(".ts") ? join(import.meta.dirname, "cli"
707
784
  /** How the folder's `.acryl/bin/acryl` starts the client: with the Node that runs the app, and its type-stripping flag when run from source. */
708
785
  function shimTarget() {
709
786
  const fromSource = CLIENT.endsWith(".ts");
787
+ const inElectron = process.versions.electron !== void 0;
788
+ const node = inElectron ? realNode() : void 0;
710
789
  return {
711
- node: process.execPath,
790
+ node: node ?? process.execPath,
712
791
  nodeOptions: fromSource ? process.execArgv.filter((option) => /^--experimental-(strip|transform)-types$/u.test(option)) : [],
713
792
  client: CLIENT,
714
- electron: process.versions.electron !== void 0
793
+ electron: inElectron && node === void 0
715
794
  };
716
795
  }
717
796
  /** A listening address as one the operator reaches: an any-address bind is reached on loopback. */
@@ -720,6 +799,11 @@ function loopbackUrl(url) {
720
799
  if (parsed.hostname === "0.0.0.0" || parsed.hostname === "[::]") parsed.hostname = "127.0.0.1";
721
800
  return parsed.origin;
722
801
  }
802
+ const escapeHtml = (text) => text.replace(/[&<>"']/gu, (char) => `&#${char.charCodeAt(0)};`);
803
+ /** The page of a path the app does not serve, development only: what is missing, a way back, and the door's script. */
804
+ function notFoundPage(path, scriptTag) {
805
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>404 Not Found</title></head><body><main><h1>404 Not Found</h1><p>This app has no page at <code>${escapeHtml(path)}</code>.</p><p><a href="/">Back to the app</a></p></main>${scriptTag}</body></html>`;
806
+ }
723
807
  function apply(ctx, rawConfig = {}) {
724
808
  const config = Config(rawConfig);
725
809
  if (process.env.ACRYL_DOOR === "off" || process.env.NODE_ENV === "production" && config.allowInProduction !== true) return;
@@ -741,12 +825,35 @@ function apply(ctx, rawConfig = {}) {
741
825
  },
742
826
  extensions: () => ctx.get("extensions"),
743
827
  tools: () => ctx.get("tools"),
744
- expose: config.tools?.expose ?? []
828
+ expose: config.tools?.expose ?? [],
829
+ reloadOnChange: config.reloadOnChange !== false
745
830
  });
746
831
  ctx.effect(() => () => {
747
832
  channel.close();
748
833
  }, "acryl-door: page channel");
749
834
  const pageModule = new Acryl().get(`${DOOR_PREFIX}/page.js`, (c) => c.body(`import '${DOOR_PREFIX}/page/main.ts'\n`, 200, { "content-type": "text/javascript; charset=utf-8" })).route(`${DOOR_PREFIX}/page`, clientModules({ root: PAGE_DIR }));
835
+ const tag = `<script type="module" src="${DOOR_PREFIX}/page.js"><\/script>`;
836
+ const addScript = async (c, next) => {
837
+ await next();
838
+ if (!(c.res.headers.get("content-type") ?? "").includes("text/html")) {
839
+ if (c.res.status === 404 && (c.req.header("accept") ?? "").includes("text/html") && !(c.res.headers.get("content-type") ?? "").includes("json")) c.res = new Response(notFoundPage(c.req.path, tag), {
840
+ status: 404,
841
+ headers: { "content-type": "text/html; charset=utf-8" }
842
+ });
843
+ return;
844
+ }
845
+ const html = await c.res.text();
846
+ const headers = new Headers(c.res.headers);
847
+ headers.delete("content-length");
848
+ const carries = html.includes(`${DOOR_PREFIX}/page.js`);
849
+ c.res = new Response(carries ? html : html.includes("</body>") ? html.replace("</body>", `${tag}</body>`) : html + tag, {
850
+ status: c.res.status,
851
+ headers
852
+ });
853
+ };
854
+ const router = ctx.router;
855
+ if (typeof router.wrap === "function") router.wrap(addScript);
856
+ else ctx.router.route("/", new Acryl().use("*", addScript));
750
857
  ctx.router.route("/", privateApi(DOOR_PREFIX, pageModule, pageChannelRoutes(channel, { prefix: DOOR_PREFIX }), operatorRoutes(DOOR_PREFIX, secret, door, reportError), mcpRoutes(DOOR_PREFIX, secret, door, reportError)));
751
858
  ctx.inject(["tools"], (inner) => {
752
859
  inner.effect(() => registerChatTools(inner.get("tools"), door), "acryl-door: the verbs in the tools registry");
@@ -761,4 +868,4 @@ function apply(ctx, rawConfig = {}) {
761
868
  }, "acryl-door: the door service, .acryl/door.json and .acryl/bin/acryl");
762
869
  }
763
870
  //#endregion
764
- export { Config, DOOR_PREFIX, Door, DoorError, STATE_DIR, VERBS, VERB_SPECS, apply, chatToolName, doorFilePath, inject, mcpTools, name, readDoorFile };
871
+ export { Config, DOOR_PREFIX, Door, DoorError, STATE_DIR, VERBS, VERB_SPECS, apply, chatToolName, doorFilePath, inject, mcpTools, name, notFoundPage, readDoorFile };
@@ -1,7 +1,7 @@
1
- import { t as Acryl } from "../app-BM0rTCp7.js";
2
- import { loopbackOnly } from "../loopback-only.js";
3
- import "../src-2Z4mJg0x.js";
4
- import "../middleware-CPQ8himN.js";
1
+ import { t as Acryl } from "../app-CQcGWjrk.js";
2
+ import { i as loopbackOnly } from "../loopback-only-BbasoF7R.js";
3
+ import "../src-BTBm4OPD.js";
4
+ import "../middleware-BfG_fp8P.js";
5
5
  import { Service } from "@deepseek-ai/cordis";
6
6
  //#region ../../plugins/acryl-inspect/src/report.ts
7
7
  /**
@@ -1,4 +1,4 @@
1
- import { KvOnDbOptions, kvOnDb } from "../kv/db.js";
1
+ import { n as kvOnDb, t as KvOnDbOptions } from "../open-CULVJgOc.js";
2
2
  import { Context } from "@deepseek-ai/cordis";
3
3
  import z from "@deepseek-ai/schemastery";
4
4
 
@@ -1,5 +1,5 @@
1
1
  import "../src-DRFVEQQb.js";
2
- import { kvOnDb } from "../kv/db.js";
2
+ import { t as kvOnDb } from "../open-But9E2rL.js";
3
3
  import z from "@deepseek-ai/schemastery";
4
4
  //#region ../../plugins/acryl-kv-db/src/index.ts
5
5
  /** Stable Cordis plugin name, equal to the package name and the Loader row id. */
@@ -1,4 +1,4 @@
1
- import { OpenKvOptions, openKv } from "../kv/json.js";
1
+ import { n as openKv, t as OpenKvOptions } from "../open-BqK4D2Wn.js";
2
2
  import { Context } from "@deepseek-ai/cordis";
3
3
  import z from "@deepseek-ai/schemastery";
4
4
 
@@ -1,5 +1,5 @@
1
1
  import "../src-DRFVEQQb.js";
2
- import { openKv } from "../kv/json.js";
2
+ import { t as openKv } from "../open-Dp4sxspc.js";
3
3
  import { dirname, isAbsolute, join, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import z from "@deepseek-ai/schemastery";
@@ -1,6 +1,6 @@
1
1
  import { c as UpgradeHubOptions, i as testWebSocket, l as UpgradeRouter, n as TestWebSocketOptions, o as UpgradeEmitter, p as WebSocketLike, r as TestWebSocketResult, s as UpgradeHub, t as TestSocket, u as RequestError } from "../test-websocket-DyeS50Tr.js";
2
2
  import { n as UpgradeWebSocket } from "../index-C-SklY6u.js";
3
- import { ServeStaticOptions, serveStatic } from "../node-server/serve-static.js";
3
+ import { n as serveStatic, t as ServeStaticOptions } from "../serve-static-B7sqnftW.js";
4
4
  import { Context, Service } from "@deepseek-ai/cordis";
5
5
  import z from "@deepseek-ai/schemastery";
6
6
 
@@ -1,117 +1,4 @@
1
- import { c as Request, i as createAdaptorServer, l as RequestError, n as UpgradeEmitter, r as UpgradeHub, t as testWebSocket } from "../test-websocket-D8WWqE5G.js";
2
- import { t as serveStatic } from "../serve-static-3uo_9rjF.js";
3
- import { Service } from "@deepseek-ai/cordis";
4
- import z from "@deepseek-ai/schemastery";
5
- //#region ../../plugins/acryl-node-server/src/index.ts
6
- /** Stable Cordis plugin name, equal to the package name and the Loader row id. */
7
- const name = "acryl-node-server";
8
- const inject = ["router"];
9
- /** Every field has a default, so a caller passes only what it changes: `ctx.plugin(plugin, { ... })` takes the partial shape. */
10
- const Config = z.object({
11
- hostname: z.string().default("127.0.0.1").description("Address to bind. Loopback by default; binding elsewhere exposes the app to the network."),
12
- port: z.number().min(0).max(65535).default(0).description("Port to listen on; 0 lets the system choose. A taken port is followed by the next free one."),
13
- scan: z.number().min(0).default(20).description("How many further ports to try when the configured one is taken."),
14
- websocket: z.object({
15
- maxPayload: z.number().min(1).default(1024 * 1024).description("The largest WebSocket frame accepted, in bytes; a bigger one closes the socket with 1009."),
16
- heartbeat: z.number().min(0).default(3e4).description("Milliseconds between pings; a peer that did not answer the last one is terminated. 0 turns it off."),
17
- grace: z.number().min(0).default(2e3).description("Milliseconds a closing socket gets before it is terminated when its plugin unloads.")
18
- }).default({})
19
- });
20
- var NodeServer = class extends Service {
21
- hostname;
22
- config;
23
- server;
24
- hub;
25
- bound = 0;
26
- constructor(ctx, config) {
27
- super(ctx, "nodeServer");
28
- this.config = config;
29
- this.hostname = config.hostname;
30
- const router = ctx.router;
31
- this.server = createAdaptorServer({
32
- fetch: (request, { incoming }) => router.fetch(new Request(request), {
33
- remoteAddress: incoming.socket.remoteAddress,
34
- localPort: incoming.socket.localPort
35
- }),
36
- hostname: config.hostname,
37
- overrideGlobalObjects: false
38
- });
39
- ctx.effect(() => async () => {
40
- await this.hub?.close();
41
- this.server.closeAllConnections();
42
- if (this.server.listening) await new Promise((resolve) => {
43
- this.server.close(() => {
44
- resolve();
45
- });
46
- });
47
- }, "acryl-node-server: close listener and open connections");
48
- }
49
- /**
50
- * `upgradeWebSocket` for the plugin that asks (call it once, in `apply`): `app.get('/ws', upgrade((c) => ({ onMessage(event, ws) { ... } })))`. Checks go in ordinary middleware on the
51
- * route, and a handler that returns a `Response` refuses the upgrade with that status before any handshake. Every socket it accepts belongs to the calling plugin: when the plugin
52
- * unloads they are closed with 1001 within `websocket.grace` and the handler sees `close` once. Pass `{ textOnly: true }` to drop binary frames. Needs the `ws` package.
53
- */
54
- upgrades() {
55
- if (!this.hub) {
56
- this.hub = new UpgradeHub(this.config.websocket);
57
- this.hub.attach(this.server, this.ctx.router);
58
- }
59
- return this.hub.upgradeWebSocketFor(this.ctx);
60
- }
61
- /** How many WebSocket connections are open now. */
62
- get sockets() {
63
- return this.hub?.size ?? 0;
64
- }
65
- /** The port in use, 0 before the server listens. */
66
- get port() {
67
- return this.bound;
68
- }
69
- /** The address clients use. */
70
- get url() {
71
- return `http://${this.hostname.includes(":") ? `[${this.hostname}]` : this.hostname}:${String(this.bound)}`;
72
- }
73
- /** Bind, scanning past taken ports. */
74
- async listen() {
75
- const { port, scan, hostname } = this.config;
76
- const last = port === 0 ? 0 : Math.min(65535, port + scan);
77
- for (let candidate = port; candidate <= last; candidate += 1) try {
78
- await new Promise((resolve, reject) => {
79
- const onError = (error) => {
80
- reject(error);
81
- };
82
- this.server.once("error", onError);
83
- this.server.listen(candidate, hostname, () => {
84
- this.server.off("error", onError);
85
- resolve();
86
- });
87
- });
88
- this.bound = this.server.address().port;
89
- return;
90
- } catch (error) {
91
- if (!isPortUnavailable(error)) throw error;
92
- }
93
- throw new Error(`acryl-node-server: no free port from ${String(port)} to ${String(last)} on ${hostname}`);
94
- }
95
- };
96
- /**
97
- * Whether a failed bind means "this port, try the next one". A port another socket holds answers `EADDRINUSE` everywhere. On Windows a port
98
- * inside a range the system reserves (Hyper-V, WSL and Docker exclude ranges, `netsh interface ipv4 show excludedportrange`) answers
99
- * `EACCES`, which is a property of that port too; elsewhere `EACCES` means the process may not bind low ports at all, so moving on would
100
- * only hide the reason.
101
- */
102
- function isPortUnavailable(error, platform = process.platform) {
103
- const code = error?.code;
104
- return code === "EADDRINUSE" || platform === "win32" && code === "EACCES";
105
- }
106
- /** Loads once the server listens, so a plugin that injects `nodeServer` can rely on `url`. A failure to bind fails the plugin. */
107
- async function apply(ctx, config = {}) {
108
- await new NodeServer(ctx, {
109
- hostname: "127.0.0.1",
110
- port: 0,
111
- scan: 20,
112
- websocket: {},
113
- ...config
114
- }).listen();
115
- }
116
- //#endregion
1
+ import { l as RequestError, n as UpgradeEmitter, r as UpgradeHub, t as testWebSocket } from "../test-websocket-BLeEvybj.js";
2
+ import { t as serveStatic } from "../serve-static-C1Nkj_xB.js";
3
+ import { a as isPortUnavailable, i as inject, n as NodeServer, o as name, r as apply, t as Config } from "../src-LX0E4ec4.js";
117
4
  export { Config, NodeServer, RequestError, UpgradeEmitter, UpgradeHub, apply, inject, isPortUnavailable, name, serveStatic, testWebSocket };
@@ -1,5 +1,5 @@
1
1
  import { n as UpgradeWebSocket } from "../index-C-SklY6u.js";
2
- import { i as RouterHttpService } from "../index-D4nVR3wu.js";
2
+ import { i as RouterHttpService } from "../index-DMOtA-NJ.js";
3
3
  import { IncomingMessage, ServerResponse } from "node:http";
4
4
  import { Duplex } from "node:stream";
5
5
  import { Context, Service } from "@deepseek-ai/cordis";
@@ -1,5 +1,5 @@
1
- import { n as UpgradeEmitter, r as UpgradeHub } from "../test-websocket-D8WWqE5G.js";
2
- import "./node-server.js";
1
+ import { n as UpgradeEmitter, r as UpgradeHub } from "../test-websocket-BLeEvybj.js";
2
+ import "../src-LX0E4ec4.js";
3
3
  import { Readable } from "node:stream";
4
4
  import { Service } from "@deepseek-ai/cordis";
5
5
  import { pipeline } from "node:stream/promises";
@@ -2,6 +2,6 @@ import { B as NotFoundHandler, L as MiddlewareHandler, O as Handler, Q as Valida
2
2
  import { c as StatusCode, n as ContentfulStatusCode } from "../http-status-DaAwGAFL.js";
3
3
  import { a as getConnInfo, i as connectionOf, n as RouterBindings, r as RouterEnv, t as ConnectionInfo } from "../connection-ZLEso7XL.js";
4
4
  import { t as Acryl } from "../app-D8riatvv.js";
5
- import { a as TrailingSlash, i as RouterHttpService, n as MountableApp, o as apply, r as Router, s as name, t as Config } from "../index-D4nVR3wu.js";
5
+ import { a as TrailingSlash, i as RouterHttpService, n as MountableApp, o as apply, r as Router, s as name, t as Config } from "../index-DMOtA-NJ.js";
6
6
  import { t as HTTPException } from "../http-exception-kpGJkmpi.js";
7
7
  export { Acryl, type AcrylRequest, Config, type ConnectionInfo, type ContentfulStatusCode, Context, type ContextRenderer, type ContextVariableMap, type Env, type ErrorHandler, type ExecutionContext, HTTPException, type Handler, type Input, type MiddlewareHandler, type MountableApp, type Next, type NotFoundHandler, type NotFoundResponse, Router, type RouterBindings, type RouterEnv, type RouterHttpService, type Schema, type StatusCode, type ToSchema, type TrailingSlash, type TypedResponse, type ValidationTargets, apply, connectionOf, getConnInfo, name };
@@ -1,6 +1,6 @@
1
- import { HTTPException } from "../http-exception.js";
2
- import { t as Context } from "../context-DDO0cfuP.js";
3
- import { t as Acryl } from "../app-BM0rTCp7.js";
1
+ import { t as HTTPException } from "../http-exception-MjWkT89V.js";
2
+ import { t as Context } from "../context-CkOh7Ygv.js";
3
+ import { t as Acryl } from "../app-CQcGWjrk.js";
4
4
  import { n as getConnInfo, t as connectionOf } from "../connection-C7xgP1hP.js";
5
- import { i as name, n as Router, r as apply, t as Config } from "../src-2Z4mJg0x.js";
5
+ import { i as name, n as Router, r as apply, t as Config } from "../src-BTBm4OPD.js";
6
6
  export { Acryl, Config, Context, HTTPException, Router, apply, connectionOf, getConnInfo, name };
@@ -1,10 +1,10 @@
1
- import { t as Acryl } from "../app-BM0rTCp7.js";
2
- import { methodNotAllowed } from "../method-not-allowed.js";
3
- import { r as streamSSE } from "../streaming-DHST3Scg.js";
4
- import { loopbackOnly } from "../loopback-only.js";
5
- import "../helpers-CvTauQT5.js";
6
- import "../src-2Z4mJg0x.js";
7
- import "../middleware-CPQ8himN.js";
1
+ import { t as Acryl } from "../app-CQcGWjrk.js";
2
+ import { t as methodNotAllowed } from "../method-not-allowed-TO2_hnfl.js";
3
+ import { r as streamSSE } from "../streaming-BlDaph8U.js";
4
+ import { i as loopbackOnly } from "../loopback-only-BbasoF7R.js";
5
+ import "../helpers-BEjcdbgw.js";
6
+ import "../src-BTBm4OPD.js";
7
+ import "../middleware-BfG_fp8P.js";
8
8
  import { Service } from "@deepseek-ai/cordis";
9
9
  //#region ../../plugins/acryl-state/src/cell.ts
10
10
  /** A writable cell. `report` receives anything a listener throws; one failing listener never blocks the others. */
@@ -1,4 +1,4 @@
1
- import "./app-BM0rTCp7.js";
1
+ import "./app-CQcGWjrk.js";
2
2
  //#region ../../plugins/acryl-router/src/core/index.ts
3
3
  /**
4
4
  * @module
@@ -0,0 +1,106 @@
1
+ //#region ../../plugins/acryl-router/src/core/middleware/cors/index.ts
2
+ /**
3
+ * CORS Middleware for Acryl.
4
+ *
5
+ *
6
+ * @param {CORSOptions} [options] - The options for the CORS middleware.
7
+ * @param {string | string[] | ((origin: string, c: Context) => Promise<string | undefined | null> | string | undefined | null)} [options.origin='*'] - The value of "Access-Control-Allow-Origin" CORS header.
8
+ * @param {string[] | ((origin: string, c: Context) => Promise<string[]> | string[])} [options.allowMethods=['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH', 'QUERY']] - The value of "Access-Control-Allow-Methods" CORS header.
9
+ * @param {string[]} [options.allowHeaders=[]] - The value of "Access-Control-Allow-Headers" CORS header.
10
+ * @param {number} [options.maxAge] - The value of "Access-Control-Max-Age" CORS header.
11
+ * @param {boolean} [options.credentials] - The value of "Access-Control-Allow-Credentials" CORS header.
12
+ * @param {string[]} [options.exposeHeaders=[]] - The value of "Access-Control-Expose-Headers" CORS header.
13
+ * @returns {MiddlewareHandler} The middleware handler function.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const app = new Acryl()
18
+ *
19
+ * app.use('/api/*', cors())
20
+ * app.use(
21
+ * '/api2/*',
22
+ * cors({
23
+ * origin: 'http://example.com',
24
+ * allowHeaders: ['X-Custom-Header', 'Upgrade-Insecure-Requests'],
25
+ * allowMethods: ['POST', 'GET', 'OPTIONS'],
26
+ * exposeHeaders: ['Content-Length', 'X-Kuma-Revision'],
27
+ * maxAge: 600,
28
+ * credentials: true,
29
+ * })
30
+ * )
31
+ *
32
+ * app.all('/api/abc', (c) => {
33
+ * return c.json({ success: true })
34
+ * })
35
+ * app.all('/api2/abc', (c) => {
36
+ * return c.json({ success: true })
37
+ * })
38
+ * ```
39
+ */
40
+ const cors = (options) => {
41
+ const opts = {
42
+ origin: "*",
43
+ allowMethods: [
44
+ "GET",
45
+ "HEAD",
46
+ "PUT",
47
+ "POST",
48
+ "DELETE",
49
+ "PATCH",
50
+ "QUERY"
51
+ ],
52
+ allowHeaders: [],
53
+ exposeHeaders: [],
54
+ ...options
55
+ };
56
+ const exposeHeadersStr = opts.exposeHeaders?.length ? opts.exposeHeaders.join(",") : void 0;
57
+ const allowHeadersStr = opts.allowHeaders?.length ? opts.allowHeaders.join(",") : void 0;
58
+ const findAllowOrigin = ((optsOrigin) => {
59
+ if (typeof optsOrigin === "string") if (optsOrigin === "*") return () => optsOrigin;
60
+ else return (origin) => optsOrigin === origin ? origin : null;
61
+ else if (typeof optsOrigin === "function") return optsOrigin;
62
+ else return (origin) => optsOrigin.includes(origin) ? origin : null;
63
+ })(opts.origin);
64
+ const findAllowMethods = ((optsAllowMethods) => {
65
+ if (typeof optsAllowMethods === "function") return async (origin, c) => (await optsAllowMethods(origin, c)).join(",");
66
+ else if (Array.isArray(optsAllowMethods)) {
67
+ const methodsStr = optsAllowMethods.join(",");
68
+ return () => methodsStr;
69
+ } else return () => "";
70
+ })(opts.allowMethods);
71
+ return async function cors(c, next) {
72
+ function set(key, value) {
73
+ c.res.headers.set(key, value);
74
+ }
75
+ const allowOrigin = await findAllowOrigin(c.req.header("origin") || "", c);
76
+ if (allowOrigin) set("Access-Control-Allow-Origin", allowOrigin);
77
+ if (opts.credentials) set("Access-Control-Allow-Credentials", "true");
78
+ if (exposeHeadersStr) set("Access-Control-Expose-Headers", exposeHeadersStr);
79
+ if (c.req.method === "OPTIONS") {
80
+ if (opts.origin !== "*") c.res.headers.append("Vary", "Origin");
81
+ if (opts.maxAge != null) set("Access-Control-Max-Age", opts.maxAge.toString());
82
+ const allowMethods = await findAllowMethods(c.req.header("origin") || "", c);
83
+ if (allowMethods) set("Access-Control-Allow-Methods", allowMethods);
84
+ let headersStr = allowHeadersStr;
85
+ if (!headersStr) {
86
+ const requestHeaders = c.req.header("Access-Control-Request-Headers");
87
+ if (requestHeaders) headersStr = requestHeaders.split(",").map((h) => h.trim()).join(",");
88
+ }
89
+ if (headersStr) {
90
+ set("Access-Control-Allow-Headers", headersStr);
91
+ c.res.headers.append("Vary", "Access-Control-Request-Headers");
92
+ }
93
+ c.res.headers.delete("Content-Length");
94
+ c.res.headers.delete("Content-Type");
95
+ return new Response(null, {
96
+ headers: c.res.headers,
97
+ status: 204,
98
+ statusText: "No Content"
99
+ });
100
+ }
101
+ await next();
102
+ if (opts.origin !== "*") c.header("Vary", "Origin", { append: true });
103
+ };
104
+ };
105
+ //#endregion
106
+ export { cors as t };