acrylblends 0.8.1 → 0.9.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 (235) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +83 -7
  3. package/dist/index.js.map +1 -1
  4. package/lib/audit-BXnYsLge.d.ts +58 -0
  5. package/lib/cell-ChU41DZD.d.ts +32 -0
  6. package/lib/client-modules-CLvWOjbd.js +267 -0
  7. package/lib/connection-ZLEso7XL.d.ts +25 -0
  8. package/lib/conninfo.d.ts +1 -23
  9. package/lib/cordis/db-sqlite.d.ts +56 -0
  10. package/lib/cordis/db-sqlite.js +58 -0
  11. package/lib/cordis/db.d.ts +8 -0
  12. package/lib/cordis/db.js +1 -0
  13. package/lib/cordis/door.d.ts +159 -0
  14. package/lib/cordis/door.js +757 -0
  15. package/lib/cordis/inspect.d.ts +77 -0
  16. package/lib/cordis/inspect.js +149 -0
  17. package/lib/cordis/kv-db.d.ts +16 -0
  18. package/lib/cordis/kv-db.js +18 -0
  19. package/lib/cordis/kv-json.d.ts +16 -0
  20. package/lib/cordis/kv-json.js +44 -0
  21. package/lib/cordis/kv.d.ts +8 -0
  22. package/lib/cordis/kv.js +1 -0
  23. package/lib/cordis/node-server.d.ts +45 -0
  24. package/lib/cordis/node-server.js +93 -0
  25. package/lib/cordis/router-http.d.ts +56 -0
  26. package/lib/cordis/router-http.js +117 -0
  27. package/lib/cordis/router.d.ts +8 -0
  28. package/lib/cordis/router.js +7 -0
  29. package/lib/cordis/state/react.d.ts +6 -0
  30. package/lib/cordis/state/react.js +11 -0
  31. package/lib/cordis/state.d.ts +33 -0
  32. package/lib/cordis/state.js +163 -0
  33. package/lib/core-CaY8XDZ6.js +19 -0
  34. package/lib/database-DBHdwefi.d.ts +44 -0
  35. package/lib/db/conformance.d.ts +11 -0
  36. package/lib/db/conformance.js +267 -0
  37. package/lib/db/sqlite.d.ts +1 -1
  38. package/lib/db/sqlite.js +1 -196
  39. package/lib/db.d.ts +2 -1
  40. package/lib/{src-0hI7M7cN.js → deprecated-BP4fPt38.js} +0 -17
  41. package/lib/door/host.d.ts +24 -0
  42. package/lib/door/host.js +2 -0
  43. package/lib/door/page.d.ts +105 -0
  44. package/lib/door/page.js +1030 -0
  45. package/lib/door/protocol.d.ts +2 -0
  46. package/lib/door/protocol.js +197 -0
  47. package/lib/error-t4N2js2I.d.ts +8 -0
  48. package/lib/etag-DaKMOjty.js +135 -0
  49. package/lib/etag.js +1 -134
  50. package/lib/helpers-CvTauQT5.js +5 -0
  51. package/lib/http-exception-kpGJkmpi.d.ts +57 -0
  52. package/lib/http-exception.d.ts +1 -56
  53. package/lib/{index-BLTYsyc1.d.ts → index-BnGgRKTv.d.ts} +3 -39
  54. package/lib/index-BqVQkTnu.d.ts +98 -0
  55. package/lib/index-Dmst2QCQ.d.ts +44 -0
  56. package/lib/index.d.ts +1 -1
  57. package/lib/index.js +2 -1
  58. package/lib/jwk-LkzeoaU8.js +117 -0
  59. package/lib/jwk.js +1 -116
  60. package/lib/jwt-CskdhEW9.js +115 -0
  61. package/lib/jwt.js +2 -114
  62. package/lib/kv/conformance.d.ts +13 -0
  63. package/lib/kv/conformance.js +301 -0
  64. package/lib/kv/db.d.ts +2 -2
  65. package/lib/kv/json.d.ts +1 -1
  66. package/lib/kv-9f-AlRKN.d.ts +39 -0
  67. package/lib/kv.d.ts +2 -1
  68. package/lib/language-D9a4zqvn.js +198 -0
  69. package/lib/language.js +1 -197
  70. package/lib/middleware-CPQ8himN.js +12 -0
  71. package/lib/node-server/serve-static.d.ts +0 -1
  72. package/lib/node-server/serve-static.js +1 -1
  73. package/lib/node-server.d.ts +1 -7
  74. package/lib/node-server.js +3 -1586
  75. package/lib/open-CMhnJODn.js +197 -0
  76. package/lib/protocol-BizVt4J6.d.ts +144 -0
  77. package/lib/request-id-CKVvHOO1.js +34 -0
  78. package/lib/request-id.js +1 -33
  79. package/lib/secure-headers-mP3j6Hm6.js +163 -0
  80. package/lib/secure-headers.js +1 -162
  81. package/lib/{serve-static-B849HlX2.js → serve-static-3uo_9rjF.js} +1 -4
  82. package/lib/server-DvgMGtzo.js +1323 -0
  83. package/lib/snapshot-text-D3NspDyT.js +338 -0
  84. package/lib/src-CwABFr6r.js +328 -0
  85. package/lib/timeout.d.ts +1 -1
  86. package/lib/timing-Cky7C1Jf.js +183 -0
  87. package/lib/timing.js +1 -182
  88. package/package.json +89 -1
  89. package/plugins/acryl-db/plugin.json +6 -1
  90. package/plugins/acryl-db-sqlite/plugin.json +4 -0
  91. package/plugins/acryl-db-sqlite/src/facade/node.ts +1 -1
  92. package/plugins/acryl-db-sqlite/src/index.ts +2 -2
  93. package/plugins/acryl-db-sqlite/src/open.ts +2 -2
  94. package/plugins/acryl-door/plugin.json +4 -1
  95. package/plugins/acryl-door/src/host/http.ts +2 -2
  96. package/plugins/acryl-door/src/host/mcp-routes.ts +1 -1
  97. package/plugins/acryl-door/src/host/operator-routes.ts +1 -1
  98. package/plugins/acryl-door/src/host/page-routes.ts +3 -3
  99. package/plugins/acryl-door/src/index.ts +2 -2
  100. package/plugins/acryl-inspect/plugin.json +4 -1
  101. package/plugins/acryl-inspect/src/service.ts +2 -2
  102. package/plugins/acryl-kv/plugin.json +6 -1
  103. package/plugins/acryl-kv-db/plugin.json +4 -0
  104. package/plugins/acryl-kv-db/src/index.ts +1 -1
  105. package/plugins/acryl-kv-db/src/open.ts +3 -3
  106. package/plugins/acryl-kv-json/plugin.json +4 -0
  107. package/plugins/acryl-kv-json/src/index.ts +1 -1
  108. package/plugins/acryl-kv-json/src/open.ts +2 -2
  109. package/plugins/acryl-node-server/plugin.json +5 -1
  110. package/plugins/acryl-node-server/src/adapter/conninfo.ts +1 -1
  111. package/plugins/acryl-node-server/src/adapter/early-hints.ts +1 -1
  112. package/plugins/acryl-node-server/src/adapter/serve-static.ts +2 -2
  113. package/plugins/acryl-node-server/src/adapter/websocket.ts +2 -2
  114. package/plugins/acryl-node-server/src/client-modules.ts +2 -2
  115. package/plugins/acryl-node-server/src/index.ts +1 -1
  116. package/plugins/acryl-router/plugin.json +11 -1
  117. package/plugins/acryl-router/src/connection.d.ts +25 -0
  118. package/plugins/acryl-router/src/core/app-base.d.ts +215 -0
  119. package/plugins/acryl-router/src/core/app.d.ts +19 -0
  120. package/plugins/acryl-router/src/core/client/client.d.ts +4 -0
  121. package/plugins/acryl-router/src/core/client/fetch-result-please.d.ts +35 -0
  122. package/plugins/acryl-router/src/core/client/index.d.ts +7 -0
  123. package/plugins/acryl-router/src/core/client/types.d.ts +229 -0
  124. package/plugins/acryl-router/src/core/client/utils.d.ts +18 -0
  125. package/plugins/acryl-router/src/core/compose.d.ts +14 -0
  126. package/plugins/acryl-router/src/core/context.d.ts +443 -0
  127. package/plugins/acryl-router/src/core/helper/accepts/accepts.d.ts +30 -0
  128. package/plugins/acryl-router/src/core/helper/accepts/index.d.ts +5 -0
  129. package/plugins/acryl-router/src/core/helper/conninfo/index.d.ts +5 -0
  130. package/plugins/acryl-router/src/core/helper/conninfo/types.d.ts +36 -0
  131. package/plugins/acryl-router/src/core/helper/cookie/index.d.ts +25 -0
  132. package/plugins/acryl-router/src/core/helper/css/common.d.ts +59 -0
  133. package/plugins/acryl-router/src/core/helper/css/index.d.ts +80 -0
  134. package/plugins/acryl-router/src/core/helper/dev/index.d.ts +20 -0
  135. package/plugins/acryl-router/src/core/helper/factory/index.d.ts +82 -0
  136. package/plugins/acryl-router/src/core/helper/html/index.d.ts +8 -0
  137. package/plugins/acryl-router/src/core/helper/proxy/index.d.ts +73 -0
  138. package/plugins/acryl-router/src/core/helper/route/index.d.ts +75 -0
  139. package/plugins/acryl-router/src/core/helper/ssg/index.d.ts +7 -0
  140. package/plugins/acryl-router/src/core/helper/ssg/middleware.d.ts +44 -0
  141. package/plugins/acryl-router/src/core/helper/ssg/plugins.d.ts +27 -0
  142. package/plugins/acryl-router/src/core/helper/ssg/ssg.d.ts +91 -0
  143. package/plugins/acryl-router/src/core/helper/ssg/utils.d.ts +16 -0
  144. package/plugins/acryl-router/src/core/helper/streaming/index.d.ts +8 -0
  145. package/plugins/acryl-router/src/core/helper/streaming/sse.d.ts +13 -0
  146. package/plugins/acryl-router/src/core/helper/streaming/stream.d.ts +3 -0
  147. package/plugins/acryl-router/src/core/helper/streaming/text.d.ts +3 -0
  148. package/plugins/acryl-router/src/core/helper/streaming/utils.d.ts +1 -0
  149. package/plugins/acryl-router/src/core/helper/testing/index.d.ts +13 -0
  150. package/plugins/acryl-router/src/core/helper/websocket/index.d.ts +70 -0
  151. package/plugins/acryl-router/src/core/http-exception.d.ts +58 -0
  152. package/plugins/acryl-router/src/core/index.d.ts +37 -0
  153. package/plugins/acryl-router/src/core/jsx/base.d.ts +61 -0
  154. package/plugins/acryl-router/src/core/jsx/children.d.ts +9 -0
  155. package/plugins/acryl-router/src/core/jsx/components.d.ts +15 -0
  156. package/plugins/acryl-router/src/core/jsx/constants.d.ts +6 -0
  157. package/plugins/acryl-router/src/core/jsx/context.d.ts +51 -0
  158. package/plugins/acryl-router/src/core/jsx/dom/components.d.ts +10 -0
  159. package/plugins/acryl-router/src/core/jsx/dom/context.d.ts +3 -0
  160. package/plugins/acryl-router/src/core/jsx/dom/css.d.ts +83 -0
  161. package/plugins/acryl-router/src/core/jsx/dom/hooks/index.d.ts +39 -0
  162. package/plugins/acryl-router/src/core/jsx/dom/intrinsic-element/components.d.ts +16 -0
  163. package/plugins/acryl-router/src/core/jsx/dom/jsx-dev-runtime.d.ts +8 -0
  164. package/plugins/acryl-router/src/core/jsx/dom/jsx-runtime.d.ts +7 -0
  165. package/plugins/acryl-router/src/core/jsx/dom/render.d.ts +73 -0
  166. package/plugins/acryl-router/src/core/jsx/dom/utils.d.ts +1 -0
  167. package/plugins/acryl-router/src/core/jsx/hooks/index.d.ts +47 -0
  168. package/plugins/acryl-router/src/core/jsx/index.d.ts +76 -0
  169. package/plugins/acryl-router/src/core/jsx/intrinsic-element/common.d.ts +6 -0
  170. package/plugins/acryl-router/src/core/jsx/intrinsic-element/components.d.ts +13 -0
  171. package/plugins/acryl-router/src/core/jsx/intrinsic-elements.d.ts +735 -0
  172. package/plugins/acryl-router/src/core/jsx/streaming.d.ts +36 -0
  173. package/plugins/acryl-router/src/core/jsx/types.d.ts +36 -0
  174. package/plugins/acryl-router/src/core/jsx/utils.d.ts +4 -0
  175. package/plugins/acryl-router/src/core/request/constants.d.ts +1 -0
  176. package/plugins/acryl-router/src/core/request.d.ts +306 -0
  177. package/plugins/acryl-router/src/core/router/reg-exp-router/index.d.ts +6 -0
  178. package/plugins/acryl-router/src/core/router/reg-exp-router/matcher.d.ts +7 -0
  179. package/plugins/acryl-router/src/core/router/reg-exp-router/node.d.ts +13 -0
  180. package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.d.ts +17 -0
  181. package/plugins/acryl-router/src/core/router/reg-exp-router/router.d.ts +11 -0
  182. package/plugins/acryl-router/src/core/router/reg-exp-router/trie.d.ts +8 -0
  183. package/plugins/acryl-router/src/core/router/smart-router/index.d.ts +5 -0
  184. package/plugins/acryl-router/src/core/router/smart-router/router.d.ts +11 -0
  185. package/plugins/acryl-router/src/core/router/trie-router/index.d.ts +5 -0
  186. package/plugins/acryl-router/src/core/router/trie-router/node.d.ts +6 -0
  187. package/plugins/acryl-router/src/core/router/trie-router/router.d.ts +7 -0
  188. package/plugins/acryl-router/src/core/router/utils.d.ts +1 -0
  189. package/plugins/acryl-router/src/core/router.d.ts +97 -0
  190. package/plugins/acryl-router/src/core/types.d.ts +573 -0
  191. package/plugins/acryl-router/src/core/utils/accept.d.ts +6 -0
  192. package/plugins/acryl-router/src/core/utils/body.d.ts +79 -0
  193. package/plugins/acryl-router/src/core/utils/buffer.d.ts +17 -0
  194. package/plugins/acryl-router/src/core/utils/color.d.ts +21 -0
  195. package/plugins/acryl-router/src/core/utils/concurrent.d.ts +11 -0
  196. package/plugins/acryl-router/src/core/utils/constants.d.ts +4 -0
  197. package/plugins/acryl-router/src/core/utils/cookie.d.ts +41 -0
  198. package/plugins/acryl-router/src/core/utils/crypto.d.ts +15 -0
  199. package/plugins/acryl-router/src/core/utils/handler.d.ts +6 -0
  200. package/plugins/acryl-router/src/core/utils/headers.d.ts +8 -0
  201. package/plugins/acryl-router/src/core/utils/html.d.ts +44 -0
  202. package/plugins/acryl-router/src/core/utils/http-status.d.ts +32 -0
  203. package/plugins/acryl-router/src/core/utils/mime.d.ts +70 -0
  204. package/plugins/acryl-router/src/core/utils/stream.d.ts +31 -0
  205. package/plugins/acryl-router/src/core/utils/types.d.ts +128 -0
  206. package/plugins/acryl-router/src/core/utils/url.d.ts +44 -0
  207. package/plugins/acryl-router/src/core/validator/index.d.ts +7 -0
  208. package/plugins/acryl-router/src/core/validator/utils.d.ts +29 -0
  209. package/plugins/acryl-router/src/core/validator/validator.d.ts +16 -0
  210. package/plugins/acryl-router/src/helpers/index.d.ts +17 -0
  211. package/plugins/acryl-router/src/helpers/schema-validator.d.ts +54 -0
  212. package/plugins/acryl-router-http/plugin.json +4 -1
  213. package/plugins/acryl-router-http/src/index.ts +1 -1
  214. package/plugins/acryl-state/plugin.json +5 -1
  215. package/plugins/acryl-state/src/service.ts +3 -3
  216. package/plugins/tsconfig.json +32 -20
  217. package/docs/framework/UPSTREAM.md +0 -3
  218. package/docs/framework/upstream.json +0 -9
  219. package/lib/index-D4FAsBZu.d.ts +0 -85
  220. package/plugins/acryl-db/upstream.json +0 -18
  221. package/plugins/acryl-db-sqlite/upstream.json +0 -21
  222. package/plugins/acryl-door/upstream.json +0 -28
  223. package/plugins/acryl-inspect/upstream.json +0 -18
  224. package/plugins/acryl-kv/upstream.json +0 -18
  225. package/plugins/acryl-kv-db/upstream.json +0 -21
  226. package/plugins/acryl-kv-json/upstream.json +0 -22
  227. package/plugins/acryl-node-server/upstream.json +0 -21
  228. package/plugins/acryl-router/upstream.json +0 -20
  229. package/plugins/acryl-router-http/upstream.json +0 -22
  230. package/plugins/acryl-state/NOTICE +0 -30
  231. package/plugins/acryl-state/upstream.json +0 -22
  232. package/plugins/desktop/upstream.json +0 -12
  233. package/plugins/desktop-web/upstream.json +0 -12
  234. package/templates/todo/upstream.json +0 -12
  235. /package/lib/{index-CTy4N7ng.d.ts → deprecated-CTy4N7ng.d.ts} +0 -0
