@ketvietlab/ketjs 0.1.1

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 (283) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +36 -0
  3. package/dist/agent/capabilities.d.ts +62 -0
  4. package/dist/agent/capabilities.d.ts.map +1 -0
  5. package/dist/agent/capabilities.js +89 -0
  6. package/dist/agent/capabilities.js.map +1 -0
  7. package/dist/agent/permissions.d.ts +56 -0
  8. package/dist/agent/permissions.d.ts.map +1 -0
  9. package/dist/agent/permissions.js +209 -0
  10. package/dist/agent/permissions.js.map +1 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +698 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/codegen/dts.d.ts +3 -0
  16. package/dist/codegen/dts.d.ts.map +1 -0
  17. package/dist/codegen/dts.js +94 -0
  18. package/dist/codegen/dts.js.map +1 -0
  19. package/dist/data/adapter.d.ts +4 -0
  20. package/dist/data/adapter.d.ts.map +1 -0
  21. package/dist/data/adapter.js +18 -0
  22. package/dist/data/adapter.js.map +1 -0
  23. package/dist/data/changeset.d.ts +49 -0
  24. package/dist/data/changeset.d.ts.map +1 -0
  25. package/dist/data/changeset.js +206 -0
  26. package/dist/data/changeset.js.map +1 -0
  27. package/dist/data/expr.d.ts +59 -0
  28. package/dist/data/expr.d.ts.map +1 -0
  29. package/dist/data/expr.js +61 -0
  30. package/dist/data/expr.js.map +1 -0
  31. package/dist/data/fleet.d.ts +28 -0
  32. package/dist/data/fleet.d.ts.map +1 -0
  33. package/dist/data/fleet.js +82 -0
  34. package/dist/data/fleet.js.map +1 -0
  35. package/dist/data/list-search.d.ts +93 -0
  36. package/dist/data/list-search.d.ts.map +1 -0
  37. package/dist/data/list-search.js +268 -0
  38. package/dist/data/list-search.js.map +1 -0
  39. package/dist/data/migrate.d.ts +75 -0
  40. package/dist/data/migrate.d.ts.map +1 -0
  41. package/dist/data/migrate.js +136 -0
  42. package/dist/data/migrate.js.map +1 -0
  43. package/dist/data/pool.d.ts +20 -0
  44. package/dist/data/pool.d.ts.map +1 -0
  45. package/dist/data/pool.js +92 -0
  46. package/dist/data/pool.js.map +1 -0
  47. package/dist/data/query.d.ts +111 -0
  48. package/dist/data/query.d.ts.map +1 -0
  49. package/dist/data/query.js +268 -0
  50. package/dist/data/query.js.map +1 -0
  51. package/dist/data/sqlite.d.ts +3 -0
  52. package/dist/data/sqlite.d.ts.map +1 -0
  53. package/dist/data/sqlite.js +107 -0
  54. package/dist/data/sqlite.js.map +1 -0
  55. package/dist/data/time.d.ts +9 -0
  56. package/dist/data/time.d.ts.map +1 -0
  57. package/dist/data/time.js +107 -0
  58. package/dist/data/time.js.map +1 -0
  59. package/dist/index.d.ts +79 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +51 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/kernel/apps.d.ts +43 -0
  64. package/dist/kernel/apps.d.ts.map +1 -0
  65. package/dist/kernel/apps.js +234 -0
  66. package/dist/kernel/apps.js.map +1 -0
  67. package/dist/kernel/compose.d.ts +6 -0
  68. package/dist/kernel/compose.d.ts.map +1 -0
  69. package/dist/kernel/compose.js +976 -0
  70. package/dist/kernel/compose.js.map +1 -0
  71. package/dist/kernel/define.d.ts +9 -0
  72. package/dist/kernel/define.d.ts.map +1 -0
  73. package/dist/kernel/define.js +140 -0
  74. package/dist/kernel/define.js.map +1 -0
  75. package/dist/kernel/diff.d.ts +11 -0
  76. package/dist/kernel/diff.d.ts.map +1 -0
  77. package/dist/kernel/diff.js +106 -0
  78. package/dist/kernel/diff.js.map +1 -0
  79. package/dist/kernel/errors.d.ts +17 -0
  80. package/dist/kernel/errors.d.ts.map +1 -0
  81. package/dist/kernel/errors.js +43 -0
  82. package/dist/kernel/errors.js.map +1 -0
  83. package/dist/kernel/graph.d.ts +3 -0
  84. package/dist/kernel/graph.d.ts.map +1 -0
  85. package/dist/kernel/graph.js +56 -0
  86. package/dist/kernel/graph.js.map +1 -0
  87. package/dist/kernel/i18n.d.ts +43 -0
  88. package/dist/kernel/i18n.d.ts.map +1 -0
  89. package/dist/kernel/i18n.js +79 -0
  90. package/dist/kernel/i18n.js.map +1 -0
  91. package/dist/kernel/index.d.ts +7 -0
  92. package/dist/kernel/index.d.ts.map +1 -0
  93. package/dist/kernel/index.js +7 -0
  94. package/dist/kernel/index.js.map +1 -0
  95. package/dist/kernel/layout.d.ts +17 -0
  96. package/dist/kernel/layout.d.ts.map +1 -0
  97. package/dist/kernel/layout.js +77 -0
  98. package/dist/kernel/layout.js.map +1 -0
  99. package/dist/kernel/menu.d.ts +27 -0
  100. package/dist/kernel/menu.d.ts.map +1 -0
  101. package/dist/kernel/menu.js +65 -0
  102. package/dist/kernel/menu.js.map +1 -0
  103. package/dist/kernel/modules.d.ts +31 -0
  104. package/dist/kernel/modules.d.ts.map +1 -0
  105. package/dist/kernel/modules.js +322 -0
  106. package/dist/kernel/modules.js.map +1 -0
  107. package/dist/kernel/routes.d.ts +24 -0
  108. package/dist/kernel/routes.d.ts.map +1 -0
  109. package/dist/kernel/routes.js +98 -0
  110. package/dist/kernel/routes.js.map +1 -0
  111. package/dist/kernel/types.d.ts +8 -0
  112. package/dist/kernel/types.d.ts.map +1 -0
  113. package/dist/kernel/types.js +67 -0
  114. package/dist/kernel/types.js.map +1 -0
  115. package/dist/kernel/workspace.d.ts +62 -0
  116. package/dist/kernel/workspace.d.ts.map +1 -0
  117. package/dist/kernel/workspace.js +156 -0
  118. package/dist/kernel/workspace.js.map +1 -0
  119. package/dist/scaffold/index.d.ts +2 -0
  120. package/dist/scaffold/index.d.ts.map +1 -0
  121. package/dist/scaffold/index.js +49 -0
  122. package/dist/scaffold/index.js.map +1 -0
  123. package/dist/scaffold/templates/app.test.ts.tmpl +17 -0
  124. package/dist/scaffold/templates/biome.json.tmpl +30 -0
  125. package/dist/scaffold/templates/dev.mjs.tmpl +16 -0
  126. package/dist/scaffold/templates/gitignore.tmpl +24 -0
  127. package/dist/scaffold/templates/ket.workspace.ts.tmpl +18 -0
  128. package/dist/scaffold/templates/module.ts.tmpl +27 -0
  129. package/dist/scaffold/templates/package.json.tmpl +25 -0
  130. package/dist/scaffold/templates/tsconfig.json.tmpl +15 -0
  131. package/dist/server/boot.d.ts +173 -0
  132. package/dist/server/boot.d.ts.map +1 -0
  133. package/dist/server/boot.js +659 -0
  134. package/dist/server/boot.js.map +1 -0
  135. package/dist/server/config.d.ts +48 -0
  136. package/dist/server/config.d.ts.map +1 -0
  137. package/dist/server/config.js +82 -0
  138. package/dist/server/config.js.map +1 -0
  139. package/dist/server/ctx.d.ts +13 -0
  140. package/dist/server/ctx.d.ts.map +1 -0
  141. package/dist/server/ctx.js +496 -0
  142. package/dist/server/ctx.js.map +1 -0
  143. package/dist/server/fn.d.ts +34 -0
  144. package/dist/server/fn.d.ts.map +1 -0
  145. package/dist/server/fn.js +170 -0
  146. package/dist/server/fn.js.map +1 -0
  147. package/dist/server/http.d.ts +79 -0
  148. package/dist/server/http.d.ts.map +1 -0
  149. package/dist/server/http.js +459 -0
  150. package/dist/server/http.js.map +1 -0
  151. package/dist/server/idem.d.ts +31 -0
  152. package/dist/server/idem.d.ts.map +1 -0
  153. package/dist/server/idem.js +68 -0
  154. package/dist/server/idem.js.map +1 -0
  155. package/dist/server/jobs.d.ts +5 -0
  156. package/dist/server/jobs.d.ts.map +1 -0
  157. package/dist/server/jobs.js +24 -0
  158. package/dist/server/jobs.js.map +1 -0
  159. package/dist/server/multipart.d.ts +18 -0
  160. package/dist/server/multipart.d.ts.map +1 -0
  161. package/dist/server/multipart.js +183 -0
  162. package/dist/server/multipart.js.map +1 -0
  163. package/dist/server/project.d.ts +9 -0
  164. package/dist/server/project.d.ts.map +1 -0
  165. package/dist/server/project.js +67 -0
  166. package/dist/server/project.js.map +1 -0
  167. package/dist/server/queue.d.ts +80 -0
  168. package/dist/server/queue.d.ts.map +1 -0
  169. package/dist/server/queue.js +494 -0
  170. package/dist/server/queue.js.map +1 -0
  171. package/dist/server/respond.d.ts +91 -0
  172. package/dist/server/respond.d.ts.map +1 -0
  173. package/dist/server/respond.js +99 -0
  174. package/dist/server/respond.js.map +1 -0
  175. package/dist/server/runtime.d.ts +14 -0
  176. package/dist/server/runtime.d.ts.map +1 -0
  177. package/dist/server/runtime.js +25 -0
  178. package/dist/server/runtime.js.map +1 -0
  179. package/dist/server/session.d.ts +55 -0
  180. package/dist/server/session.d.ts.map +1 -0
  181. package/dist/server/session.js +172 -0
  182. package/dist/server/session.js.map +1 -0
  183. package/dist/server/sessionstore.d.ts +54 -0
  184. package/dist/server/sessionstore.d.ts.map +1 -0
  185. package/dist/server/sessionstore.js +232 -0
  186. package/dist/server/sessionstore.js.map +1 -0
  187. package/dist/server/storage/index.d.ts +11 -0
  188. package/dist/server/storage/index.d.ts.map +1 -0
  189. package/dist/server/storage/index.js +31 -0
  190. package/dist/server/storage/index.js.map +1 -0
  191. package/dist/server/storage/local.d.ts +5 -0
  192. package/dist/server/storage/local.d.ts.map +1 -0
  193. package/dist/server/storage/local.js +165 -0
  194. package/dist/server/storage/local.js.map +1 -0
  195. package/dist/server/storage/s3.d.ts +11 -0
  196. package/dist/server/storage/s3.d.ts.map +1 -0
  197. package/dist/server/storage/s3.js +133 -0
  198. package/dist/server/storage/s3.js.map +1 -0
  199. package/dist/server/storage/sigv4.d.ts +23 -0
  200. package/dist/server/storage/sigv4.d.ts.map +1 -0
  201. package/dist/server/storage/sigv4.js +79 -0
  202. package/dist/server/storage/sigv4.js.map +1 -0
  203. package/dist/server/storage/types.d.ts +37 -0
  204. package/dist/server/storage/types.d.ts.map +1 -0
  205. package/dist/server/storage/types.js +82 -0
  206. package/dist/server/storage/types.js.map +1 -0
  207. package/dist/server/stream.d.ts +43 -0
  208. package/dist/server/stream.d.ts.map +1 -0
  209. package/dist/server/stream.js +117 -0
  210. package/dist/server/stream.js.map +1 -0
  211. package/dist/server/streamstore.d.ts +29 -0
  212. package/dist/server/streamstore.d.ts.map +1 -0
  213. package/dist/server/streamstore.js +140 -0
  214. package/dist/server/streamstore.js.map +1 -0
  215. package/dist/server/tenants.d.ts +108 -0
  216. package/dist/server/tenants.d.ts.map +1 -0
  217. package/dist/server/tenants.js +172 -0
  218. package/dist/server/tenants.js.map +1 -0
  219. package/dist/server/transport/index.d.ts +5 -0
  220. package/dist/server/transport/index.d.ts.map +1 -0
  221. package/dist/server/transport/index.js +3 -0
  222. package/dist/server/transport/index.js.map +1 -0
  223. package/dist/server/transport/memory.d.ts +14 -0
  224. package/dist/server/transport/memory.d.ts.map +1 -0
  225. package/dist/server/transport/memory.js +36 -0
  226. package/dist/server/transport/memory.js.map +1 -0
  227. package/dist/server/transport/types.d.ts +44 -0
  228. package/dist/server/transport/types.d.ts.map +1 -0
  229. package/dist/server/transport/types.js +79 -0
  230. package/dist/server/transport/types.js.map +1 -0
  231. package/dist/server/worker.d.ts +33 -0
  232. package/dist/server/worker.d.ts.map +1 -0
  233. package/dist/server/worker.js +428 -0
  234. package/dist/server/worker.js.map +1 -0
  235. package/dist/testing.d.ts +146 -0
  236. package/dist/testing.d.ts.map +1 -0
  237. package/dist/testing.js +406 -0
  238. package/dist/testing.js.map +1 -0
  239. package/dist/theme/contracts.d.ts +4 -0
  240. package/dist/theme/contracts.d.ts.map +1 -0
  241. package/dist/theme/contracts.js +87 -0
  242. package/dist/theme/contracts.js.map +1 -0
  243. package/dist/theme/index.d.ts +8 -0
  244. package/dist/theme/index.d.ts.map +1 -0
  245. package/dist/theme/index.js +7 -0
  246. package/dist/theme/index.js.map +1 -0
  247. package/dist/theme/joints.d.ts +19 -0
  248. package/dist/theme/joints.d.ts.map +1 -0
  249. package/dist/theme/joints.js +126 -0
  250. package/dist/theme/joints.js.map +1 -0
  251. package/dist/theme/ktl/compile.d.ts +26 -0
  252. package/dist/theme/ktl/compile.d.ts.map +1 -0
  253. package/dist/theme/ktl/compile.js +226 -0
  254. package/dist/theme/ktl/compile.js.map +1 -0
  255. package/dist/theme/ktl/lexer.d.ts +20 -0
  256. package/dist/theme/ktl/lexer.d.ts.map +1 -0
  257. package/dist/theme/ktl/lexer.js +56 -0
  258. package/dist/theme/ktl/lexer.js.map +1 -0
  259. package/dist/theme/ktl/parser.d.ts +64 -0
  260. package/dist/theme/ktl/parser.d.ts.map +1 -0
  261. package/dist/theme/ktl/parser.js +220 -0
  262. package/dist/theme/ktl/parser.js.map +1 -0
  263. package/dist/theme/render.d.ts +19 -0
  264. package/dist/theme/render.d.ts.map +1 -0
  265. package/dist/theme/render.js +246 -0
  266. package/dist/theme/render.js.map +1 -0
  267. package/dist/theme/templates.d.ts +2 -0
  268. package/dist/theme/templates.d.ts.map +1 -0
  269. package/dist/theme/templates.js +40 -0
  270. package/dist/theme/templates.js.map +1 -0
  271. package/dist/theme/tokens.d.ts +4 -0
  272. package/dist/theme/tokens.d.ts.map +1 -0
  273. package/dist/theme/tokens.js +15 -0
  274. package/dist/theme/tokens.js.map +1 -0
  275. package/dist/theme/viewmodel.d.ts +6 -0
  276. package/dist/theme/viewmodel.d.ts.map +1 -0
  277. package/dist/theme/viewmodel.js +40 -0
  278. package/dist/theme/viewmodel.js.map +1 -0
  279. package/dist/types.d.ts +614 -0
  280. package/dist/types.d.ts.map +1 -0
  281. package/dist/types.js +3 -0
  282. package/dist/types.js.map +1 -0
  283. package/package.json +60 -0
