@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,99 @@
1
+ // What a route is allowed to return.
2
+ //
3
+ // The problem this closes: `body` used to be a `string`, so a value that had been
4
+ // through the escaper and a value someone concatenated by hand were the same type.
5
+ // Nothing could tell them apart — not the compiler, not a reviewer skimming a diff.
6
+ // The document shell was concatenation for exactly that reason: it was allowed.
7
+ //
8
+ // So RouteResult is branded. The brand is a type-only symbol, erased at runtime,
9
+ // which means the shape is unforgeable in TypeScript without saying so: a route
10
+ // cannot return `{ body: '<div>' + name }` because that object is not a
11
+ // RouteResult. The only ways to make one are below, and the only one that accepts
12
+ // a string is `raw`, which is one word to grep for and one word to argue about in
13
+ // review. `bytes` and `streamed` carry octets rather than a string, and they refuse
14
+ // markup content types so that the same claim holds for them.
15
+ import { renderToString, html, when, each } from '@ketvietlab/ketjs-view';
16
+ export const NAVIGATION_HEADER = 'x-ket-navigation';
17
+ export const NAVIGATION_VERSION = 'fragment-v1';
18
+ export const NAVIGATION_TYPE = 'text/vnd.ket.fragments+html';
19
+ const made = (body, type, status) => ({ body, type, ...(status === undefined ? {} : { status }) });
20
+ /**
21
+ * A whole document. `<!doctype html>` is a constant rather than a hole, which is
22
+ * why it can be prepended: there is nothing to escape in it.
23
+ */
24
+ export function page(o) {
25
+ return made('<!doctype html>' + renderToString(o.body), 'text/html', o.status);
26
+ }
27
+ /** A fragment: no doctype, for a partial response or an island. */
28
+ export function fragment(body, o = {}) {
29
+ return made(renderToString(body), o.type ?? 'text/html', o.status);
30
+ }
31
+ /** Whether this browser asks for the progressive-navigation representation. */
32
+ export function isNavigationRequest(req) {
33
+ return req.headers[NAVIGATION_HEADER] === NAVIGATION_VERSION;
34
+ }
35
+ /**
36
+ * Serve either the full document or only its named, replaceable slot bodies.
37
+ * Thunks are deliberate: an island outside the selected representation is never
38
+ * constructed merely to be thrown away by the browser.
39
+ */
40
+ export function navigablePage(req, o) {
41
+ const vary = { vary: 'X-Ket-Navigation' };
42
+ if (!isNavigationRequest(req))
43
+ return withHeaders(page({ body: o.document(), status: o.status }), vary);
44
+ const entries = Object.entries(o.slots);
45
+ for (const [name] of entries) {
46
+ if (!/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/.test(name))
47
+ throw new TypeError(`navigation slot "${name}" must be a lowercase dotted name`);
48
+ }
49
+ const body = html `<ket-fragments data-title=${o.title}>${each(entries, ([name]) => name, ([name, render]) => html `<template data-ket-slot=${name}>${render()}</template>`)}</ket-fragments>`;
50
+ return withHeaders(made(renderToString(body), NAVIGATION_TYPE, o.status), vary);
51
+ }
52
+ export function json(value, o = {}) {
53
+ return made(JSON.stringify(value, null, 2), 'application/json', o.status);
54
+ }
55
+ export function text(body, o = {}) {
56
+ return made(body, o.type ?? 'text/plain', o.status);
57
+ }
58
+ /** Octets are not markup, and these two keep it that way; raw() remains the one word to grep for. */
59
+ const octets = (type) => {
60
+ if (/^\s*(?:text\/html|application\/xhtml\+xml|image\/svg\+xml)\b/i.test(type))
61
+ throw new Error(`bytes()/streamed() will not serve "${type}"; use raw() if markup is intended`);
62
+ return type;
63
+ };
64
+ /** Binary data already resident in memory. Unlike raw(), it can never become HTML by accident. */
65
+ export function bytes(body, o) {
66
+ return made(body, octets(o.type), o.status);
67
+ }
68
+ /** A backpressure-aware binary response; the HTTP layer consumes it chunk by chunk. */
69
+ export function streamed(body, o) {
70
+ return made(body, octets(o.type), o.status);
71
+ }
72
+ /**
73
+ * The escape hatch, deliberately named so a search finds every use.
74
+ *
75
+ * Reach for it when the bytes did not come from the escaper and cannot: a sitemap
76
+ * assembled by a library, a cached response, markup from a source you have already
77
+ * decided to trust. Never for a value that came from a request.
78
+ */
79
+ export function raw(body, o = {}) {
80
+ return made(body, o.type ?? 'text/html', o.status);
81
+ }
82
+ /**
83
+ * Add headers to a response without rebuilding it, and without spreading — a
84
+ * spread would produce a plain object that only looks like a RouteResult, which
85
+ * is exactly the hole the brand exists to close.
86
+ */
87
+ export function withHeaders(result, headers) {
88
+ return { ...result, headers: { ...result.headers, ...headers } };
89
+ }
90
+ /**
91
+ * The document every screen sits in, as markup rather than as a string.
92
+ *
93
+ * `lang` reaches the first attribute on the page, which is where i18n starts and
94
+ * where the old concatenated shell put whatever the query string said.
95
+ */
96
+ export function document(o) {
97
+ return html `<html lang=${o.lang}><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">${when(o.title !== undefined, () => html `<title>${o.title}</title>`)}${o.head ?? ''}</head><body>${o.body}</body></html>`;
98
+ }
99
+ //# sourceMappingURL=respond.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"respond.js","sourceRoot":"","sources":["../../../../../../packages/ketjs/src/server/respond.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { AppSpec } from '../kernel/workspace.ts';
2
+ import type { KetModule, Manifest } from '../types.ts';
3
+ import type { RuntimeConfig } from './config.ts';
4
+ export type BootedRuntime = {
5
+ spec: AppSpec;
6
+ config: RuntimeConfig;
7
+ modules: KetModule[];
8
+ manifest: Manifest;
9
+ };
10
+ export declare function bootRuntime(spec: AppSpec, options?: {
11
+ env?: Record<string, string | undefined>;
12
+ port?: number;
13
+ }): Promise<BootedRuntime>;
14
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../../packages/ketjs/src/server/runtime.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,EAAE,aAAa,CAAA;IACrB,OAAO,EAAE,SAAS,EAAE,CAAA;IACpB,QAAQ,EAAE,QAAQ,CAAA;CACnB,CAAA;AAED,wBAAsB,WAAW,CAC/B,IAAI,EAAE,OAAO,EACb,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GACxE,OAAO,CAAC,aAAa,CAAC,CAexB"}
@@ -0,0 +1,25 @@
1
+ // The common process role foundation. HTTP and workers load exactly the same
2
+ // configuration, build artifact, module graph and executable registries; only
3
+ // their outer loops differ.
4
+ import { compose } from '../kernel/compose.js';
5
+ import { readConfig } from './config.js';
6
+ import { registerFunctions } from './fn.js';
7
+ import { registerJobs } from './jobs.js';
8
+ export async function bootRuntime(spec, options = {}) {
9
+ const config = readConfig(options.env ?? process.env, {
10
+ sqliteFile: `.ket/${spec.name}.db`,
11
+ ...spec.serve?.defaults,
12
+ ...(options.port === undefined ? {} : { port: options.port }),
13
+ });
14
+ if (options.port !== undefined)
15
+ config.port = options.port;
16
+ const modules = [...spec.modules, ...(spec.theme ? [spec.theme] : []), ...(spec.themes ?? [])];
17
+ const manifest = compose(modules, {
18
+ appRequires: spec.requires ?? [],
19
+ headless: spec.headless ?? false,
20
+ });
21
+ registerFunctions(modules);
22
+ registerJobs(modules);
23
+ return { spec, config, modules, manifest };
24
+ }
25
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../../../../packages/ketjs/src/server/runtime.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,8EAA8E;AAC9E,4BAA4B;AAE5B,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAYxC,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAa,EACb,OAAO,GAAgE,EAAE;IAEzE,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE;QACpD,UAAU,EAAE,QAAQ,IAAI,CAAC,IAAI,KAAK;QAClC,GAAG,IAAI,CAAC,KAAK,EAAE,QAAQ;QACvB,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;KAC9D,CAAC,CAAA;IACF,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;QAAE,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAA;IAC1D,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAA;IAC9F,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,EAAE;QAChC,WAAW,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE;QAChC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK;KACjC,CAAC,CAAA;IACF,iBAAiB,CAAC,OAAO,CAAC,CAAA;IAC1B,YAAY,CAAC,OAAO,CAAC,CAAA;IACrB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAA;AAC5C,CAAC"}
@@ -0,0 +1,55 @@
1
+ import type { SessionContext, SessionRecord, SessionStore } from './sessionstore.ts';
2
+ import type { Scope } from '../types.ts';
3
+ import type { IncomingMessage } from 'node:http';
4
+ export declare const SESSION_COOKIE = "ket_session";
5
+ export type SessionOptions = {
6
+ store?: SessionStore;
7
+ /**
8
+ * Signing key. It MUST be the same on every pod: a cookie signed by one and
9
+ * rejected by another is a login that works only until the load balancer sends
10
+ * you elsewhere. Absent, one is generated and the fact is said out loud.
11
+ */
12
+ secret?: string;
13
+ /** How long an idle session lives. Refreshed while it is being used. */
14
+ idleTtlMs?: number;
15
+ /** How long any session may live, however active. Not refreshable. */
16
+ absoluteTtlMs?: number;
17
+ /** Sent on the cookie unless the request came in over plain HTTP to localhost. */
18
+ secure?: boolean;
19
+ /** The scope a request with no session gets — a public storefront still needs one. */
20
+ anonymous?: Scope | null;
21
+ now?: () => number;
22
+ };
23
+ /** Cookies as sent by a browser: `a=1; b=2`. Values are not decoded further. */
24
+ export declare function parseCookies(header: string | undefined): Record<string, string>;
25
+ export type Sessions = {
26
+ readonly store: SessionStore;
27
+ /** True when the secret was generated rather than configured — see the banner. */
28
+ readonly ephemeralSecret: boolean;
29
+ start(o: {
30
+ userId: string;
31
+ companies: string[];
32
+ company?: string | null;
33
+ branch?: string | null;
34
+ branches?: string[] | null;
35
+ securityVersion?: number;
36
+ }): Promise<{
37
+ record: SessionRecord;
38
+ cookie: string;
39
+ }>;
40
+ /** The session a request carries, refreshed if it is still alive. */
41
+ of(req: IncomingMessage): Promise<SessionRecord | null>;
42
+ end(req: IncomingMessage): Promise<void>;
43
+ endUser(userId: string): Promise<number>;
44
+ endUserExcept(userId: string, keepId: string): Promise<number>;
45
+ /** Atomically switch or reconcile the live company/branch context. */
46
+ update(record: SessionRecord, context: SessionContext): Promise<SessionRecord | null>;
47
+ /** A cookie that clears the browser's. */
48
+ clearCookie(): string;
49
+ scopeOf(record: SessionRecord | null): Scope | null;
50
+ sweep(): Promise<number>;
51
+ };
52
+ export declare function createSessions(o?: SessionOptions): Promise<Sessions>;
53
+ export { memorySessionStore, dbSessionStore } from './sessionstore.ts';
54
+ export type { SessionContext, SessionStore, SessionRecord } from './sessionstore.ts';
55
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../../../packages/ketjs/src/server/session.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACpF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAEhD,eAAO,MAAM,cAAc,gBAAgB,CAAA;AAE3C,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,CAAC,EAAE,YAAY,CAAA;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,sEAAsE;IACtE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,kFAAkF;IAClF,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,sFAAsF;IACtF,SAAS,CAAC,EAAE,KAAK,GAAG,IAAI,CAAA;IACxB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB,CAAA;AAoBD,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ/E;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;IAC5B,kFAAkF;IAClF,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;IACjC,KAAK,CAAC,CAAC,EAAE;QACP,MAAM,EAAE,MAAM,CAAA;QACd,SAAS,EAAE,MAAM,EAAE,CAAA;QACnB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;QACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;QACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;QAC1B,eAAe,CAAC,EAAE,MAAM,CAAA;KACzB,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,aAAa,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACtD,qEAAqE;IACrE,EAAE,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAA;IACvD,GAAG,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACxC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACxC,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9D,sEAAsE;IACtE,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAA;IACrF,0CAA0C;IAC1C,WAAW,IAAI,MAAM,CAAA;IACrB,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,CAAA;IACnD,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;CACzB,CAAA;AAED,wBAAsB,cAAc,CAAC,CAAC,GAAE,cAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC,CA6I9E;AAED,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACtE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA"}
@@ -0,0 +1,172 @@
1
+ // Sessions: the cookie, the secret, and the runtime that ties them to a store.
2
+ //
3
+ // This is the seam every earlier decision pointed at. `resolveScope` has been one
4
+ // function since D27 precisely so that replacing headers with a session would be
5
+ // one change — and D32 settled the shape a session has to produce: a set of
6
+ // companies to read and one to write.
7
+ import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
8
+ import { KetError } from '../kernel/errors.js';
9
+ import { memorySessionStore } from './sessionstore.js';
10
+ export const SESSION_COOKIE = 'ket_session';
11
+ const DEFAULTS = { idleTtlMs: 7 * 24 * 60 * 60_000, absoluteTtlMs: 30 * 24 * 60 * 60_000 };
12
+ /** `id.signature`, base64url. The signature is what makes a forged id cheap to reject. */
13
+ const sign = (id, secret) => `${id}.${createHmac('sha256', secret).update(id).digest('base64url')}`;
14
+ const unsign = (value, secret) => {
15
+ const dot = value.lastIndexOf('.');
16
+ if (dot <= 0)
17
+ return null;
18
+ const id = value.slice(0, dot);
19
+ const given = Buffer.from(value.slice(dot + 1), 'base64url');
20
+ const want = createHmac('sha256', secret).update(id).digest();
21
+ // Length first: timingSafeEqual throws on a mismatch, and a throw here would be
22
+ // a louder signal than the comparison it is meant to hide.
23
+ if (given.length !== want.length || !timingSafeEqual(given, want))
24
+ return null;
25
+ return id;
26
+ };
27
+ /** Cookies as sent by a browser: `a=1; b=2`. Values are not decoded further. */
28
+ export function parseCookies(header) {
29
+ const out = {};
30
+ for (const part of (header ?? '').split(';')) {
31
+ const at = part.indexOf('=');
32
+ if (at <= 0)
33
+ continue;
34
+ out[part.slice(0, at).trim()] = part.slice(at + 1).trim();
35
+ }
36
+ return out;
37
+ }
38
+ export async function createSessions(o = {}) {
39
+ const now = o.now ?? (() => Date.now());
40
+ const store = o.store ?? memorySessionStore({ now });
41
+ await store.init();
42
+ const ephemeralSecret = !o.secret;
43
+ const secret = o.secret ?? randomBytes(32).toString('base64url');
44
+ const idleTtl = o.idleTtlMs ?? DEFAULTS.idleTtlMs;
45
+ const absoluteTtl = o.absoluteTtlMs ?? DEFAULTS.absoluteTtlMs;
46
+ const cookie = (value, maxAgeSec) => [
47
+ `${SESSION_COOKIE}=${value}`,
48
+ 'Path=/',
49
+ 'HttpOnly',
50
+ // Lax rather than Strict: Strict drops the cookie on any inbound link, so a
51
+ // user following one from email arrives logged out for no security gain here.
52
+ 'SameSite=Lax',
53
+ ...(o.secure === false ? [] : ['Secure']),
54
+ `Max-Age=${maxAgeSec}`,
55
+ ].join('; ');
56
+ const idOf = (req) => {
57
+ const raw = parseCookies(req.headers.cookie)[SESSION_COOKIE];
58
+ return raw ? unsign(raw, secret) : null;
59
+ };
60
+ return {
61
+ store,
62
+ ephemeralSecret,
63
+ async start({ userId, companies, company, branch, branches, securityVersion }) {
64
+ if (!companies.length) {
65
+ throw new KetError({
66
+ code: 'E_SESSION_NO_COMPANY',
67
+ message: `cannot start a session for "${userId}" with no company`,
68
+ hint: 'grant the user at least one company first — a session that can read nothing is not a session',
69
+ });
70
+ }
71
+ const active = company ?? companies[0];
72
+ if (!companies.includes(active)) {
73
+ throw new KetError({
74
+ code: 'E_WRITE_COMPANY_NOT_READABLE',
75
+ message: `session for "${userId}" would write to "${active}" but may only read ${companies.join(', ')}`,
76
+ hint: 'the active company must be one the user is a member of',
77
+ });
78
+ }
79
+ const readableBranches = branches ?? null;
80
+ const activeBranch = branch ?? readableBranches?.[0] ?? null;
81
+ if (activeBranch && readableBranches && !readableBranches.includes(activeBranch)) {
82
+ throw new KetError({
83
+ code: 'E_WRITE_BRANCH_NOT_READABLE',
84
+ message: `session for "${userId}" would write to branch "${activeBranch}" outside its readable set`,
85
+ hint: 'the active branch must be one the user is a member of',
86
+ });
87
+ }
88
+ const at = now();
89
+ const record = {
90
+ id: randomBytes(32).toString('base64url'),
91
+ userId,
92
+ companies: [...companies],
93
+ company: active,
94
+ branch: activeBranch,
95
+ branches: readableBranches,
96
+ securityVersion: securityVersion ?? 0,
97
+ revision: 0,
98
+ createdAt: at,
99
+ expiresAt: at + idleTtl,
100
+ };
101
+ await store.create(record);
102
+ return { record, cookie: cookie(sign(record.id, secret), Math.floor(idleTtl / 1000)) };
103
+ },
104
+ async of(req) {
105
+ const id = idOf(req);
106
+ if (!id)
107
+ return null;
108
+ const record = await store.read(id);
109
+ if (!record)
110
+ return null;
111
+ const at = now();
112
+ // Refreshed while in use, but never past the absolute limit: a session that
113
+ // renews forever is a session that never ends.
114
+ const ceiling = record.createdAt + absoluteTtl;
115
+ if (at >= ceiling) {
116
+ await store.destroy(id);
117
+ return null;
118
+ }
119
+ const next = Math.min(at + idleTtl, ceiling);
120
+ if (next > record.expiresAt) {
121
+ if ((await store.touch(id, next)) === null)
122
+ return null;
123
+ record.expiresAt = next;
124
+ }
125
+ return record;
126
+ },
127
+ async end(req) {
128
+ const id = idOf(req);
129
+ if (id)
130
+ await store.destroy(id);
131
+ },
132
+ endUser(userId) {
133
+ return store.destroyUser(userId);
134
+ },
135
+ endUserExcept(userId, keepId) {
136
+ return store.destroyUserExcept(userId, keepId);
137
+ },
138
+ update(record, context) {
139
+ if (!context.company || !context.companies.includes(context.company)) {
140
+ throw new KetError({
141
+ code: 'E_WRITE_COMPANY_NOT_READABLE',
142
+ message: 'the active company must be one of the session readable companies',
143
+ });
144
+ }
145
+ if (context.branch && context.branches && !context.branches.includes(context.branch)) {
146
+ throw new KetError({
147
+ code: 'E_WRITE_BRANCH_NOT_READABLE',
148
+ message: 'the active branch must be one of the session readable branches',
149
+ });
150
+ }
151
+ return store.updateContext(record.id, record.revision, context);
152
+ },
153
+ clearCookie() {
154
+ return cookie('', 0);
155
+ },
156
+ scopeOf(record) {
157
+ if (!record)
158
+ return o.anonymous ?? null;
159
+ return {
160
+ company: record.company,
161
+ companies: record.companies,
162
+ branch: record.branch,
163
+ branches: record.branches,
164
+ };
165
+ },
166
+ sweep() {
167
+ return store.sweep(now());
168
+ },
169
+ };
170
+ }
171
+ export { memorySessionStore, dbSessionStore } from './sessionstore.js';
172
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../../../../packages/ketjs/src/server/session.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,kFAAkF;AAClF,iFAAiF;AACjF,4EAA4E;AAC5E,sCAAsC;AAEtC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAKtD,MAAM,CAAC,MAAM,cAAc,GAAG,aAAa,CAAA;AAqB3C,MAAM,QAAQ,GAAG,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,aAAa,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,CAAA;AAE1F,0FAA0F;AAC1F,MAAM,IAAI,GAAG,CAAC,EAAU,EAAE,MAAc,EAAU,EAAE,CAClD,GAAG,EAAE,IAAI,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAA;AAExE,MAAM,MAAM,GAAG,CAAC,KAAa,EAAE,MAAc,EAAiB,EAAE;IAC9D,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;IAClC,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IACzB,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IAC9B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,CAAA;IAC5D,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAA;IAC7D,gFAAgF;IAChF,2DAA2D;IAC3D,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAC9E,OAAO,EAAE,CAAA;AACX,CAAC,CAAA;AAED,gFAAgF;AAChF,MAAM,UAAU,YAAY,CAAC,MAA0B;IACrD,MAAM,GAAG,GAA2B,EAAE,CAAA;IACtC,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7C,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAC5B,IAAI,EAAE,IAAI,CAAC;YAAE,SAAQ;QACrB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAC3D,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AA2BD,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,CAAC,GAAmB,EAAE;IACzD,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;IACvC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,kBAAkB,CAAC,EAAE,GAAG,EAAE,CAAC,CAAA;IACpD,MAAM,KAAK,CAAC,IAAI,EAAE,CAAA;IAElB,MAAM,eAAe,GAAG,CAAC,CAAC,CAAC,MAAM,CAAA;IACjC,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,IAAI,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;IAChE,MAAM,OAAO,GAAG,CAAC,CAAC,SAAS,IAAI,QAAQ,CAAC,SAAS,CAAA;IACjD,MAAM,WAAW,GAAG,CAAC,CAAC,aAAa,IAAI,QAAQ,CAAC,aAAa,CAAA;IAE7D,MAAM,MAAM,GAAG,CAAC,KAAa,EAAE,SAAiB,EAAU,EAAE,CAC1D;QACE,GAAG,cAAc,IAAI,KAAK,EAAE;QAC5B,QAAQ;QACR,UAAU;QACV,4EAA4E;QAC5E,8EAA8E;QAC9E,cAAc;QACd,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACzC,WAAW,SAAS,EAAE;KACvB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEd,MAAM,IAAI,GAAG,CAAC,GAAoB,EAAiB,EAAE;QACnD,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,MAA4B,CAAC,CAAC,cAAc,CAAC,CAAA;QAClF,OAAO,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACzC,CAAC,CAAA;IAED,OAAO;QACL,KAAK;QACL,eAAe;QAEf,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE;YAC3E,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;gBACtB,MAAM,IAAI,QAAQ,CAAC;oBACjB,IAAI,EAAE,sBAAsB;oBAC5B,OAAO,EAAE,+BAA+B,MAAM,mBAAmB;oBACjE,IAAI,EAAE,8FAA8F;iBACrG,CAAC,CAAA;YACJ,CAAC;YACD,MAAM,MAAM,GAAG,OAAO,IAAK,SAAS,CAAC,CAAC,CAAY,CAAA;YAClD,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAChC,MAAM,IAAI,QAAQ,CAAC;oBACjB,IAAI,EAAE,8BAA8B;oBACpC,OAAO,EAAE,gBAAgB,MAAM,qBAAqB,MAAM,uBAAuB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;oBACvG,IAAI,EAAE,wDAAwD;iBAC/D,CAAC,CAAA;YACJ,CAAC;YACD,MAAM,gBAAgB,GAAG,QAAQ,IAAI,IAAI,CAAA;YACzC,MAAM,YAAY,GAAG,MAAM,IAAI,gBAAgB,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;YAC5D,IAAI,YAAY,IAAI,gBAAgB,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;gBACjF,MAAM,IAAI,QAAQ,CAAC;oBACjB,IAAI,EAAE,6BAA6B;oBACnC,OAAO,EAAE,gBAAgB,MAAM,4BAA4B,YAAY,4BAA4B;oBACnG,IAAI,EAAE,uDAAuD;iBAC9D,CAAC,CAAA;YACJ,CAAC;YACD,MAAM,EAAE,GAAG,GAAG,EAAE,CAAA;YAChB,MAAM,MAAM,GAAkB;gBAC5B,EAAE,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;gBACzC,MAAM;gBACN,SAAS,EAAE,CAAC,GAAG,SAAS,CAAC;gBACzB,OAAO,EAAE,MAAM;gBACf,MAAM,EAAE,YAAY;gBACpB,QAAQ,EAAE,gBAAgB;gBAC1B,eAAe,EAAE,eAAe,IAAI,CAAC;gBACrC,QAAQ,EAAE,CAAC;gBACX,SAAS,EAAE,EAAE;gBACb,SAAS,EAAE,EAAE,GAAG,OAAO;aACxB,CAAA;YACD,MAAM,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,EAAE,CAAA;QACxF,CAAC;QAED,KAAK,CAAC,EAAE,CAAC,GAAG;YACV,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;YACpB,IAAI,CAAC,EAAE;gBAAE,OAAO,IAAI,CAAA;YACpB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YACnC,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAA;YACxB,MAAM,EAAE,GAAG,GAAG,EAAE,CAAA;YAChB,4EAA4E;YAC5E,+CAA+C;YAC/C,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,GAAG,WAAW,CAAA;YAC9C,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC;gBAClB,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;gBACvB,OAAO,IAAI,CAAA;YACb,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,OAAO,CAAC,CAAA;YAC5C,IAAI,IAAI,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;gBAC5B,IAAI,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI;oBAAE,OAAO,IAAI,CAAA;gBACvD,MAAM,CAAC,SAAS,GAAG,IAAI,CAAA;YACzB,CAAC;YACD,OAAO,MAAM,CAAA;QACf,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,GAAG;YACX,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;YACpB,IAAI,EAAE;gBAAE,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QACjC,CAAC;QAED,OAAO,CAAC,MAAM;YACZ,OAAO,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;QAClC,CAAC;QAED,aAAa,CAAC,MAAM,EAAE,MAAM;YAC1B,OAAO,KAAK,CAAC,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAChD,CAAC;QAED,MAAM,CAAC,MAAM,EAAE,OAAO;YACpB,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;gBACrE,MAAM,IAAI,QAAQ,CAAC;oBACjB,IAAI,EAAE,8BAA8B;oBACpC,OAAO,EAAE,kEAAkE;iBAC5E,CAAC,CAAA;YACJ,CAAC;YACD,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,QAAQ,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBACrF,MAAM,IAAI,QAAQ,CAAC;oBACjB,IAAI,EAAE,6BAA6B;oBACnC,OAAO,EAAE,gEAAgE;iBAC1E,CAAC,CAAA;YACJ,CAAC;YACD,OAAO,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QACjE,CAAC;QAED,WAAW;YACT,OAAO,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,CAAA;QACtB,CAAC;QAED,OAAO,CAAC,MAAM;YACZ,IAAI,CAAC,MAAM;gBAAE,OAAO,CAAC,CAAC,SAAS,IAAI,IAAI,CAAA;YACvC,OAAO;gBACL,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;aAC1B,CAAA;QACH,CAAC;QAED,KAAK;YACH,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAA;QAC3B,CAAC;KACF,CAAA;AACH,CAAC;AAED,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA"}
@@ -0,0 +1,54 @@
1
+ import type { Adapter } from '../types.ts';
2
+ /** What a session carries. Everything a request needs to know who it is. */
3
+ export type SessionRecord = {
4
+ id: string;
5
+ userId: string;
6
+ /** Companies this session may read — becomes scope.companies (D32). */
7
+ companies: string[];
8
+ /** The one it writes to — becomes scope.company. Always in `companies`. */
9
+ company: string | null;
10
+ /** The one operational branch new company+branch rows are stamped with. */
11
+ branch: string | null;
12
+ branches: string[] | null;
13
+ /** Invalidates sessions after credential or account-state changes. */
14
+ securityVersion: number;
15
+ /** Compare-and-set revision for context switches and live reconciliation. */
16
+ revision: number;
17
+ createdAt: number;
18
+ /** Refreshed while in use, never past createdAt + absoluteTtl. */
19
+ expiresAt: number;
20
+ };
21
+ export type SessionStore = {
22
+ readonly name: string;
23
+ init(): Promise<void>;
24
+ create(record: SessionRecord): Promise<void>;
25
+ read(id: string): Promise<SessionRecord | null>;
26
+ /** Extend a live session. Returns the new expiry, or null if it was already gone. */
27
+ touch(id: string, expiresAt: number): Promise<number | null>;
28
+ /** Replace identity scope only if nobody changed this session since it was read. */
29
+ updateContext(id: string, expectedRevision: number, context: SessionContext): Promise<SessionRecord | null>;
30
+ destroy(id: string): Promise<void>;
31
+ /** List live sessions for profile/admin screens. */
32
+ listUser(userId: string): Promise<SessionRecord[]>;
33
+ /** Every session of one user — logging out everywhere, or revoking an account. */
34
+ destroyUser(userId: string): Promise<number>;
35
+ /** Rotate credentials while keeping the session that performed the change. */
36
+ destroyUserExcept(userId: string, keepId: string): Promise<number>;
37
+ /** Remove what has expired. Returns rows removed. */
38
+ sweep(now: number): Promise<number>;
39
+ };
40
+ export type SessionContext = Pick<SessionRecord, 'companies' | 'company' | 'branches' | 'branch' | 'securityVersion'>;
41
+ export declare function memorySessionStore(o?: {
42
+ now?: () => number;
43
+ }): SessionStore;
44
+ /**
45
+ * One row per session, in whatever database the app already has.
46
+ *
47
+ * This is the store that makes several pods work at all, and it needs no extra
48
+ * infrastructure to do it — which is the point. Reach for Redis when the read
49
+ * traffic justifies it, not before.
50
+ */
51
+ export declare function dbSessionStore(adapter: Adapter, o?: {
52
+ now?: () => number;
53
+ }): SessionStore;
54
+ //# sourceMappingURL=sessionstore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionstore.d.ts","sourceRoot":"","sources":["../../../../../packages/ketjs/src/server/sessionstore.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAE1C,4EAA4E;AAC5E,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,uEAAuE;IACvE,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACzB,sEAAsE;IACtE,eAAe,EAAE,MAAM,CAAA;IACvB,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrB,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5C,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAA;IAC/C,qFAAqF;IACrF,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IAC5D,oFAAoF;IACpF,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAA;IAC3G,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClC,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAAA;IAClD,kFAAkF;IAClF,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC5C,8EAA8E;IAC9E,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAClE,qDAAqD;IACrD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACpC,CAAA;AAED,MAAM,MAAM,cAAc,GAAG,IAAI,CAC/B,aAAa,EACb,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,iBAAiB,CACpE,CAAA;AAID,wBAAgB,kBAAkB,CAAC,CAAC,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAAO,GAAG,YAAY,CA4E/E;AAmBD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAAO,GAAG,YAAY,CAyI7F"}