@@ -0,0 +1,301 @@
1
+ import { afterEach, describe, expect, it } from "vitest";
2
+ //#region ../../plugins/acryl-kv/src/conformance.ts
3
+ /**
4
+ * The `kv` conformance suite: what every provider of the port must do, as Vitest tests. A provider passes it to be accepted
5
+ * (`describeKvConformance('acryl-kv-json', open)` in its own tests), so the same cases run on a file and on SQL.
6
+ */
7
+ function describeKvConformance(provider, open) {
8
+ describe(`kv conformance: ${provider}`, () => {
9
+ const opened = [];
10
+ const track = (subject) => {
11
+ opened.push(subject);
12
+ return subject;
13
+ };
14
+ const fresh = async () => track(await open()).kv;
15
+ afterEach(async () => {
16
+ await Promise.all(opened.splice(0).map((subject) => subject.close()));
17
+ });
18
+ it("round-trips null, booleans, numbers, text with unicode, arrays and nested objects", async () => {
19
+ const kv = await fresh();
20
+ for (const [index, value] of [
21
+ null,
22
+ true,
23
+ false,
24
+ 0,
25
+ -1.5,
26
+ 9007199254740991,
27
+ "",
28
+ "café ✓ 😀",
29
+ [],
30
+ [1, [2, [3]]],
31
+ {},
32
+ {
33
+ a: { b: [1, { c: null }] },
34
+ "ключ": "значение"
35
+ }
36
+ ].entries()) {
37
+ await kv.set(`value/${index}`, value);
38
+ expect(await kv.get(`value/${index}`), JSON.stringify(value)).toEqual(value);
39
+ }
40
+ });
41
+ it("answers get with undefined for a missing key, and overwrites on set", async () => {
42
+ const kv = await fresh();
43
+ expect(await kv.get("missing")).toBeUndefined();
44
+ await kv.set("k", { n: 1 });
45
+ await kv.set("k", { n: 2 });
46
+ expect(await kv.get("k")).toEqual({ n: 2 });
47
+ });
48
+ it("deletes a key, and a missing key is not an error", async () => {
49
+ const kv = await fresh();
50
+ await kv.set("k", 1);
51
+ await kv.delete("k");
52
+ expect(await kv.get("k")).toBeUndefined();
53
+ await expect(kv.delete("k")).resolves.toBeUndefined();
54
+ await expect(kv.delete("never-existed")).resolves.toBeUndefined();
55
+ });
56
+ it("stores a null value as null, apart from a missing key", async () => {
57
+ const kv = await fresh();
58
+ await kv.set("k", null);
59
+ expect(await kv.get("k")).toBeNull();
60
+ expect(await kv.list()).toEqual(["k"]);
61
+ });
62
+ it("lists keys sorted by code point, with an optional prefix", async () => {
63
+ const kv = await fresh();
64
+ for (const key of [
65
+ "b",
66
+ "a/2",
67
+ "a/10",
68
+ "a/1",
69
+ "ab",
70
+ "B",
71
+ "a"
72
+ ]) await kv.set(key, key);
73
+ expect(await kv.list()).toEqual([
74
+ "B",
75
+ "a",
76
+ "a/1",
77
+ "a/10",
78
+ "a/2",
79
+ "ab",
80
+ "b"
81
+ ]);
82
+ expect(await kv.list("a/")).toEqual([
83
+ "a/1",
84
+ "a/10",
85
+ "a/2"
86
+ ]);
87
+ expect(await kv.list("a")).toEqual([
88
+ "a",
89
+ "a/1",
90
+ "a/10",
91
+ "a/2",
92
+ "ab"
93
+ ]);
94
+ expect(await kv.list("")).toEqual([
95
+ "B",
96
+ "a",
97
+ "a/1",
98
+ "a/10",
99
+ "a/2",
100
+ "ab",
101
+ "b"
102
+ ]);
103
+ expect(await kv.list("nothing")).toEqual([]);
104
+ expect(await kv.list(void 0)).toEqual(await kv.list());
105
+ });
106
+ it("orders astral characters after the rest of the basic plane, as code points", async () => {
107
+ const kv = await fresh();
108
+ for (const key of [
109
+ "😀",
110
+ "~",
111
+ "z",
112
+ ""
113
+ ]) await kv.set(key, 1);
114
+ expect(await kv.list()).toEqual([
115
+ "z",
116
+ "",
117
+ "~",
118
+ "😀"
119
+ ]);
120
+ });
121
+ it("treats % _ \\ and letter case in a prefix as plain characters", async () => {
122
+ const kv = await fresh();
123
+ for (const key of [
124
+ "100%",
125
+ "100x",
126
+ "a_b",
127
+ "axb",
128
+ "Abc",
129
+ "abc",
130
+ "back\\slash",
131
+ "backXslash"
132
+ ]) await kv.set(key, 1);
133
+ expect(await kv.list("100%")).toEqual(["100%"]);
134
+ expect(await kv.list("a_")).toEqual(["a_b"]);
135
+ expect(await kv.list("ab")).toEqual(["abc"]);
136
+ expect(await kv.list("Ab")).toEqual(["Abc"]);
137
+ expect(await kv.list("back\\")).toEqual(["back\\slash"]);
138
+ expect(await kv.list("😀")).toEqual([]);
139
+ });
140
+ it("updates a missing key from undefined, and a present one from its value, resolving the new value", async () => {
141
+ const kv = await fresh();
142
+ expect(await kv.update("n", (current) => (current ?? 0) + 1)).toBe(1);
143
+ expect(await kv.update("n", (current) => (current ?? 0) + 10)).toBe(11);
144
+ expect(await kv.get("n")).toBe(11);
145
+ expect(await kv.update("list", (current) => [...current ?? [], "a"])).toEqual(["a"]);
146
+ });
147
+ it("counts fifty concurrent updates to exactly fifty", async () => {
148
+ const kv = await fresh();
149
+ await Promise.all(Array.from({ length: 50 }, () => kv.update("counter", (current) => (current ?? 0) + 1)));
150
+ expect(await kv.get("counter")).toBe(50);
151
+ });
152
+ it("keeps updates of different keys independent, and a set between two updates in order", async () => {
153
+ const kv = await fresh();
154
+ const results = await Promise.all([
155
+ kv.update("a", (current) => (current ?? 0) + 1),
156
+ kv.set("a", 100),
157
+ kv.update("a", (current) => (current ?? 0) + 1),
158
+ kv.update("b", (current) => (current ?? 0) + 5)
159
+ ]);
160
+ expect(results[0]).toBe(1);
161
+ expect(results[2]).toBe(101);
162
+ expect(await kv.get("a")).toBe(101);
163
+ expect(await kv.get("b")).toBe(5);
164
+ });
165
+ it("leaves the value as it was when the update callback throws, and rejects with its error", async () => {
166
+ const kv = await fresh();
167
+ await kv.set("k", "before");
168
+ const failure = /* @__PURE__ */ new Error("stop");
169
+ await expect(kv.update("k", () => {
170
+ throw failure;
171
+ })).rejects.toBe(failure);
172
+ expect(await kv.get("k")).toBe("before");
173
+ await expect(kv.update("absent", () => {
174
+ throw failure;
175
+ })).rejects.toBe(failure);
176
+ expect(await kv.get("absent")).toBeUndefined();
177
+ });
178
+ it("leaves the value as it was when the update returns something that is not JSON", async () => {
179
+ const kv = await fresh();
180
+ await kv.set("k", "before");
181
+ await expect(kv.update("k", () => void 0)).rejects.toThrow(TypeError);
182
+ await expect(kv.update("k", () => NaN)).rejects.toThrow(TypeError);
183
+ expect(await kv.get("k")).toBe("before");
184
+ });
185
+ it("rejects an update callback that returns a promise, with a message that says what to do, and keeps the value", async () => {
186
+ const kv = await fresh();
187
+ await kv.set("k", 1);
188
+ const asynchronous = (async () => 2);
189
+ await expect(kv.update("k", asynchronous)).rejects.toThrow(/synchronous/u);
190
+ expect(await kv.get("k")).toBe(1);
191
+ await kv.update("k", (current) => (current ?? 0) + 1);
192
+ expect(await kv.get("k")).toBe(2);
193
+ });
194
+ it("refuses an empty or non-string key on every call, and writes nothing", async () => {
195
+ const kv = await fresh();
196
+ for (const key of [
197
+ "",
198
+ 1,
199
+ null,
200
+ void 0,
201
+ {}
202
+ ]) {
203
+ await expect(kv.get(key)).rejects.toThrow(TypeError);
204
+ await expect(kv.set(key, 1)).rejects.toThrow(TypeError);
205
+ await expect(kv.update(key, () => 1)).rejects.toThrow(TypeError);
206
+ await expect(kv.delete(key)).rejects.toThrow(TypeError);
207
+ }
208
+ expect(await kv.list()).toEqual([]);
209
+ });
210
+ it("refuses a value that is not JSON with a TypeError, and writes nothing", async () => {
211
+ const kv = await fresh();
212
+ const circular = {};
213
+ circular.self = circular;
214
+ for (const value of [
215
+ void 0,
216
+ () => 1,
217
+ Symbol("s"),
218
+ 1n,
219
+ NaN,
220
+ Number.POSITIVE_INFINITY,
221
+ circular,
222
+ { deep: [Number.NEGATIVE_INFINITY] }
223
+ ]) await expect(kv.set("k", value), String(value)).rejects.toThrow(TypeError);
224
+ expect(await kv.list()).toEqual([]);
225
+ await kv.set("k", "kept");
226
+ await expect(kv.set("k", void 0)).rejects.toThrow(TypeError);
227
+ expect(await kv.get("k")).toBe("kept");
228
+ });
229
+ it("hands out copies: changing what get returned, or the value after set, changes nothing stored", async () => {
230
+ const kv = await fresh();
231
+ const original = {
232
+ list: [1, 2],
233
+ nested: { n: 1 }
234
+ };
235
+ await kv.set("k", original);
236
+ original.list.push(3);
237
+ const first = await kv.get("k");
238
+ expect(first).toEqual({
239
+ list: [1, 2],
240
+ nested: { n: 1 }
241
+ });
242
+ first.nested.n = 99;
243
+ expect(await kv.get("k")).toEqual({
244
+ list: [1, 2],
245
+ nested: { n: 1 }
246
+ });
247
+ (await kv.update("k", (current) => {
248
+ current.list.push(4);
249
+ return current;
250
+ })).list.push(5);
251
+ expect(await kv.get("k")).toEqual({
252
+ list: [
253
+ 1,
254
+ 2,
255
+ 4
256
+ ],
257
+ nested: { n: 1 }
258
+ });
259
+ });
260
+ it("stores a value of a megabyte", async () => {
261
+ const kv = await fresh();
262
+ const big = "x".repeat(1024 * 1024);
263
+ await kv.set("big", { big });
264
+ expect((await kv.get("big"))?.big.length).toBe(big.length);
265
+ });
266
+ it("keeps what was written across close and reopen, and keeps a deleted key deleted", async () => {
267
+ const first = track(await open());
268
+ await first.kv.set("a", { n: 1 });
269
+ await first.kv.set("b", "two");
270
+ await first.kv.update("count", (current) => (current ?? 0) + 1);
271
+ await first.kv.set("gone", 1);
272
+ await first.kv.delete("gone");
273
+ const second = track(await first.reopen());
274
+ expect(await second.kv.list()).toEqual([
275
+ "a",
276
+ "b",
277
+ "count"
278
+ ]);
279
+ expect(await second.kv.get("a")).toEqual({ n: 1 });
280
+ expect(await second.kv.get("count")).toBe(1);
281
+ expect(await second.kv.get("gone")).toBeUndefined();
282
+ });
283
+ it("rejects calls after close, and closing twice does nothing", async () => {
284
+ const subject = track(await open());
285
+ await subject.kv.set("k", 1);
286
+ await subject.close();
287
+ await subject.close();
288
+ await expect(subject.kv.get("k")).rejects.toThrow();
289
+ await expect(subject.kv.set("k", 2)).rejects.toThrow();
290
+ });
291
+ it("waits for writes still in flight when it closes", async () => {
292
+ const subject = track(await open());
293
+ const writes = Array.from({ length: 20 }, (_, index) => subject.kv.set(`k${String(index).padStart(2, "0")}`, index));
294
+ await subject.close();
295
+ await Promise.all(writes);
296
+ expect(await track(await subject.reopen()).kv.list()).toHaveLength(20);
297
+ });
298
+ });
299
+ }
300
+ //#endregion
301
+ export { describeKvConformance };
package/lib/kv/db.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { s as Db } from "../index-D4FAsBZu.js";
2
- import { f as Kv } from "../index-BLTYsyc1.js";
1
+ import { t as Db } from "../database-DBHdwefi.js";
2
+ import { n as Kv } from "../kv-9f-AlRKN.js";
3
3
 