@@ -0,0 +1,232 @@
1
+ // Where sessions live.
2
+ //
3
+ // The store is an interface for the same reason the stream store is: a single
4
+ // process is happy in memory, and a deployment behind more than one pod is not.
5
+ // Sessions in memory across three pods means a login lands on one and the next
6
+ // request is anonymous on another — a bug that only appears once you scale, which
7
+ // is the worst moment to discover it.
8
+ //
9
+ // The database-backed store already solves that, because the database is shared.
10
+ // Redis would be faster, and it belongs in its own package the way the Postgres
11
+ // driver does — the framework cannot depend on a client without spending the one
12
+ // exception it has already spent.
13
+ const alive = (r, now) => r.expiresAt > now;
14
+ export function memorySessionStore(o = {}) {
15
+ const now = o.now ?? (() => Date.now());
16
+ const rows = new Map();
17
+ return {
18
+ name: 'memory',
19
+ async init() { },
20
+ async create(r) {
21
+ rows.set(r.id, { ...r });
22
+ },
23
+ async read(id) {
24
+ const r = rows.get(id);
25
+ if (!r)
26
+ return null;
27
+ // Expired is gone, not merely stale: reading it back would be a session
28
+ // that outlived its own expiry for as long as nothing swept.
29
+ if (!alive(r, now())) {
30
+ rows.delete(id);
31
+ return null;
32
+ }
33
+ return { ...r };
34
+ },
35
+ async touch(id, expiresAt) {
36
+ const r = rows.get(id);
37
+ if (!r || !alive(r, now()))
38
+ return null;
39
+ r.expiresAt = expiresAt;
40
+ return expiresAt;
41
+ },
42
+ async updateContext(id, expectedRevision, context) {
43
+ const r = rows.get(id);
44
+ if (!r || !alive(r, now()) || r.revision !== expectedRevision)
45
+ return null;
46
+ const next = { ...r, ...context, revision: r.revision + 1 };
47
+ rows.set(id, next);
48
+ return { ...next, companies: [...next.companies], branches: next.branches ? [...next.branches] : null };
49
+ },
50
+ async destroy(id) {
51
+ rows.delete(id);
52
+ },
53
+ async listUser(userId) {
54
+ const out = [];
55
+ for (const [id, r] of rows) {
56
+ if (!alive(r, now())) {
57
+ rows.delete(id);
58
+ continue;
59
+ }
60
+ if (r.userId === userId)
61
+ out.push({ ...r, companies: [...r.companies], branches: r.branches ? [...r.branches] : null });
62
+ }
63
+ return out.sort((a, b) => b.createdAt - a.createdAt);
64
+ },
65
+ async destroyUser(userId) {
66
+ let n = 0;
67
+ for (const [id, r] of rows)
68
+ if (r.userId === userId) {
69
+ rows.delete(id);
70
+ n++;
71
+ }
72
+ return n;
73
+ },
74
+ async destroyUserExcept(userId, keepId) {
75
+ let n = 0;
76
+ for (const [id, r] of rows)
77
+ if (r.userId === userId && id !== keepId) {
78
+ rows.delete(id);
79
+ n++;
80
+ }
81
+ return n;
82
+ },
83
+ async sweep(at) {
84
+ let n = 0;
85
+ for (const [id, r] of rows)
86
+ if (r.expiresAt <= at) {
87
+ rows.delete(id);
88
+ n++;
89
+ }
90
+ return n;
91
+ },
92
+ };
93
+ }
94
+ const DDL = `
95
+ CREATE TABLE IF NOT EXISTS ket_session (
96
+ id TEXT PRIMARY KEY,
97
+ user_id TEXT NOT NULL,
98
+ companies TEXT NOT NULL,
99
+ company TEXT,
100
+ branch TEXT,
101
+ branches TEXT,
102
+ security_version INTEGER NOT NULL DEFAULT 0,
103
+ revision INTEGER NOT NULL DEFAULT 0,
104
+ created_at BIGINT NOT NULL,
105
+ expires_at BIGINT NOT NULL
106
+ );
107
+ CREATE INDEX IF NOT EXISTS ket_session_user ON ket_session (user_id);
108
+ CREATE INDEX IF NOT EXISTS ket_session_expiry ON ket_session (expires_at);
109
+ `;
110
+ /**
111
+ * One row per session, in whatever database the app already has.
112
+ *
113
+ * This is the store that makes several pods work at all, and it needs no extra
114
+ * infrastructure to do it — which is the point. Reach for Redis when the read
115
+ * traffic justifies it, not before.
116
+ */
117
+ export function dbSessionStore(adapter, o = {}) {
118
+ // Injectable, because a store that reaches for Date.now cannot be tested for
119
+ // expiry without sleeping — and a test that sleeps is a test that flakes.
120
+ const now = o.now ?? (() => Date.now());
121
+ const pg = adapter.name === 'postgres';
122
+ const p = (n) => (pg ? `$${n}` : '?');
123
+ const decode = (row) => ({
124
+ id: String(row.id),
125
+ userId: String(row.user_id),
126
+ companies: JSON.parse(String(row.companies)),
127
+ company: row.company === null || row.company === undefined ? null : String(row.company),
128
+ branch: row.branch === null || row.branch === undefined ? null : String(row.branch),
129
+ branches: row.branches === null || row.branches === undefined
130
+ ? null
131
+ : JSON.parse(String(row.branches)),
132
+ securityVersion: Number(row.security_version ?? 0),
133
+ revision: Number(row.revision ?? 0),
134
+ createdAt: Number(row.created_at),
135
+ expiresAt: Number(row.expires_at),
136
+ });
137
+ return {
138
+ name: adapter.name,
139
+ async init() {
140
+ await adapter.exec(DDL);
141
+ const columns = (await adapter.introspect()).ket_session ?? {};
142
+ for (const [name, sql] of [
143
+ ['branch', 'TEXT'],
144
+ ['security_version', 'INTEGER NOT NULL DEFAULT 0'],
145
+ ['revision', 'INTEGER NOT NULL DEFAULT 0'],
146
+ ])
147
+ if (pg)
148
+ await adapter.exec(`ALTER TABLE ket_session ADD COLUMN IF NOT EXISTS ${name} ${sql}`);
149
+ else if (!columns[name])
150
+ await adapter.exec(`ALTER TABLE ket_session ADD COLUMN ${name} ${sql}`);
151
+ },
152
+ async create(r) {
153
+ await adapter.run(`INSERT INTO ket_session
154
+ (id, user_id, companies, company, branch, branches, security_version, revision, created_at, expires_at)
155
+ VALUES (${p(1)}, ${p(2)}, ${p(3)}, ${p(4)}, ${p(5)}, ${p(6)}, ${p(7)}, ${p(8)}, ${p(9)}, ${p(10)})`, [
156
+ r.id,
157
+ r.userId,
158
+ JSON.stringify(r.companies),
159
+ r.company,
160
+ r.branch,
161
+ r.branches ? JSON.stringify(r.branches) : null,
162
+ r.securityVersion,
163
+ r.revision,
164
+ r.createdAt,
165
+ r.expiresAt,
166
+ ]);
167
+ },
168
+ async read(id) {
169
+ const rows = await adapter.all(`SELECT * FROM ket_session WHERE id = ${p(1)}`, [id]);
170
+ if (!rows.length)
171
+ return null;
172
+ const r = decode(rows[0]);
173
+ if (r.expiresAt <= now()) {
174
+ await adapter.run(`DELETE FROM ket_session WHERE id = ${p(1)}`, [id]);
175
+ return null;
176
+ }
177
+ return r;
178
+ },
179
+ async touch(id, expiresAt) {
180
+ // The expiry guard is in the statement, so a session that lapsed between the
181
+ // read and the write is not quietly revived by the refresh.
182
+ const res = (await adapter.run(`UPDATE ket_session SET expires_at = ${p(1)} WHERE id = ${p(2)} AND expires_at > ${p(3)}`, [expiresAt, id, now()]));
183
+ return res.changes ? expiresAt : null;
184
+ },
185
+ async updateContext(id, expectedRevision, context) {
186
+ const res = (await adapter.run(`UPDATE ket_session SET
187
+ companies = ${p(1)}, company = ${p(2)}, branch = ${p(3)}, branches = ${p(4)},
188
+ security_version = ${p(5)}, revision = revision + 1
189
+ WHERE id = ${p(6)} AND revision = ${p(7)} AND expires_at > ${p(8)}`, [
190
+ JSON.stringify(context.companies),
191
+ context.company,
192
+ context.branch,
193
+ context.branches ? JSON.stringify(context.branches) : null,
194
+ context.securityVersion,
195
+ id,
196
+ expectedRevision,
197
+ now(),
198
+ ]));
199
+ if (!res.changes)
200
+ return null;
201
+ const rows = await adapter.all(`SELECT * FROM ket_session WHERE id = ${p(1)}`, [id]);
202
+ return rows.length ? decode(rows[0]) : null;
203
+ },
204
+ async destroy(id) {
205
+ await adapter.run(`DELETE FROM ket_session WHERE id = ${p(1)}`, [id]);
206
+ },
207
+ async listUser(userId) {
208
+ await adapter.run(`DELETE FROM ket_session WHERE user_id = ${p(1)} AND expires_at <= ${p(2)}`, [
209
+ userId,
210
+ now(),
211
+ ]);
212
+ const rows = await adapter.all(`SELECT * FROM ket_session WHERE user_id = ${p(1)} ORDER BY created_at DESC`, [userId]);
213
+ return rows.map((row) => decode(row));
214
+ },
215
+ async destroyUser(userId) {
216
+ const res = (await adapter.run(`DELETE FROM ket_session WHERE user_id = ${p(1)}`, [userId]));
217
+ return res.changes ?? 0;
218
+ },
219
+ async destroyUserExcept(userId, keepId) {
220
+ const res = (await adapter.run(`DELETE FROM ket_session WHERE user_id = ${p(1)} AND id <> ${p(2)}`, [
221
+ userId,
222
+ keepId,
223
+ ]));
224
+ return res.changes ?? 0;
225
+ },
226
+ async sweep(at) {
227
+ const res = (await adapter.run(`DELETE FROM ket_session WHERE expires_at <= ${p(1)}`, [at]));
228
+ return res.changes ?? 0;
229
+ },
230
+ };
231
+ }
232
+ //# sourceMappingURL=sessionstore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionstore.js","sourceRoot":"","sources":["../../../../../../packages/ketjs/src/server/sessionstore.ts"],"names":[],"mappings":"AAAA,uBAAuB;AACvB,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,+EAA+E;AAC/E,kFAAkF;AAClF,sCAAsC;AACtC,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,iFAAiF;AACjF,kCAAkC;AAiDlC,MAAM,KAAK,GAAG,CAAC,CAAgB,EAAE,GAAW,EAAW,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,CAAA;AAE3E,MAAM,UAAU,kBAAkB,CAAC,CAAC,GAA2B,EAAE;IAC/D,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;IACvC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAyB,CAAA;IAC7C,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,KAAK,CAAC,IAAI,KAAI,CAAC;QACf,KAAK,CAAC,MAAM,CAAC,CAAC;YACZ,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,CAAA;QAC1B,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE;YACX,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACtB,IAAI,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAA;YACnB,wEAAwE;YACxE,6DAA6D;YAC7D,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;gBACrB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;gBACf,OAAO,IAAI,CAAA;YACb,CAAC;YACD,OAAO,EAAE,GAAG,CAAC,EAAE,CAAA;QACjB,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,SAAS;YACvB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACtB,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;gBAAE,OAAO,IAAI,CAAA;YACvC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAA;YACvB,OAAO,SAAS,CAAA;QAClB,CAAC;QACD,KAAK,CAAC,aAAa,CAAC,EAAE,EAAE,gBAAgB,EAAE,OAAO;YAC/C,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACtB,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,KAAK,gBAAgB;gBAAE,OAAO,IAAI,CAAA;YAC1E,MAAM,IAAI,GAAG,EAAE,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAA;YAC3D,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAA;YAClB,OAAO,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QACzG,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,EAAE;YACd,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QACjB,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,MAAM;YACnB,MAAM,GAAG,GAAoB,EAAE,CAAA;YAC/B,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;gBAC3B,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;oBACrB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;oBACf,SAAQ;gBACV,CAAC;gBACD,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM;oBACrB,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;YAClG,CAAC;YACD,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAA;QACtD,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,MAAM;YACtB,IAAI,CAAC,GAAG,CAAC,CAAA;YACT,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI;gBACxB,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;oBACxB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;oBACf,CAAC,EAAE,CAAA;gBACL,CAAC;YACH,OAAO,CAAC,CAAA;QACV,CAAC;QACD,KAAK,CAAC,iBAAiB,CAAC,MAAM,EAAE,MAAM;YACpC,IAAI,CAAC,GAAG,CAAC,CAAA;YACT,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI;gBACxB,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;oBACzC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;oBACf,CAAC,EAAE,CAAA;gBACL,CAAC;YACH,OAAO,CAAC,CAAA;QACV,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,EAAE;YACZ,IAAI,CAAC,GAAG,CAAC,CAAA;YACT,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI;gBACxB,IAAI,CAAC,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;oBACtB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;oBACf,CAAC,EAAE,CAAA;gBACL,CAAC;YACH,OAAO,CAAC,CAAA;QACV,CAAC;KACF,CAAA;AACH,CAAC;AAED,MAAM,GAAG,GAAG;;;;;;;;;;;;;;;CAeX,CAAA;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,OAAgB,EAAE,CAAC,GAA2B,EAAE;IAC7E,6EAA6E;IAC7E,0EAA0E;IAC1E,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;IACvC,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,KAAK,UAAU,CAAA;IACtC,MAAM,CAAC,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IAC7C,MAAM,MAAM,GAAG,CAAC,GAA4B,EAAiB,EAAE,CAAC,CAAC;QAC/D,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC;QAC3B,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAa;QACxD,OAAO,EAAE,GAAG,CAAC,OAAO,KAAK,IAAI,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC;QACvF,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,IAAI,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;QACnF,QAAQ,EACN,GAAG,CAAC,QAAQ,KAAK,IAAI,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS;YACjD,CAAC,CAAC,IAAI;YACN,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAc;QACpD,eAAe,EAAE,MAAM,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,CAAC;QAClD,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC;QACnC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC;QACjC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC;KAClC,CAAC,CAAA;IAEF,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,KAAK,CAAC,IAAI;YACR,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YACvB,MAAM,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,WAAW,IAAI,EAAE,CAAA;YAC9D,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI;gBACxB,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAClB,CAAC,kBAAkB,EAAE,4BAA4B,CAAC;gBAClD,CAAC,UAAU,EAAE,4BAA4B,CAAC;aAClC;gBACR,IAAI,EAAE;oBAAE,MAAM,OAAO,CAAC,IAAI,CAAC,oDAAoD,IAAI,IAAI,GAAG,EAAE,CAAC,CAAA;qBACxF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;oBAAE,MAAM,OAAO,CAAC,IAAI,CAAC,sCAAsC,IAAI,IAAI,GAAG,EAAE,CAAC,CAAA;QACpG,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,CAAC;YACZ,MAAM,OAAO,CAAC,GAAG,CACf;;mBAEW,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,EACpG;gBACE,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,MAAM;gBACR,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC3B,CAAC,CAAC,OAAO;gBACT,CAAC,CAAC,MAAM;gBACR,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI;gBAC9C,CAAC,CAAC,eAAe;gBACjB,CAAC,CAAC,QAAQ;gBACV,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,SAAS;aACZ,CACF,CAAA;QACH,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,EAAE;YACX,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;YACpF,IAAI,CAAC,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAA;YAC7B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAA4B,CAAC,CAAA;YACpD,IAAI,CAAC,CAAC,SAAS,IAAI,GAAG,EAAE,EAAE,CAAC;gBACzB,MAAM,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;gBACrE,OAAO,IAAI,CAAA;YACb,CAAC;YACD,OAAO,CAAC,CAAA;QACV,CAAC;QAED,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,SAAS;YACvB,6EAA6E;YAC7E,4DAA4D;YAC5D,MAAM,GAAG,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,CAC5B,uCAAuC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,EAAE,EACzF,CAAC,SAAS,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CACvB,CAAyB,CAAA;YAC1B,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAA;QACvC,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,EAAE,EAAE,gBAAgB,EAAE,OAAO;YAC/C,MAAM,GAAG,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,CAC5B;yBACiB,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;gCACtD,CAAC,CAAC,CAAC,CAAC;sBACd,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,EAAE,EACpE;gBACE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC;gBACjC,OAAO,CAAC,OAAO;gBACf,OAAO,CAAC,MAAM;gBACd,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI;gBAC1D,OAAO,CAAC,eAAe;gBACvB,EAAE;gBACF,gBAAgB;gBAChB,GAAG,EAAE;aACN,CACF,CAAyB,CAAA;YAC1B,IAAI,CAAC,GAAG,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAA;YAC7B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;YACpF,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAA4B,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QACxE,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,EAAE;YACd,MAAM,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;QACvE,CAAC;QAED,KAAK,CAAC,QAAQ,CAAC,MAAM;YACnB,MAAM,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC7F,MAAM;gBACN,GAAG,EAAE;aACN,CAAC,CAAA;YACF,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAC5B,6CAA6C,CAAC,CAAC,CAAC,CAAC,2BAA2B,EAC5E,CAAC,MAAM,CAAC,CACT,CAAA;YACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAA8B,CAAC,CAAC,CAAA;QAClE,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,MAAM;YACtB,MAAM,GAAG,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAE1F,CAAA;YACD,OAAO,GAAG,CAAC,OAAO,IAAI,CAAC,CAAA;QACzB,CAAC;QAED,KAAK,CAAC,iBAAiB,CAAC,MAAM,EAAE,MAAM;YACpC,MAAM,GAAG,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;gBAClG,MAAM;gBACN,MAAM;aACP,CAAC,CAAyB,CAAA;YAC3B,OAAO,GAAG,CAAC,OAAO,IAAI,CAAC,CAAA;QACzB,CAAC;QAED,KAAK,CAAC,KAAK,CAAC,EAAE;YACZ,MAAM,GAAG,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAE1F,CAAA;YACD,OAAO,GAAG,CAAC,OAAO,IAAI,CAAC,CAAA;QACzB,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { RuntimeConfig } from '../config.ts';
2
+ import type { Storage } from './types.ts';
3
+ export declare function storageFromConfig(config: RuntimeConfig): Storage;
4
+ export { localStorage } from './local.ts';
5
+ export { s3Storage } from './s3.ts';
6
+ export { namespacedStorage, effectStorage } from './types.ts';
7
+ export type { Storage, Stored, OpenStorage, StorageEffect } from './types.ts';
8
+ export type { S3StorageOptions } from './s3.ts';
9
+ export { signRequest, presignUrl, sha256 } from './sigv4.ts';
10
+ export type { SigV4Credentials } from './sigv4.ts';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../packages/ketjs/src/server/storage/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAGjD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEzC,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAqBhE;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACnC,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC7D,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC7E,YAAY,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAC5D,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,31 @@
1
+ import { KetError } from '../../kernel/errors.js';
2
+ import { localStorage } from './local.js';
3
+ import { s3Storage } from './s3.js';
4
+ export function storageFromConfig(config) {
5
+ if (config.storageKind === 'local')
6
+ return localStorage({ dir: config.storageDir });
7
+ const missing = [
8
+ ['KET_S3_ENDPOINT', config.s3Endpoint],
9
+ ['KET_S3_BUCKET', config.s3Bucket],
10
+ ['KET_S3_KEY', config.s3AccessKeyId],
11
+ ['KET_S3_SECRET', config.s3SecretAccessKey],
12
+ ].filter(([, value]) => !value);
13
+ if (missing.length)
14
+ throw new KetError({
15
+ code: 'E_STORAGE_CONFIG',
16
+ message: `S3 storage is missing ${missing.map(([name]) => name).join(', ')}`,
17
+ });
18
+ return s3Storage({
19
+ endpoint: config.s3Endpoint,
20
+ region: config.s3Region,
21
+ bucket: config.s3Bucket,
22
+ accessKeyId: config.s3AccessKeyId,
23
+ secretAccessKey: config.s3SecretAccessKey,
24
+ pathStyle: config.s3PathStyle,
25
+ });
26
+ }
27
+ export { localStorage } from './local.js';
28
+ export { s3Storage } from './s3.js';
29
+ export { namespacedStorage, effectStorage } from './types.js';
30
+ export { signRequest, presignUrl, sha256 } from './sigv4.js';
31
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../packages/ketjs/src/server/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AAEjD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAGnC,MAAM,UAAU,iBAAiB,CAAC,MAAqB;IACrD,IAAI,MAAM,CAAC,WAAW,KAAK,OAAO;QAAE,OAAO,YAAY,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA;IACnF,MAAM,OAAO,GAAG;QACd,CAAC,iBAAiB,EAAE,MAAM,CAAC,UAAU,CAAC;QACtC,CAAC,eAAe,EAAE,MAAM,CAAC,QAAQ,CAAC;QAClC,CAAC,YAAY,EAAE,MAAM,CAAC,aAAa,CAAC;QACpC,CAAC,eAAe,EAAE,MAAM,CAAC,iBAAiB,CAAC;KAC5C,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAA;IAC/B,IAAI,OAAO,CAAC,MAAM;QAChB,MAAM,IAAI,QAAQ,CAAC;YACjB,IAAI,EAAE,kBAAkB;YACxB,OAAO,EAAE,yBAAyB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;SAC7E,CAAC,CAAA;IACJ,OAAO,SAAS,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC,UAAoB;QACrC,MAAM,EAAE,MAAM,CAAC,QAAQ;QACvB,MAAM,EAAE,MAAM,CAAC,QAAkB;QACjC,WAAW,EAAE,MAAM,CAAC,aAAuB;QAC3C,eAAe,EAAE,MAAM,CAAC,iBAA2B;QACnD,SAAS,EAAE,MAAM,CAAC,WAAW;KAC9B,CAAC,CAAA;AACJ,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACnC,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAG7D,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,5 @@
1
+ import type { Storage } from './types.ts';
2
+ export declare function localStorage(options: {
3
+ dir: string;
4
+ }): Storage;
5
+ //# sourceMappingURL=local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../../../../../packages/ketjs/src/server/storage/local.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAU,MAAM,YAAY,CAAA;AAIjD,wBAAgB,YAAY,CAAC,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAgJ9D"}
@@ -0,0 +1,165 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { createReadStream } from 'node:fs';
3
+ import { mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from 'node:fs/promises';
4
+ import { dirname, relative, resolve, sep } from 'node:path';
5
+ import { storageKey } from './types.js';
6
+ export function localStorage(options) {
7
+ const root = resolve(options.dir);
8
+ const pathOf = (key) => {
9
+ const path = resolve(root, storageKey(key));
10
+ if (!path.startsWith(root + sep))
11
+ throw new Error(`storage key escapes root: ${key}`);
12
+ return path;
13
+ };
14
+ const metaPath = (path) => `${path}.ketmeta`;
15
+ /**
16
+ * A crash between open() and rename() strands a temp file that list() hides, so
17
+ * the sweep can never reach it. Each write clears the stale ones beside it.
18
+ */
19
+ const reapTemps = async (dir) => {
20
+ const cutoff = Date.now() - 60 * 60 * 1_000;
21
+ let entries;
22
+ try {
23
+ entries = await readdir(dir, { withFileTypes: true });
24
+ }
25
+ catch {
26
+ return;
27
+ }
28
+ for (const entry of entries) {
29
+ if (!entry.isFile() || !entry.name.includes('.ket-tmp-'))
30
+ continue;
31
+ const stale = resolve(dir, entry.name);
32
+ try {
33
+ if ((await stat(stale)).mtimeMs < cutoff)
34
+ await rm(stale, { force: true });
35
+ }
36
+ catch { }
37
+ }
38
+ };
39
+ const head = async (key) => {
40
+ const path = pathOf(key);
41
+ try {
42
+ const info = await stat(path);
43
+ // A directory satisfies stat() but is not an object. Without this a key naming
44
+ // a shard reports a plausible size and get() then hands back an EISDIR stream
45
+ // after the response headers have already gone out.
46
+ if (!info.isFile())
47
+ return null;
48
+ let meta = { type: 'application/octet-stream' };
49
+ try {
50
+ meta = JSON.parse(await readFile(metaPath(path), 'utf8'));
51
+ }
52
+ catch { }
53
+ return {
54
+ key,
55
+ size: info.size,
56
+ type: meta.type,
57
+ ...(meta.etag ? { etag: meta.etag } : {}),
58
+ modifiedAt: info.mtime.toISOString(),
59
+ };
60
+ }
61
+ catch (error) {
62
+ if (error.code === 'ENOENT')
63
+ return null;
64
+ throw error;
65
+ }
66
+ };
67
+ return {
68
+ name: 'local',
69
+ async put(key, body, options) {
70
+ const path = pathOf(key);
71
+ await mkdir(dirname(path), { recursive: true });
72
+ await reapTemps(dirname(path));
73
+ const temp = `${path}.ket-tmp-${randomUUID()}`;
74
+ const handle = await open(temp, 'wx');
75
+ const hash = createHash('sha256');
76
+ let size = 0;
77
+ try {
78
+ for await (const chunk of body) {
79
+ size += chunk.byteLength;
80
+ hash.update(chunk);
81
+ let offset = 0;
82
+ while (offset < chunk.byteLength)
83
+ offset += (await handle.write(chunk, offset)).bytesWritten;
84
+ }
85
+ await handle.sync();
86
+ }
87
+ catch (error) {
88
+ await handle.close().catch(() => { });
89
+ await rm(temp, { force: true });
90
+ throw error;
91
+ }
92
+ await handle.close();
93
+ if (options.size !== undefined && size !== options.size) {
94
+ await rm(temp, { force: true });
95
+ throw new Error(`storage body size ${size} does not match declared size ${options.size}`);
96
+ }
97
+ const etag = hash.digest('hex');
98
+ await rename(temp, path);
99
+ const metaTemp = `${metaPath(path)}.ket-tmp-${randomUUID()}`;
100
+ await writeFile(metaTemp, JSON.stringify({ type: options.type, etag }));
101
+ await rename(metaTemp, metaPath(path));
102
+ return (await head(key));
103
+ },
104
+ async get(key) {
105
+ const meta = await head(key);
106
+ if (!meta)
107
+ return null;
108
+ return { body: createReadStream(pathOf(key)), meta };
109
+ },
110
+ head,
111
+ async remove(key) {
112
+ const path = pathOf(key);
113
+ await Promise.all([rm(path, { force: true }), rm(metaPath(path), { force: true })]);
114
+ },
115
+ async list(prefix, options = {}) {
116
+ if (prefix)
117
+ storageKey(prefix.endsWith('/') ? `${prefix}x` : prefix);
118
+ const keys = [];
119
+ const limit = Math.max(1, Math.min(1_000, options.limit ?? 100));
120
+ // Visit in key order — a directory sorts as "name/", which is the prefix every
121
+ // key beneath it carries — so results come out sorted and the walk stops once
122
+ // the page plus one look-ahead is full. Sorting on arrival instead made a
123
+ // paginated sweep re-read the whole tree for every page.
124
+ const ordered = (entries) => entries
125
+ .map((entry) => ({ entry, at: entry.isDirectory() ? `${entry.name}/` : entry.name }))
126
+ .sort((a, b) => (a.at < b.at ? -1 : a.at > b.at ? 1 : 0));
127
+ const walk = async (dir) => {
128
+ let entries;
129
+ try {
130
+ entries = await readdir(dir, { withFileTypes: true });
131
+ }
132
+ catch (error) {
133
+ if (error.code === 'ENOENT')
134
+ return;
135
+ throw error;
136
+ }
137
+ for (const { entry } of ordered(entries)) {
138
+ if (keys.length > limit)
139
+ return;
140
+ const path = resolve(dir, entry.name);
141
+ if (entry.isDirectory())
142
+ await walk(path);
143
+ else if (!entry.name.endsWith('.ketmeta') && !entry.name.includes('.ket-tmp-')) {
144
+ const key = relative(root, path).split(sep).join('/');
145
+ if (!key.startsWith(prefix) || (options.after && key <= options.after))
146
+ continue;
147
+ keys.push(key);
148
+ }
149
+ }
150
+ };
151
+ const slash = prefix.lastIndexOf('/');
152
+ const directory = slash >= 0 ? prefix.slice(0, slash) : '';
153
+ await walk(directory ? pathOf(directory) : root);
154
+ const page = keys.slice(0, limit);
155
+ return {
156
+ keys: page,
157
+ ...(keys.length > limit ? { next: page[page.length - 1] } : {}),
158
+ };
159
+ },
160
+ async signedUrl() {
161
+ return null;
162
+ },
163
+ };
164
+ }
165
+ //# sourceMappingURL=local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../../../../../../packages/ketjs/src/server/storage/local.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Storage } from './types.ts';
2
+ import type { SigV4Credentials } from './sigv4.ts';
3
+ export type S3StorageOptions = SigV4Credentials & {
4
+ endpoint: string;
5
+ bucket: string;
6
+ pathStyle?: boolean;
7
+ fetch?: typeof globalThis.fetch;
8
+ now?: () => Date;
9
+ };
10
+ export declare function s3Storage(options: S3StorageOptions): Storage;
11
+ //# sourceMappingURL=s3.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../../../../../packages/ketjs/src/server/storage/s3.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAU,MAAM,YAAY,CAAA;AAEjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,GAAG;IAChD,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IAC/B,GAAG,CAAC,EAAE,MAAM,IAAI,CAAA;CACjB,CAAA;AAkBD,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CA+H5D"}
@@ -0,0 +1,133 @@
1
+ import { storageKey } from './types.js';
2
+ import { presignUrl, signRequest } from './sigv4.js';
3
+ const encodeKey = (key) => storageKey(key)
4
+ .split('/')
5
+ .map((part) => encodeURIComponent(part))
6
+ .join('/');
7
+ const xml = (value) => value.replace(/&(lt|gt|amp|quot|apos);/g, (_, entity) => {
8
+ const found = { lt: '<', gt: '>', amp: '&', quot: '"', apos: "'" };
9
+ return found[entity];
10
+ });
11
+ export function s3Storage(options) {
12
+ const request = options.fetch ?? globalThis.fetch;
13
+ const endpoint = new URL(options.endpoint);
14
+ const credentials = {
15
+ accessKeyId: options.accessKeyId,
16
+ secretAccessKey: options.secretAccessKey,
17
+ region: options.region,
18
+ service: 's3',
19
+ };
20
+ const urlOf = (key) => {
21
+ const url = new URL(endpoint);
22
+ const suffix = key ? `/${encodeKey(key)}` : '';
23
+ if (options.pathStyle)
24
+ url.pathname = `/${encodeURIComponent(options.bucket)}${suffix}`;
25
+ else {
26
+ url.hostname = `${options.bucket}.${url.hostname}`;
27
+ url.pathname = suffix || '/';
28
+ }
29
+ return url;
30
+ };
31
+ const signedFetch = async (method, url, init = {}, payloadHash = method === 'PUT'
32
+ ? 'UNSIGNED-PAYLOAD'
33
+ : 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855') => {
34
+ const headers = signRequest({
35
+ method,
36
+ url,
37
+ headers: init.headers,
38
+ payloadHash,
39
+ credentials,
40
+ now: options.now?.(),
41
+ });
42
+ return request(url, {
43
+ ...init,
44
+ method,
45
+ headers,
46
+ ...(init.body ? { duplex: 'half' } : {}),
47
+ });
48
+ };
49
+ const checked = async (response, operation) => {
50
+ if (response.ok)
51
+ return response;
52
+ const detail = (await response.text()).slice(0, 1_000);
53
+ throw new Error(`S3 ${operation} failed (${response.status}): ${detail}`);
54
+ };
55
+ const meta = (key, headers) => ({
56
+ key,
57
+ size: Number(headers.get('content-length') ?? 0),
58
+ type: headers.get('content-type') ?? 'application/octet-stream',
59
+ ...(headers.get('etag') ? { etag: headers.get('etag').replace(/^"|"$/g, '') } : {}),
60
+ ...(headers.get('last-modified') ? { modifiedAt: headers.get('last-modified') } : {}),
61
+ });
62
+ return {
63
+ name: 's3',
64
+ async put(key, body, o) {
65
+ // Without a declared length fetch frames the body as chunked, which S3 answers
66
+ // with 411; refuse here rather than at the far end with an opaque error.
67
+ if (o.size === undefined)
68
+ throw new Error(`S3 PUT of "${key}" requires a declared size; S3 rejects a body sent without content-length`);
69
+ const url = urlOf(key);
70
+ const headers = new Headers({ 'content-type': o.type, 'content-length': String(o.size) });
71
+ const response = await checked(await signedFetch('PUT', url, { headers, body }), 'PUT');
72
+ return {
73
+ key,
74
+ size: o.size,
75
+ type: o.type,
76
+ ...(response.headers.get('etag')
77
+ ? { etag: response.headers.get('etag').replace(/^"|"$/g, '') }
78
+ : {}),
79
+ };
80
+ },
81
+ async get(key) {
82
+ const response = await signedFetch('GET', urlOf(key));
83
+ if (response.status === 404) {
84
+ await response.body?.cancel();
85
+ return null;
86
+ }
87
+ await checked(response, 'GET');
88
+ if (!response.body)
89
+ throw new Error('S3 GET returned no body');
90
+ return {
91
+ body: response.body,
92
+ meta: meta(key, response.headers),
93
+ };
94
+ },
95
+ async head(key) {
96
+ const response = await signedFetch('HEAD', urlOf(key));
97
+ // Without s3:ListBucket S3 answers 403, not 404, for a key that is not there,
98
+ // so treating only 404 as absent breaks every first upload of new content.
99
+ if (response.status === 404 || response.status === 403)
100
+ return null;
101
+ await checked(response, 'HEAD');
102
+ return meta(key, response.headers);
103
+ },
104
+ async remove(key) {
105
+ await checked(await signedFetch('DELETE', urlOf(key)), 'DELETE');
106
+ },
107
+ async list(prefix, o = {}) {
108
+ const url = urlOf();
109
+ url.searchParams.set('list-type', '2');
110
+ url.searchParams.set('prefix', prefix);
111
+ url.searchParams.set('max-keys', String(Math.max(1, Math.min(1_000, o.limit ?? 100))));
112
+ if (o.after)
113
+ url.searchParams.set('start-after', o.after);
114
+ // URLSearchParams serialises a space as "+" but SigV4 signs it as %20, and the
115
+ // two must agree or the request is refused.
116
+ url.search = url.search.replace(/\+/g, '%20');
117
+ const response = await checked(await signedFetch('GET', url), 'LIST');
118
+ const body = await response.text();
119
+ const keys = [...body.matchAll(/<Key>([\s\S]*?)<\/Key>/g)].map((match) => xml(match[1]));
120
+ const truncated = /<IsTruncated>true<\/IsTruncated>/.test(body);
121
+ return { keys, ...(truncated && keys.length ? { next: keys[keys.length - 1] } : {}) };
122
+ },
123
+ async signedUrl(key, o) {
124
+ return presignUrl({
125
+ url: urlOf(key),
126
+ expiresIn: o.expiresIn,
127
+ credentials,
128
+ now: options.now?.(),
129
+ });
130
+ },
131
+ };
132
+ }
133
+ //# sourceMappingURL=s3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3.js","sourceRoot":"","sources":["../../../../../../../packages/ketjs/src/server/storage/s3.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEvC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAepD,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,EAAE,CAChC,UAAU,CAAC,GAAG,CAAC;KACZ,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;KACvC,IAAI,CAAC,GAAG,CAAC,CAAA;AAEd,MAAM,GAAG,GAAG,CAAC,KAAa,EAAU,EAAE,CACpC,KAAK,CAAC,OAAO,CAAC,0BAA0B,EAAE,CAAC,CAAC,EAAE,MAAc,EAAE,EAAE;IAC9D,MAAM,KAAK,GAA2B,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAA;IAC1F,OAAO,KAAK,CAAC,MAAM,CAAW,CAAA;AAChC,CAAC,CAAC,CAAA;AAEJ,MAAM,UAAU,SAAS,CAAC,OAAyB;IACjD,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAA;IACjD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC1C,MAAM,WAAW,GAAqB;QACpC,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO,EAAE,IAAI;KACd,CAAA;IACD,MAAM,KAAK,GAAG,CAAC,GAAY,EAAO,EAAE;QAClC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAA;QAC7B,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QAC9C,IAAI,OAAO,CAAC,SAAS;YAAE,GAAG,CAAC,QAAQ,GAAG,IAAI,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAAA;aAClF,CAAC;YACJ,GAAG,CAAC,QAAQ,GAAG,GAAG,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAA;YAClD,GAAG,CAAC,QAAQ,GAAG,MAAM,IAAI,GAAG,CAAA;QAC9B,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC,CAAA;IAED,MAAM,WAAW,GAAG,KAAK,EACvB,MAAc,EACd,GAAQ,EACR,IAAI,GAAsB,EAAE,EAC5B,WAAW,GAAG,MAAM,KAAK,KAAK;QAC5B,CAAC,CAAC,kBAAkB;QACpB,CAAC,CAAC,kEAAkE,EACnD,EAAE;QACrB,MAAM,OAAO,GAAG,WAAW,CAAC;YAC1B,MAAM;YACN,GAAG;YACH,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,WAAW;YACX,WAAW;YACX,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE;SACrB,CAAC,CAAA;QACF,OAAO,OAAO,CAAC,GAAG,EAAE;YAClB,GAAG,IAAI;YACP,MAAM;YACN,OAAO;YACP,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACf,CAAC,CAAA;IAC9B,CAAC,CAAA;IAED,MAAM,OAAO,GAAG,KAAK,EAAE,QAAkB,EAAE,SAAiB,EAAqB,EAAE;QACjF,IAAI,QAAQ,CAAC,EAAE;YAAE,OAAO,QAAQ,CAAA;QAChC,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;QACtD,MAAM,IAAI,KAAK,CAAC,MAAM,SAAS,YAAY,QAAQ,CAAC,MAAM,MAAM,MAAM,EAAE,CAAC,CAAA;IAC3E,CAAC,CAAA;IAED,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,OAAgB,EAAU,EAAE,CAAC,CAAC;QACvD,GAAG;QACH,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,0BAA0B;QAC/D,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAY,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/F,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,CAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChG,CAAC,CAAA;IAEF,OAAO;QACL,IAAI,EAAE,IAAI;QACV,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;YACpB,+EAA+E;YAC/E,yEAAyE;YACzE,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS;gBACtB,MAAM,IAAI,KAAK,CACb,cAAc,GAAG,2EAA2E,CAC7F,CAAA;YACH,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;YACtB,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,IAAI,EAAE,gBAAgB,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YACzF,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,MAAM,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,CAAA;YACvF,OAAO;gBACL,GAAG;gBACH,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;oBAC9B,CAAC,CAAC,EAAE,IAAI,EAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAY,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE;oBAC1E,CAAC,CAAC,EAAE,CAAC;aACR,CAAA;QACH,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,GAAG;YACX,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;YACrD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC5B,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAA;gBAC7B,OAAO,IAAI,CAAA;YACb,CAAC;YACD,MAAM,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;YAC9B,IAAI,CAAC,QAAQ,CAAC,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;YAC9D,OAAO;gBACL,IAAI,EAAE,QAAQ,CAAC,IAA4C;gBAC3D,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC;aAClC,CAAA;QACH,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,GAAG;YACZ,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;YACtD,8EAA8E;YAC9E,2EAA2E;YAC3E,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;gBAAE,OAAO,IAAI,CAAA;YACnE,MAAM,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;YAC/B,OAAO,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;QACpC,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,GAAG;YACd,MAAM,OAAO,CAAC,MAAM,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;QAClE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;YACvB,MAAM,GAAG,GAAG,KAAK,EAAE,CAAA;YACnB,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,CAAA;YACtC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;YACtC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;YACtF,IAAI,CAAC,CAAC,KAAK;gBAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,CAAA;YACzD,+EAA+E;YAC/E,4CAA4C;YAC5C,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;YAC7C,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,MAAM,WAAW,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,CAAA;YACrE,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;YAClC,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,yBAAyB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAW,CAAC,CAAC,CAAA;YAClG,MAAM,SAAS,GAAG,kCAAkC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAC/D,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,SAAS,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;QACjG,CAAC;QACD,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;YACpB,OAAO,UAAU,CAAC;gBAChB,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC;gBACf,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,WAAW;gBACX,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE;aACrB,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC"}