@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,23 @@
1
+ export type SigV4Credentials = {
2
+ accessKeyId: string;
3
+ secretAccessKey: string;
4
+ region: string;
5
+ service?: string;
6
+ };
7
+ export declare const sha256: (value: string | Uint8Array) => string;
8
+ export declare function signRequest(options: {
9
+ method: string;
10
+ url: URL;
11
+ headers?: HeadersInit;
12
+ payloadHash: string;
13
+ credentials: SigV4Credentials;
14
+ now?: Date;
15
+ }): Headers;
16
+ export declare function presignUrl(options: {
17
+ method?: string;
18
+ url: URL;
19
+ expiresIn: number;
20
+ credentials: SigV4Credentials;
21
+ now?: Date;
22
+ }): string;
23
+ //# sourceMappingURL=sigv4.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sigv4.d.ts","sourceRoot":"","sources":["../../../../../../packages/ketjs/src/server/storage/sigv4.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,eAAO,MAAM,MAAM,UAAW,MAAM,GAAG,UAAU,KAAG,MAA0D,CAAA;AAkD9G,wBAAgB,WAAW,CAAC,OAAO,EAAE;IACnC,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,GAAG,CAAA;IACR,OAAO,CAAC,EAAE,WAAW,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,gBAAgB,CAAA;IAC7B,GAAG,CAAC,EAAE,IAAI,CAAA;CACX,GAAG,OAAO,CA2BV;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE;IAClC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,GAAG,CAAA;IACR,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,gBAAgB,CAAA;IAC7B,GAAG,CAAC,EAAE,IAAI,CAAA;CACX,GAAG,MAAM,CAwBT"}
@@ -0,0 +1,79 @@
1
+ import { createHash, createHmac } from 'node:crypto';
2
+ export const sha256 = (value) => createHash('sha256').update(value).digest('hex');
3
+ const hmac = (key, value) => createHmac('sha256', key).update(value).digest();
4
+ const encode = (value) => encodeURIComponent(value).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
5
+ const canonicalPath = (pathname) => pathname
6
+ .split('/')
7
+ .map((part) => encode(decodeURIComponent(part)))
8
+ .join('/') || '/';
9
+ const canonicalQuery = (params) => [...params.entries()]
10
+ .map(([key, value]) => [encode(key), encode(value)])
11
+ // SigV4 orders by code point, not by collation: localeCompare reorders case
12
+ // and ignores punctuation, which yields a canonical request S3 will not rebuild.
13
+ .sort(([ak, av], [bk, bv]) => (ak < bk ? -1 : ak > bk ? 1 : av < bv ? -1 : av > bv ? 1 : 0))
14
+ .map(([key, value]) => `${key}=${value}`)
15
+ .join('&');
16
+ const timestamp = (date) => {
17
+ const long = date.toISOString().replace(/[:-]|\.\d{3}/g, '');
18
+ return { long, short: long.slice(0, 8) };
19
+ };
20
+ const signingKey = (credentials, date) => hmac(hmac(hmac(hmac(`AWS4${credentials.secretAccessKey}`, date), credentials.region), credentials.service ?? 's3'), 'aws4_request');
21
+ const signature = (canonical, credentials, date) => {
22
+ const scope = `${date.short}/${credentials.region}/${credentials.service ?? 's3'}/aws4_request`;
23
+ const toSign = `AWS4-HMAC-SHA256\n${date.long}\n${scope}\n${sha256(canonical)}`;
24
+ return {
25
+ scope,
26
+ value: createHmac('sha256', signingKey(credentials, date.short)).update(toSign).digest('hex'),
27
+ };
28
+ };
29
+ export function signRequest(options) {
30
+ const date = timestamp(options.now ?? new Date());
31
+ const headers = new Headers(options.headers);
32
+ headers.set('host', options.url.host);
33
+ headers.set('x-amz-date', date.long);
34
+ headers.set('x-amz-content-sha256', options.payloadHash);
35
+ headers.delete('authorization');
36
+ const entries = [...headers.entries()]
37
+ .map(([key, value]) => [key.toLowerCase(), value.trim().replace(/\s+/g, ' ')])
38
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
39
+ const signedHeaders = entries.map(([key]) => key).join(';');
40
+ const canonicalHeaders = entries.map(([key, value]) => `${key}:${value}\n`).join('');
41
+ const canonical = [
42
+ options.method.toUpperCase(),
43
+ canonicalPath(options.url.pathname),
44
+ canonicalQuery(options.url.searchParams),
45
+ canonicalHeaders,
46
+ signedHeaders,
47
+ options.payloadHash,
48
+ ].join('\n');
49
+ const signed = signature(canonical, options.credentials, date);
50
+ headers.set('authorization', `AWS4-HMAC-SHA256 Credential=${options.credentials.accessKeyId}/${signed.scope},SignedHeaders=${signedHeaders},Signature=${signed.value}`);
51
+ headers.delete('host'); // fetch owns Host; it still matches the value used above.
52
+ return headers;
53
+ }
54
+ export function presignUrl(options) {
55
+ if (!Number.isInteger(options.expiresIn) || options.expiresIn < 1 || options.expiresIn > 604_800)
56
+ throw new Error('SigV4 expiry must be between 1 and 604800 seconds');
57
+ const url = new URL(options.url);
58
+ const date = timestamp(options.now ?? new Date());
59
+ const scope = `${date.short}/${options.credentials.region}/${options.credentials.service ?? 's3'}/aws4_request`;
60
+ url.searchParams.set('X-Amz-Algorithm', 'AWS4-HMAC-SHA256');
61
+ url.searchParams.set('X-Amz-Credential', `${options.credentials.accessKeyId}/${scope}`);
62
+ url.searchParams.set('X-Amz-Date', date.long);
63
+ url.searchParams.set('X-Amz-Expires', String(options.expiresIn));
64
+ url.searchParams.set('X-Amz-SignedHeaders', 'host');
65
+ const canonical = [
66
+ (options.method ?? 'GET').toUpperCase(),
67
+ canonicalPath(url.pathname),
68
+ canonicalQuery(url.searchParams),
69
+ `host:${url.host}\n`,
70
+ 'host',
71
+ 'UNSIGNED-PAYLOAD',
72
+ ].join('\n');
73
+ url.searchParams.set('X-Amz-Signature', signature(canonical, options.credentials, date).value);
74
+ // Emit the encoding the signature was taken over; URLSearchParams would write a
75
+ // space as "+", which SigV4 does not accept.
76
+ url.search = url.search.replace(/\+/g, '%20');
77
+ return url.toString();
78
+ }
79
+ //# sourceMappingURL=sigv4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sigv4.js","sourceRoot":"","sources":["../../../../../../../packages/ketjs/src/server/storage/sigv4.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ export type Stored = {
2
+ key: string;
3
+ size: number;
4
+ type: string;
5
+ etag?: string;
6
+ modifiedAt?: string;
7
+ };
8
+ export type Storage = {
9
+ name: string;
10
+ put(key: string, body: AsyncIterable<Uint8Array>, options: {
11
+ type: string;
12
+ size?: number;
13
+ }): Promise<Stored>;
14
+ get(key: string): Promise<{
15
+ body: AsyncIterable<Uint8Array>;
16
+ meta: Stored;
17
+ } | null>;
18
+ head(key: string): Promise<Stored | null>;
19
+ remove(key: string): Promise<void>;
20
+ list(prefix: string, options?: {
21
+ after?: string;
22
+ limit?: number;
23
+ }): Promise<{
24
+ keys: string[];
25
+ next?: string;
26
+ }>;
27
+ signedUrl(key: string, options: {
28
+ expiresIn: number;
29
+ }): Promise<string | null>;
30
+ };
31
+ export type OpenStorage = (config: import('../config.ts').RuntimeConfig) => Storage | Promise<Storage>;
32
+ export type StorageEffect = 'storage:read' | 'storage:write' | 'storage:remove';
33
+ /** Apply the same declared-effect boundary to blob I/O that Ctx applies to database I/O. */
34
+ export declare function effectStorage(storage: Storage, effects: readonly string[], operation: string): Storage;
35
+ export declare const storageKey: (key: string) => string;
36
+ export declare function namespacedStorage(storage: Storage, namespace: string): Storage;
37
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../packages/ketjs/src/server/storage/types.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,MAAM,GAAG;IACnB,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC5G,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;IACnF,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACzC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClC,IAAI,CACF,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3C,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC7C,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;CAC/E,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,OAAO,cAAc,EAAE,aAAa,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;AAEtG,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,eAAe,GAAG,gBAAgB,CAAA;AAE/E,4FAA4F;AAC5F,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAoCtG;AAED,eAAO,MAAM,UAAU,QAAS,MAAM,KAAG,MAYxC,CAAA;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CA6B9E"}
@@ -0,0 +1,82 @@
1
+ import { KetError } from '../../kernel/errors.js';
2
+ /** Apply the same declared-effect boundary to blob I/O that Ctx applies to database I/O. */
3
+ export function effectStorage(storage, effects, operation) {
4
+ const need = (effect) => {
5
+ if (effects.includes(effect))
6
+ return;
7
+ throw new KetError({
8
+ code: 'E_EFFECT_NOT_DECLARED',
9
+ message: `"${operation}" attempted ${effect} but declares effects [${effects.join(', ') || 'none'}]`,
10
+ hint: `add "${effect}" to the job's effects, or stop performing that storage operation`,
11
+ });
12
+ };
13
+ return {
14
+ name: storage.name,
15
+ put(key, body, options) {
16
+ need('storage:write');
17
+ return storage.put(key, body, options);
18
+ },
19
+ get(key) {
20
+ need('storage:read');
21
+ return storage.get(key);
22
+ },
23
+ head(key) {
24
+ need('storage:read');
25
+ return storage.head(key);
26
+ },
27
+ remove(key) {
28
+ need('storage:remove');
29
+ return storage.remove(key);
30
+ },
31
+ list(prefix, options) {
32
+ need('storage:read');
33
+ return storage.list(prefix, options);
34
+ },
35
+ signedUrl(key, options) {
36
+ need('storage:read');
37
+ return storage.signedUrl(key, options);
38
+ },
39
+ };
40
+ }
41
+ export const storageKey = (key) => {
42
+ if (!key ||
43
+ key.startsWith('/') ||
44
+ key.includes('\\') ||
45
+ key.includes('\0') ||
46
+ key.split('/').some((part) => part === '' || part === '.' || part === '..') ||
47
+ key.endsWith('.ketmeta') ||
48
+ key.includes('.ket-tmp-'))
49
+ throw new Error(`unsafe storage key "${key}"`);
50
+ return key;
51
+ };
52
+ export function namespacedStorage(storage, namespace) {
53
+ const safe = storageKey(namespace);
54
+ const prefix = `${safe}/`;
55
+ const keyOf = (key) => prefix + storageKey(key);
56
+ const strip = (key) => key.slice(prefix.length);
57
+ return {
58
+ name: `${storage.name}:${safe}`,
59
+ put: (key, body, options) => storage.put(keyOf(key), body, options).then((meta) => ({ ...meta, key })),
60
+ async get(key) {
61
+ const found = await storage.get(keyOf(key));
62
+ return found ? { ...found, meta: { ...found.meta, key } } : null;
63
+ },
64
+ async head(key) {
65
+ const found = await storage.head(keyOf(key));
66
+ return found ? { ...found, key } : null;
67
+ },
68
+ remove: (key) => storage.remove(keyOf(key)),
69
+ async list(wanted, options = {}) {
70
+ const result = await storage.list(prefix + wanted, {
71
+ ...options,
72
+ ...(options.after ? { after: keyOf(options.after) } : {}),
73
+ });
74
+ return {
75
+ keys: result.keys.map(strip),
76
+ ...(result.next ? { next: strip(result.next) } : {}),
77
+ };
78
+ },
79
+ signedUrl: (key, options) => storage.signedUrl(keyOf(key), options),
80
+ };
81
+ }
82
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../../../packages/ketjs/src/server/storage/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { StreamStore } from './streamstore.ts';
2
+ export type Chunk = {
3
+ seq: number;
4
+ data: unknown;
5
+ };
6
+ export type Since = {
7
+ chunks: Chunk[];
8
+ done: boolean;
9
+ summary: unknown;
10
+ nextSeq: number;
11
+ };
12
+ export type StreamOptions = {
13
+ store?: StreamStore;
14
+ /** Flush the buffer at least this often. Bounds how much a reader can replay. */
15
+ flushMs?: number;
16
+ /** Flush early once this many chunks are buffered. */
17
+ flushEvery?: number;
18
+ };
19
+ export type Writer = {
20
+ readonly id: string;
21
+ write(chunk: unknown): void;
22
+ flush(): Promise<void>;
23
+ end(summary?: unknown): Promise<void>;
24
+ };
25
+ export declare function createStreams(store?: StreamStore, o?: StreamOptions): Promise<{
26
+ store: StreamStore;
27
+ open(id: string): Promise<Writer>;
28
+ since(id: string, fromSeq?: number): Promise<Since>;
29
+ /**
30
+ * Live tail. A reader on the same instance is woken by the writer and never
31
+ * polls; the slow poll is only a fallback for a writer on another instance.
32
+ */
33
+ tail(id: string, fromSeq?: number, opt?: {
34
+ pollMs?: number;
35
+ timeoutMs?: number;
36
+ }): AsyncGenerator<Chunk>;
37
+ /** Drop finished streams past their grace period. Nothing else expires them. */
38
+ sweep(olderThanMs?: number): Promise<number>;
39
+ }>;
40
+ export type Streams = Awaited<ReturnType<typeof createStreams>>;
41
+ export { memoryStreamStore, dbStreamStore } from './streamstore.ts';
42
+ export type { StreamStore } from './streamstore.ts';
43
+ //# sourceMappingURL=stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../../../../packages/ketjs/src/server/stream.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAe,MAAM,kBAAkB,CAAA;AAEhE,MAAM,MAAM,KAAK,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAA;AAClD,MAAM,MAAM,KAAK,GAAG;IAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAEzF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;IAC3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,GAAG,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACtC,CAAA;AASD,wBAAsB,aAAa,CAAC,KAAK,GAAE,WAAiC,EAAE,CAAC,GAAE,aAAkB;;aAYhF,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;cA0CvB,MAAM,qBAAgB,OAAO,CAAC,KAAK,CAAC;IAKpD;;;OAGG;aAEG,MAAM,0BAEL;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3C,cAAc,CAAC,KAAK,CAAC;IAiCxB,gFAAgF;iCACxC,OAAO,CAAC,MAAM,CAAC;GAK1D;AAED,MAAM,MAAM,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC,CAAA;AAC/D,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACnE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA"}
@@ -0,0 +1,117 @@
1
+ // Resumable streams.
2
+ //
3
+ // A stream has exactly one producer, so the sequence number belongs to the writer,
4
+ // not to the database. The old implementation read SELECT MAX(seq) before every
5
+ // single chunk — two round trips per token, plus a race it did not need to have.
6
+ // Now the sequence is recovered once when the writer opens, and never read again.
7
+ //
8
+ // Chunks are also batched: "resumable" means a reader never sees a gap or a
9
+ // duplicate, not that every token is its own transaction.
10
+ import { memoryStreamStore } from './streamstore.js';
11
+ const expand = (r) => ({
12
+ chunks: r.batches.flatMap((b) => b.chunks.map((data, i) => ({ seq: b.seq + i / 1000, data }))),
13
+ done: r.done,
14
+ summary: r.summary,
15
+ nextSeq: r.nextSeq,
16
+ });
17
+ export async function createStreams(store = memoryStreamStore(), o = {}) {
18
+ const flushMs = o.flushMs ?? 50;
19
+ const flushEvery = o.flushEvery ?? 32;
20
+ // The table is created on first use, not at construction. An app that never
21
+ // streams should not find a stream table in its database.
22
+ let inited = null;
23
+ const ensure = () => (inited ??= store.init());
24
+ return {
25
+ store,
26
+ async open(id) {
27
+ await ensure();
28
+ // The only sequence read there is: once, at open, so a writer that restarts
29
+ // mid-stream continues after what is already durable instead of overwriting it.
30
+ let seq = await store.head(id);
31
+ let buffer = [];
32
+ let timer = null;
33
+ let chain = Promise.resolve();
34
+ const flushNow = () => {
35
+ if (timer) {
36
+ clearTimeout(timer);
37
+ timer = null;
38
+ }
39
+ if (!buffer.length)
40
+ return chain;
41
+ const batch = buffer;
42
+ const at = seq++;
43
+ buffer = [];
44
+ chain = chain.then(() => store.append(id, at, batch));
45
+ return chain;
46
+ };
47
+ return {
48
+ id,
49
+ write(chunk) {
50
+ buffer.push(chunk);
51
+ if (buffer.length >= flushEvery) {
52
+ void flushNow();
53
+ return;
54
+ }
55
+ timer ??= setTimeout(() => {
56
+ void flushNow();
57
+ }, flushMs);
58
+ },
59
+ flush: flushNow,
60
+ async end(summary = null) {
61
+ await flushNow();
62
+ await store.markEnd(id, seq++, summary);
63
+ },
64
+ };
65
+ },
66
+ async since(id, fromSeq = 0) {
67
+ await ensure();
68
+ return expand(await store.since(id, Math.floor(fromSeq)));
69
+ },
70
+ /**
71
+ * Live tail. A reader on the same instance is woken by the writer and never
72
+ * polls; the slow poll is only a fallback for a writer on another instance.
73
+ */
74
+ async *tail(id, fromSeq = 0, opt = {}) {
75
+ const pollMs = opt.pollMs ?? 250;
76
+ const timeoutMs = opt.timeoutMs ?? 30_000;
77
+ await ensure();
78
+ let cursor = Math.floor(fromSeq);
79
+ const started = Date.now();
80
+ let wake = null;
81
+ const unsubscribe = store.subscribe(id, () => {
82
+ wake?.();
83
+ });
84
+ try {
85
+ for (;;) {
86
+ const s = expand(await store.since(id, cursor));
87
+ if (s.chunks.length)
88
+ cursor = Math.floor(s.chunks[s.chunks.length - 1].seq) + 1;
89
+ for (const c of s.chunks)
90
+ yield c;
91
+ if (s.done)
92
+ return;
93
+ if (Date.now() - started > timeoutMs)
94
+ throw new Error(`stream "${id}" timed out after ${timeoutMs}ms`);
95
+ await new Promise((resolve) => {
96
+ const t = setTimeout(resolve, pollMs);
97
+ wake = () => {
98
+ clearTimeout(t);
99
+ wake = null;
100
+ resolve();
101
+ };
102
+ });
103
+ }
104
+ }
105
+ finally {
106
+ unsubscribe();
107
+ }
108
+ },
109
+ /** Drop finished streams past their grace period. Nothing else expires them. */
110
+ async sweep(olderThanMs = 10 * 60_000) {
111
+ await ensure();
112
+ return store.sweep(olderThanMs);
113
+ },
114
+ };
115
+ }
116
+ export { memoryStreamStore, dbStreamStore } from './streamstore.js';
117
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../../../../../packages/ketjs/src/server/stream.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { Adapter } from '../types.ts';
2
+ export type Batch = {
3
+ seq: number;
4
+ chunks: unknown[];
5
+ };
6
+ export type SinceResult = {
7
+ batches: Batch[];
8
+ done: boolean;
9
+ summary: unknown;
10
+ nextSeq: number;
11
+ };
12
+ export type StreamStore = {
13
+ readonly name: string;
14
+ init(): Promise<void>;
15
+ append(topic: string, seq: number, chunks: unknown[]): Promise<void>;
16
+ markEnd(topic: string, seq: number, summary: unknown): Promise<void>;
17
+ since(topic: string, fromSeq: number): Promise<SinceResult>;
18
+ /** Highest batch sequence written, recovered once when a writer opens. */
19
+ head(topic: string): Promise<number>;
20
+ subscribe(topic: string, cb: () => void): () => void;
21
+ /** Delete finished streams older than the grace period. Returns rows removed. */
22
+ sweep(olderThanMs: number): Promise<number>;
23
+ };
24
+ export declare function memoryStreamStore(): StreamStore;
25
+ export declare const STREAM_DDL = "\nCREATE TABLE IF NOT EXISTS ket_stream (\n topic TEXT NOT NULL,\n seq INTEGER NOT NULL,\n chunks TEXT NOT NULL,\n ended INTEGER NOT NULL DEFAULT 0,\n summary TEXT,\n created_at TEXT NOT NULL,\n PRIMARY KEY (topic, seq)\n);\nCREATE INDEX IF NOT EXISTS ket_stream_ended ON ket_stream (ended, created_at);\n";
26
+ export declare function dbStreamStore(adapter: Adapter, o?: {
27
+ now?: () => string;
28
+ }): StreamStore;
29
+ //# sourceMappingURL=streamstore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamstore.d.ts","sourceRoot":"","sources":["../../../../../packages/ketjs/src/server/streamstore.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAE1C,MAAM,MAAM,KAAK,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,EAAE,CAAA;CAAE,CAAA;AACtD,MAAM,MAAM,WAAW,GAAG;IAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhG,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IAC3D,0EAA0E;IAC1E,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACpC,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IACpD,iFAAiF;IACjF,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CAC5C,CAAA;AAgBD,wBAAgB,iBAAiB,IAAI,WAAW,CAkD/C;AAED,eAAO,MAAM,UAAU,8VAWtB,CAAA;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAAO,GAAG,WAAW,CA2E3F"}
@@ -0,0 +1,140 @@
1
+ // Where stream chunks live.
2
+ //
3
+ // The earlier design put them in the same append-only table as jobs and
4
+ // idempotency records, on the grounds that all three are logs. Measured, that was
5
+ // wrong: chunks are the hottest and shortest-lived rows in the system, and sharing
6
+ // a table and an index with the coldest ones costs far more than the tidiness saves.
7
+ //
8
+ // Chunks still need to be durable — losing them on restart is the exact failure
9
+ // resumable streams exist to prevent — but the store is pluggable, so a single
10
+ // instance can stay in memory and only a multi-instance deployment pays for a table.
11
+ import { EventEmitter } from 'node:events';
12
+ // Local notification, so a reader on the same instance never polls at all.
13
+ class Notifier {
14
+ bus = new EventEmitter().setMaxListeners(0);
15
+ subscribe(topic, cb) {
16
+ this.bus.on(topic, cb);
17
+ return () => {
18
+ this.bus.off(topic, cb);
19
+ };
20
+ }
21
+ notify(topic) {
22
+ this.bus.emit(topic);
23
+ }
24
+ }
25
+ export function memoryStreamStore() {
26
+ const topics = new Map();
27
+ const notifier = new Notifier();
28
+ const slot = (t) => {
29
+ let s = topics.get(t);
30
+ if (!s) {
31
+ s = { batches: [], done: false, summary: null, endedAt: null };
32
+ topics.set(t, s);
33
+ }
34
+ return s;
35
+ };
36
+ return {
37
+ name: 'memory',
38
+ async init() { },
39
+ async append(topic, seq, chunks) {
40
+ slot(topic).batches.push({ seq, chunks });
41
+ notifier.notify(topic);
42
+ },
43
+ async markEnd(topic, _seq, summary) {
44
+ const s = slot(topic);
45
+ s.done = true;
46
+ s.summary = summary;
47
+ s.endedAt = Date.now();
48
+ notifier.notify(topic);
49
+ },
50
+ async since(topic, fromSeq) {
51
+ const s = slot(topic);
52
+ const batches = s.batches.filter((b) => b.seq >= fromSeq);
53
+ const head = s.batches.length ? s.batches[s.batches.length - 1].seq + 1 : 0;
54
+ return { batches, done: s.done, summary: s.summary, nextSeq: head };
55
+ },
56
+ async head(topic) {
57
+ const s = slot(topic);
58
+ return s.batches.length ? s.batches[s.batches.length - 1].seq + 1 : 0;
59
+ },
60
+ subscribe: (t, cb) => notifier.subscribe(t, cb),
61
+ async sweep(olderThanMs) {
62
+ const cutoff = Date.now() - olderThanMs;
63
+ let n = 0;
64
+ for (const [t, s] of topics)
65
+ if (s.endedAt != null && s.endedAt <= cutoff) {
66
+ topics.delete(t);
67
+ n++;
68
+ }
69
+ return n;
70
+ },
71
+ };
72
+ }
73
+ export const STREAM_DDL = `
74
+ CREATE TABLE IF NOT EXISTS ket_stream (
75
+ topic TEXT NOT NULL,
76
+ seq INTEGER NOT NULL,
77
+ chunks TEXT NOT NULL,
78
+ ended INTEGER NOT NULL DEFAULT 0,
79
+ summary TEXT,
80
+ created_at TEXT NOT NULL,
81
+ PRIMARY KEY (topic, seq)
82
+ );
83
+ CREATE INDEX IF NOT EXISTS ket_stream_ended ON ket_stream (ended, created_at);
84
+ `;
85
+ export function dbStreamStore(adapter, o = {}) {
86
+ const now = o.now ?? (() => new Date().toISOString());
87
+ const pg = adapter.name === 'postgres';
88
+ const p = (n) => (pg ? `$${n}` : '?');
89
+ const notifier = new Notifier();
90
+ return {
91
+ name: `db:${adapter.name}`,
92
+ async init() {
93
+ await adapter.exec(STREAM_DDL);
94
+ },
95
+ async append(topic, seq, chunks) {
96
+ await adapter.run(`INSERT INTO ket_stream (topic, seq, chunks, created_at) VALUES (${p(1)}, ${p(2)}, ${p(3)}, ${p(4)})`, [topic, seq, JSON.stringify(chunks), now()]);
97
+ notifier.notify(topic);
98
+ },
99
+ async markEnd(topic, seq, summary) {
100
+ await adapter.run(`INSERT INTO ket_stream (topic, seq, chunks, ended, summary, created_at) VALUES (${p(1)}, ${p(2)}, ${p(3)}, 1, ${p(4)}, ${p(5)})`, [topic, seq, '[]', summary == null ? null : JSON.stringify(summary), now()]);
101
+ notifier.notify(topic);
102
+ },
103
+ // One query, not the three the old implementation used.
104
+ async since(topic, fromSeq) {
105
+ const rows = await adapter.all(`SELECT seq, chunks, ended, summary FROM ket_stream WHERE topic = ${p(1)} ORDER BY seq`, [topic]);
106
+ let done = false;
107
+ let summary = null;
108
+ let head = 0;
109
+ const batches = [];
110
+ for (const r of rows) {
111
+ const seq = Number(r.seq);
112
+ head = Math.max(head, seq + 1);
113
+ if (Number(r.ended) === 1) {
114
+ done = true;
115
+ summary = r.summary == null ? null : JSON.parse(String(r.summary));
116
+ continue;
117
+ }
118
+ if (seq >= fromSeq)
119
+ batches.push({ seq, chunks: JSON.parse(String(r.chunks)) });
120
+ }
121
+ return { batches, done, summary, nextSeq: head };
122
+ },
123
+ async head(topic) {
124
+ const r = await adapter.all(`SELECT COALESCE(MAX(seq), -1) AS m FROM ket_stream WHERE topic = ${p(1)}`, [topic]);
125
+ return Number(r[0].m) + 1;
126
+ },
127
+ subscribe: (t, cb) => notifier.subscribe(t, cb),
128
+ async sweep(olderThanMs) {
129
+ const cutoff = new Date(Date.now() - olderThanMs).toISOString();
130
+ const ended = await adapter.all(`SELECT topic FROM ket_stream WHERE ended = 1 AND created_at <= ${p(1)}`, [cutoff]);
131
+ let n = 0;
132
+ for (const row of ended) {
133
+ const r = await adapter.run(`DELETE FROM ket_stream WHERE topic = ${p(1)}`, [String(row.topic)]);
134
+ n += r.changes;
135
+ }
136
+ return n;
137
+ },
138
+ };
139
+ }
140
+ //# sourceMappingURL=streamstore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamstore.js","sourceRoot":"","sources":["../../../../../../packages/ketjs/src/server/streamstore.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,EAAE;AACF,wEAAwE;AACxE,kFAAkF;AAClF,mFAAmF;AACnF,qFAAqF;AACrF,EAAE;AACF,gFAAgF;AAChF,+EAA+E;AAC/E,qFAAqF;AAErF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAmB1C,2EAA2E;AAC3E,MAAM,QAAQ;IACJ,GAAG,GAAG,IAAI,YAAY,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,CAAA;IACnD,SAAS,CAAC,KAAa,EAAE,EAAc;QACrC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QACtB,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QACzB,CAAC,CAAA;IACH,CAAC;IACD,MAAM,CAAC,KAAa;QAClB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACtB,CAAC;CACF;AAED,MAAM,UAAU,iBAAiB;IAC/B,MAAM,MAAM,GAAG,IAAI,GAAG,EAGnB,CAAA;IACH,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAA;IAC/B,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,EAAE;QACzB,IAAI,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;QACrB,IAAI,CAAC,CAAC,EAAE,CAAC;YACP,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;YAC9D,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QAClB,CAAC;QACD,OAAO,CAAC,CAAA;IACV,CAAC,CAAA;IACD,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,KAAK,CAAC,IAAI,KAAI,CAAC;QACf,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM;YAC7B,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAA;YACzC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACxB,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO;YAChC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;YACrB,CAAC,CAAC,IAAI,GAAG,IAAI,CAAA;YACb,CAAC,CAAC,OAAO,GAAG,OAAO,CAAA;YACnB,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;YACtB,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACxB,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO;YACxB,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;YACrB,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,CAAA;YACzD,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAW,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YACtF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QACrE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,KAAK;YACd,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;YACrB,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAW,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAClF,CAAC;QACD,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC;QAC/C,KAAK,CAAC,KAAK,CAAC,WAAW;YACrB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,CAAA;YACvC,IAAI,CAAC,GAAG,CAAC,CAAA;YACT,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM;gBACzB,IAAI,CAAC,CAAC,OAAO,IAAI,IAAI,IAAI,CAAC,CAAC,OAAO,IAAI,MAAM,EAAE,CAAC;oBAC7C,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;oBAChB,CAAC,EAAE,CAAA;gBACL,CAAC;YACH,OAAO,CAAC,CAAA;QACV,CAAC;KACF,CAAA;AACH,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;;CAWzB,CAAA;AAED,MAAM,UAAU,aAAa,CAAC,OAAgB,EAAE,CAAC,GAA2B,EAAE;IAC5E,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAA;IACrD,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,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAA;IAE/B,OAAO;QACL,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,EAAE;QAC1B,KAAK,CAAC,IAAI;YACR,MAAM,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAChC,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM;YAC7B,MAAM,OAAO,CAAC,GAAG,CACf,mEAAmE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,EACrG,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAC5C,CAAA;YACD,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACxB,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO;YAC/B,MAAM,OAAO,CAAC,GAAG,CACf,mFAAmF,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,EACjI,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,CAC5E,CAAA;YACD,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACxB,CAAC;QAED,wDAAwD;QACxD,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO;YACxB,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAC5B,oEAAoE,CAAC,CAAC,CAAC,CAAC,eAAe,EACvF,CAAC,KAAK,CAAC,CACR,CAAA;YACD,IAAI,IAAI,GAAG,KAAK,CAAA;YAChB,IAAI,OAAO,GAAY,IAAI,CAAA;YAC3B,IAAI,IAAI,GAAG,CAAC,CAAA;YACZ,MAAM,OAAO,GAAY,EAAE,CAAA;YAC3B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;gBACrB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;gBACzB,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,GAAG,CAAC,CAAC,CAAA;gBAC9B,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC1B,IAAI,GAAG,IAAI,CAAA;oBACX,OAAO,GAAG,CAAC,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAA;oBAClE,SAAQ;gBACV,CAAC;gBACD,IAAI,GAAG,IAAI,OAAO;oBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAc,EAAE,CAAC,CAAA;YAC9F,CAAC;YACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QAClD,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,KAAK;YACd,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CACzB,oEAAoE,CAAC,CAAC,CAAC,CAAC,EAAE,EAC1E,CAAC,KAAK,CAAC,CACR,CAAA;YACD,OAAO,MAAM,CAAE,CAAC,CAAC,CAAC,CAAmB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;QAC9C,CAAC;QAED,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC;QAE/C,KAAK,CAAC,KAAK,CAAC,WAAW;YACrB,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,CAAC,CAAC,WAAW,EAAE,CAAA;YAC/D,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAC7B,kEAAkE,CAAC,CAAC,CAAC,CAAC,EAAE,EACxE,CAAC,MAAM,CAAC,CACT,CAAA;YACD,IAAI,CAAC,GAAG,CAAC,CAAA;YACT,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;gBACxB,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;gBAChG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAA;YAChB,CAAC;YACD,OAAO,CAAC,CAAA;QACV,CAAC;KACF,CAAA;AACH,CAAC"}