4
4
  //#region ../../plugins/acryl-kv-db/src/open.d.ts
5
5
  interface KvOnDbOptions {
package/lib/kv/json.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { f as Kv, p as KvOpened } from "../index-BLTYsyc1.js";
1
+ import { n as Kv, r as KvOpened } from "../kv-9f-AlRKN.js";
2
2
 
3
3
  //#region ../../plugins/acryl-kv-json/src/open.d.ts
4
4
  interface OpenKvOptions {
@@ -0,0 +1,39 @@
1
+ //#region ../../plugins/acryl-kv/src/kv.d.ts
2
+ /** `T`, except a promise: the callback of `update` is synchronous, and an async one is a type error. */
3
+ type NotPromise<T> = T extends PromiseLike<unknown> ? never : T;
4
+ /** Anything JSON can hold. */
5
+ type JsonValue = null | boolean | number | string | JsonValue[] | {
6
+ [key: string]: JsonValue;
7
+ };
8
+ /**
9
+ * Named JSON documents. Every call returns a promise, so a file, SQLite and Postgres answer alike.
10
+ *
11
+ * Keys are non-empty strings. Values are JSON: `undefined`, functions, symbols, bigints and non-finite numbers are refused with a
12
+ * `TypeError` before anything is written, and a value read back is a copy.
13
+ *
14
+ * There is no `close`: the code that opened a store closes it (see {@link KvOpened}), so a plugin cannot close the store another uses.
15
+ */
16
+ interface Kv {
17
+ /** The value stored under the key, or `undefined`. */
18
+ get<T = JsonValue>(key: string): Promise<T | undefined>;
19
+ /** Store a value under the key, replacing any. Resolves once it is durable. */
20
+ set<T = JsonValue>(key: string, value: T): Promise<void>;
21
+ /**
22
+ * Replace the value under the key by `change(current)`, atomically: `current` is `undefined` when the key is missing, `change` is
23
+ * synchronous (an `async` function is a type error, and a returned promise is rejected at run time: read what you need first, then update), and no other write to the key happens between reading it and writing the result. Resolves the new value. A `change` that
24
+ * throws, or returns something that is not JSON, leaves the value as it was and rejects with its error.
25
+ */
26
+ update<T = JsonValue>(key: string, change: (current: T | undefined) => NotPromise<T>): Promise<T>;
27
+ /** Remove the key. A missing key is not an error. */
28
+ delete(key: string): Promise<void>;
29
+ /** The keys that start with the prefix (all of them without one), sorted by code point (the order of their UTF-8 bytes, not of UTF-16 units). */
30
+ list(prefix?: string): Promise<string[]>;
31
+ }
32
+ /** An open store and the one function that closes it. */
33
+ interface KvOpened {
34
+ readonly kv: Kv;
35
+ /** Waits for the writes in flight, then releases the storage. Calling it again does nothing; calls on `kv` after it reject. */
36
+ close(): Promise<void>;
37
+ }
38
+ //#endregion
39
+ export { NotPromise as i, Kv as n, KvOpened as r, JsonValue as t };
package/lib/kv.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- import { a as checkKey, c as decodeValue, d as JsonValue, f as Kv, i as copyKv, l as encodeValue, m as NotPromise, n as CopyKvOptions, o as codePoints, p as KvOpened, r as CopyKvResult, s as compareKeys, t as KV_SERVICE, u as runChange } from "./index-BLTYsyc1.js";
1
+ import { i as NotPromise, n as Kv, r as KvOpened, t as JsonValue } from "./kv-9f-AlRKN.js";
2
+ import { a as checkKey, c as decodeValue, i as copyKv, l as encodeValue, n as CopyKvOptions, o as codePoints, r as CopyKvResult, s as compareKeys, t as KV_SERVICE, u as runChange } from "./index-BnGgRKTv.js";
2
3
  export { type CopyKvOptions, type CopyKvResult, type JsonValue, KV_SERVICE, type Kv, type KvOpened, type NotPromise, checkKey, codePoints, compareKeys, copyKv, decodeValue, encodeValue, runChange };
@@ -0,0 +1,198 @@
1
+ import { parseAccept } from "./utils/accept.js";
2
+ import { getCookie, setCookie } from "./cookie.js";
3
+ //#region ../../plugins/acryl-router/src/core/middleware/language/language.ts
4
+ const DEFAULT_OPTIONS = {
5
+ order: [
6
+ "querystring",
7
+ "cookie",
8
+ "header"
9
+ ],
10
+ lookupQueryString: "lang",
11
+ lookupCookie: "language",
12
+ lookupFromHeaderKey: "accept-language",
13
+ lookupFromPathIndex: 0,
14
+ caches: ["cookie"],
15
+ ignoreCase: true,
16
+ fallbackLanguage: "en",
17
+ supportedLanguages: ["en"],
18
+ cookieOptions: {
19
+ sameSite: "Strict",
20
+ secure: true,
21
+ maxAge: 365 * 24 * 60 * 60,
22
+ httpOnly: true
23
+ },
24
+ debug: false
25
+ };
26
+ /**
27
+ * Parse Accept-Language header values with quality scores
28
+ * @param header Accept-Language header string
29
+ * @returns Array of parsed languages with quality scores
30
+ */
31
+ function parseAcceptLanguage(header) {
32
+ return parseAccept(header).map(({ type, q }) => ({
33
+ lang: type,
34
+ q
35
+ }));
36
+ }
37
+ /**
38
+ * Validate and normalize language codes
39
+ * @param lang Language code to normalize
40
+ * @param options Detector options
41
+ * @returns Normalized language code or undefined
42
+ */
43
+ const normalizeLanguage = (lang, options) => {
44
+ if (!lang) return;
45
+ try {
46
+ let normalizedLang = lang.trim();
47
+ if (options.convertDetectedLanguage) normalizedLang = options.convertDetectedLanguage(normalizedLang);
48
+ const compLang = options.ignoreCase ? normalizedLang.toLowerCase() : normalizedLang;
49
+ const compSupported = options.supportedLanguages.map((l) => options.ignoreCase ? l.toLowerCase() : l);
50
+ const exactIndex = compSupported.indexOf(compLang);
51
+ if (exactIndex !== -1) return options.supportedLanguages[exactIndex];
52
+ let longestMatchIndex = -1;
53
+ let longestMatchLength = -1;
54
+ for (let i = 0; i < compSupported.length; i++) {
55
+ const candidate = compSupported[i];
56
+ if (candidate.length < compLang.length && candidate.length > longestMatchLength && compLang.startsWith(candidate) && compLang[candidate.length] === "-") {
57
+ longestMatchIndex = i;
58
+ longestMatchLength = candidate.length;
59
+ }
60
+ }
61
+ if (longestMatchIndex !== -1) return options.supportedLanguages[longestMatchIndex];
62
+ return;
63
+ } catch {
64
+ return;
65
+ }
66
+ };
67
+ /**
68
+ * Detects language from query parameter
69
+ */
70
+ const detectFromQuery = (c, options) => {
71
+ const query = c.req.query(options.lookupQueryString);
72
+ return normalizeLanguage(query, options);
73
+ };
74
+ /**
75
+ * Detects language from cookie
76
+ */
77
+ const detectFromCookie = (c, options) => {
78
+ const cookie = getCookie(c, options.lookupCookie);
79
+ return normalizeLanguage(cookie, options);
80
+ };
81
+ /**
82
+ * Detects language from Accept-Language header
83
+ */
84
+ function detectFromHeader(c, options) {
85
+ try {
86
+ const acceptLanguage = c.req.header(options.lookupFromHeaderKey);
87
+ if (!acceptLanguage) return;
88
+ const languages = parseAcceptLanguage(acceptLanguage);
89
+ for (const { lang, q } of languages) {
90
+ if (q === 0) continue;
91
+ const normalizedLang = normalizeLanguage(lang, options);
92
+ if (normalizedLang) return normalizedLang;
93
+ }
94
+ return;
95
+ } catch {
96
+ return;
97
+ }
98
+ }
99
+ /**
100
+ * Detects language from URL path
101
+ */
102
+ function detectFromPath(c, options) {
103
+ const langSegment = new URL(c.req.url).pathname.split("/").filter(Boolean)[options.lookupFromPathIndex];
104
+ return normalizeLanguage(langSegment, options);
105
+ }
106
+ /**
107
+ * Collection of all language detection strategies
108
+ */
109
+ const detectors = {
110
+ querystring: detectFromQuery,
111
+ cookie: detectFromCookie,
112
+ header: detectFromHeader,
113
+ path: detectFromPath
114
+ };
115
+ /**
116
+ * Validate detector options
117
+ * @param options Detector options to validate
118
+ * @throws Error if options are invalid
119
+ */
120
+ function validateOptions(options) {
121
+ if (!options.supportedLanguages.includes(options.fallbackLanguage)) throw new Error("Fallback language must be included in supported languages");
122
+ if (options.lookupFromPathIndex < 0) throw new Error("Path index must be non-negative");
123
+ if (!options.order.every((detector) => Object.keys(detectors).includes(detector))) throw new Error("Invalid detector type in order array");
124
+ }
125
+ /**
126
+ * Cache detected language
127
+ */
128
+ function cacheLanguage(c, language, options) {
129
+ if (!Array.isArray(options.caches) || !options.caches.includes("cookie")) return;
130
+ try {
131
+ setCookie(c, options.lookupCookie, language, options.cookieOptions);
132
+ } catch (error) {
133
+ if (options.debug) console.error("Failed to cache language:", error);
134
+ }
135
+ }
136
+ /**
137
+ * Detect language from request
138
+ */
139
+ const detectLanguage = (c, options) => {
140
+ let detectedLang;
141
+ for (const detectorName of options.order) {
142
+ const detector = detectors[detectorName];
143
+ try {
144
+ detectedLang = detector(c, options);
145
+ if (detectedLang) {
146
+ if (options.debug) console.log(`Language detected from ${detectorName}: ${detectedLang}`);
147
+ break;
148
+ }
149
+ } catch (error) {
150
+ if (options.debug) console.error(`Error in ${detectorName} detector:`, error);
151
+ continue;
152
+ }
153
+ }
154
+ const finalLang = detectedLang || options.fallbackLanguage;
155
+ if (detectedLang && options.caches) cacheLanguage(c, finalLang, options);
156
+ return finalLang;
157
+ };
158
+ /**
159
+ * Language detector middleware factory
160
+ * @param userOptions Configuration options for the language detector
161
+ * @returns Acryl middleware function
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * type Variables = LanguageVariables
166
+ * const app = new Acryl<{ Variables: Variables }>()
167
+ *
168
+ * app.use(
169
+ * languageDetector({
170
+ * supportedLanguages: ['en', 'ja'], // Must include fallback
171
+ * fallbackLanguage: 'en', // Required
172
+ * })
173
+ * )
174
+ *
175
+ * app.get('/', (c) => {
176
+ * const lang = c.get('language')
177
+ * return c.text(`Current language: ${lang}`)
178
+ * })
179
+ * ```
180
+ */
181
+ const languageDetector = (userOptions) => {
182
+ const options = {
183
+ ...DEFAULT_OPTIONS,
184
+ ...userOptions,
185
+ cookieOptions: {
186
+ ...DEFAULT_OPTIONS.cookieOptions,
187
+ ...userOptions.cookieOptions
188
+ }
189
+ };
190
+ validateOptions(options);
191
+ return async function languageDetector(ctx, next) {
192
+ const lang = detectLanguage(ctx, options);
193
+ ctx.set("language", lang);
194
+ await next();
195
+ };
196
+ };
197
+ //#endregion
198
+ export { languageDetector as a, detectFromQuery as i, detectFromHeader as n, detectFromPath as r, detectFromCookie as t };