@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,40 @@
1
+ // View models ("drops") are the only data a theme may read.
2
+ //
3
+ // A drop is a null-prototype, frozen projection of exactly the fields the owning
4
+ // module declared in `views`. The ORM, the adapter and every server function stay
5
+ // on the other side of this boundary — a theme literally has no reference to reach.
6
+ import { KetError } from '../kernel/errors.js';
7
+ export function makeDrop(manifest, viewKey, row) {
8
+ const view = manifest.views[viewKey];
9
+ if (!view) {
10
+ throw new KetError({
11
+ code: 'E_UNKNOWN_VIEW',
12
+ message: `no view model "${viewKey}"`,
13
+ hint: `declared views: ${Object.keys(manifest.views).join(', ') || '(none)'}`,
14
+ });
15
+ }
16
+ const drop = Object.create(null);
17
+ for (const f of view.fields)
18
+ drop[f] = row[f] ?? null;
19
+ return Object.freeze(drop);
20
+ }
21
+ export function makeDrops(manifest, viewKey, rows) {
22
+ return rows.map((r) => makeDrop(manifest, viewKey, r));
23
+ }
24
+ // Any value handed to a theme passes through here. Anything that is not plain data
25
+ // is refused rather than silently stringified.
26
+ export function sealScope(scope) {
27
+ const out = Object.create(null);
28
+ for (const [k, v] of Object.entries(scope)) {
29
+ if (typeof v === 'function') {
30
+ throw new KetError({
31
+ code: 'E_SCOPE_CALLABLE',
32
+ message: `scope key "${k}" is a function`,
33
+ hint: 'themes receive data only; move behaviour into a server function',
34
+ });
35
+ }
36
+ out[k] = v;
37
+ }
38
+ return Object.freeze(out);
39
+ }
40
+ //# sourceMappingURL=viewmodel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewmodel.js","sourceRoot":"","sources":["../../../../../../packages/ketjs/src/theme/viewmodel.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,EAAE;AACF,iFAAiF;AACjF,kFAAkF;AAClF,oFAAoF;AAEpF,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAK9C,MAAM,UAAU,QAAQ,CAAC,QAAkB,EAAE,OAAe,EAAE,GAAQ;IACpE,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACpC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,QAAQ,CAAC;YACjB,IAAI,EAAE,gBAAgB;YACtB,OAAO,EAAE,kBAAkB,OAAO,GAAG;YACrC,IAAI,EAAE,mBAAmB,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,EAAE;SAC9E,CAAC,CAAA;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAS,CAAA;IACxC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM;QAAE,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;IACrD,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;AAC5B,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAkB,EAAE,OAAe,EAAE,IAAW;IACxE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;AACxD,CAAC;AAED,mFAAmF;AACnF,+CAA+C;AAC/C,MAAM,UAAU,SAAS,CAAC,KAA8B;IACtD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAA4B,CAAA;IAC1D,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3C,IAAI,OAAO,CAAC,KAAK,UAAU,EAAE,CAAC;YAC5B,MAAM,IAAI,QAAQ,CAAC;gBACjB,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,cAAc,CAAC,iBAAiB;gBACzC,IAAI,EAAE,iEAAiE;aACxE,CAAC,CAAA;QACJ,CAAC;QACD,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IACZ,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;AAC3B,CAAC"}
@@ -0,0 +1,614 @@
1
+ /**
2
+ * `decimal` and `float` differ in storage, not in arithmetic.
3
+ *
4
+ * Odoo takes this split and it is the right one: a quantity or a price is stored
5
+ * as exact decimal and computed as a binary float, with explicit rounding helpers
6
+ * standing between the two. Storing it as a float instead means every trip through
7
+ * the database can put back the error the rounding just took out — 0.1 written to
8
+ * a double comes back as 0.1000000000000000055.
9
+ */
10
+ /**
11
+ * A route, and whether a stranger may reach it.
12
+ *
13
+ * The bare function form is the common case and stays terse. Anything a request
14
+ * with no session may see has to say so — a login form is the obvious one, and
15
+ * everything else defaults to closed, because a default of open is a default
16
+ * nobody notices until it is on the internet.
17
+ */
18
+ export type RouteEntry = ((ctx: import('./server/boot.ts').ServeContext) => import('./server/boot.ts').Route) | {
19
+ anonymous?: boolean;
20
+ handler: (ctx: import('./server/boot.ts').ServeContext) => import('./server/boot.ts').Route;
21
+ };
22
+ export type Scalar = 'id' | 'text' | 'int' | 'float' | 'decimal' | 'bool' | 'json' | 'date' | 'datetime';
23
+ export type FieldBase = Scalar | 'ref';
24
+ export type ParsedType = {
25
+ base: FieldBase;
26
+ optional: boolean;
27
+ target?: string;
28
+ };
29
+ export type TypeParse = ({
30
+ ok: true;
31
+ } & ParsedType) | {
32
+ ok: false;
33
+ reason: string;
34
+ };
35
+ export type Field = ParsedType & {
36
+ by: string;
37
+ };
38
+ export type IndexDef = {
39
+ fields: string[];
40
+ unique?: boolean;
41
+ };
42
+ export type ComposedIndex = {
43
+ fields: string[];
44
+ unique: boolean;
45
+ by: string;
46
+ };
47
+ export type ComposedModel = {
48
+ owner: string;
49
+ scope: ModelScope;
50
+ timestamps: boolean;
51
+ fields: Record<string, Field>;
52
+ indexes: Record<string, ComposedIndex>;
53
+ };
54
+ /**
55
+ * Where a model's rows live in the two isolation dimensions.
56
+ *
57
+ * 'shared' tenant-wide. Master data: products, partners, price lists.
58
+ * 'company' one legal entity's rows. The filter is mandatory and a module
59
+ * cannot widen it — a mistake here leaks between legal entities
60
+ * that share a table, which no database boundary would catch.
61
+ * 'company+branch' additionally carries a branch. Branch is an operational
62
+ * dimension, not an isolation boundary: aggregating across the
63
+ * branches of one company is ordinary and needs no permission.
64
+ *
65
+ * There is no default. A model that does not say is a build error, because the
66
+ * safe-looking default — 'shared' — is the one that leaks.
67
+ */
68
+ export type ModelScope = 'shared' | 'company' | 'company+branch';
69
+ export type ModelDef = {
70
+ scope: ModelScope;
71
+ /** Add optional createdAt/updatedAt fields and maintain them on every write path. */
72
+ timestamps?: boolean;
73
+ fields: Record<string, string>;
74
+ /** Named database indexes. Names are local to the model and remain stable across migrations. */
75
+ indexes?: Record<string, IndexDef>;
76
+ };
77
+ export type JointDef = {
78
+ props?: Record<string, string>;
79
+ multiple?: boolean;
80
+ };
81
+ /**
82
+ * One entry in the navigation tree — an app, a section inside it, or a link.
83
+ *
84
+ * Declared by the module that owns the screen, not stored as rows. Odoo keeps
85
+ * `ir.ui.menu` in the database so a customer can rearrange it, and pays for that
86
+ * with a menu that can point at a module nobody installed. Here a menu entry is
87
+ * checked when the deployment is composed: an unknown parent is a build error, and
88
+ * a link to a function nobody ships cannot be saved because there is nothing to
89
+ * save it in.
90
+ *
91
+ * `needs` is what keeps the tree honest. A menu item whose function the viewer may
92
+ * not call does not render — a menu that offers what it cannot deliver is a menu
93
+ * that lies, and the 401 arrives after the click rather than instead of it.
94
+ */
95
+ export type MenuDef = {
96
+ /** A message key, resolved against the owning module. Falls back to itself. */
97
+ label: string;
98
+ /** The entry above this one. Absent means it is an app: a root of the tree. */
99
+ parent?: string;
100
+ /** Where it goes. Absent means it is a heading rather than a link. */
101
+ path?: string;
102
+ /** A function key the viewer must be permitted to call for this to appear. */
103
+ needs?: string;
104
+ /** Lower sorts first. Ties fall back to the label. */
105
+ sequence?: number;
106
+ /**
107
+ * The glyph shown beside this entry, by semantic name. The declaring module
108
+ * owns the choice; the theme owns the drawing and fallback. An unknown name
109
+ * loses its glyph, not its row.
110
+ */
111
+ icon?: string;
112
+ };
113
+ /**
114
+ * A section is a renderable a page composes by data. The settings schema is what
115
+ * makes that data checkable — and it is the same schema an agent is handed.
116
+ */
117
+ export type SectionDef = {
118
+ title?: string;
119
+ settings?: Record<string, string>;
120
+ };
121
+ export type ViewDef = {
122
+ of: string;
123
+ fields: string[];
124
+ };
125
+ /**
126
+ * A CMS content type is a composition-time contract, not a row created by an
127
+ * administrator. Modules register the shape they own; website data stores the
128
+ * values after validating them against this registry. Keeping the registry in the
129
+ * manifest gives themes, agents and backend editors the same closed vocabulary.
130
+ */
131
+ export type ContentTypeDef = {
132
+ /** Message key local to the declaring module. */
133
+ label: string;
134
+ /** Message key local to the declaring module. */
135
+ pluralLabel: string;
136
+ /** Custom fields stored with a revision. Values use the ordinary Ket scalar syntax. */
137
+ fields?: Record<string, string>;
138
+ /** Local names or fully-qualified taxonomy names this type accepts. */
139
+ taxonomies?: string[];
140
+ archivePath?: string;
141
+ /** A route pattern containing `{slug}` for one persisted or adapted record. */
142
+ detailPath?: string;
143
+ /** Optional public functions for content backed by a business module rather than website.Entry. */
144
+ source?: {
145
+ list: string;
146
+ get: string;
147
+ };
148
+ };
149
+ export type TaxonomyDef = {
150
+ /** Message key local to the declaring module. */
151
+ label: string;
152
+ /** Message key local to the declaring module. */
153
+ pluralLabel: string;
154
+ hierarchical?: boolean;
155
+ /** Local names or fully-qualified content type names this taxonomy accepts. */
156
+ contentTypes: string[];
157
+ };
158
+ export type ComposedContentType = Omit<ContentTypeDef, 'taxonomies'> & {
159
+ by: string;
160
+ fields: Record<string, string>;
161
+ taxonomies: string[];
162
+ };
163
+ export type ComposedTaxonomy = Omit<TaxonomyDef, 'contentTypes'> & {
164
+ by: string;
165
+ hierarchical: boolean;
166
+ contentTypes: string[];
167
+ };
168
+ /**
169
+ * A relation between two models, declared by a module that depends on both.
170
+ *
171
+ * There is no lazy side. Nothing loads itself when touched, so the N+1 that makes
172
+ * ORMs slow in ways nobody can see is not expressible: a caller either asks for the
173
+ * related rows with preload(), or does not get them.
174
+ */
175
+ export type RelationDef = {
176
+ belongsTo: string;
177
+ by: string;
178
+ } | {
179
+ hasMany: string;
180
+ by: string;
181
+ };
182
+ export type ComposedRelation = {
183
+ kind: 'belongsTo' | 'hasMany';
184
+ target: string;
185
+ by: string;
186
+ declaredBy: string;
187
+ };
188
+ export type FnSpec = {
189
+ /**
190
+ * Callable by a request carrying no session.
191
+ *
192
+ * Default false, and the default is the point. A request that has not logged in
193
+ * is not an unrestricted caller, it is a stranger — treating "no identity" as
194
+ * "no limits" is how logging in became optional. The exceptions are real and
195
+ * few: the function that checks a password (there is no session yet), and
196
+ * whatever a public storefront reads.
197
+ */
198
+ anonymous?: boolean;
199
+ /**
200
+ * Whether the generic `/_ket/fn/*` transport may call this function.
201
+ *
202
+ * `internal` functions remain callable by trusted application/module routes and
203
+ * in-process code, but they are absent from the public HTTP and agent surfaces.
204
+ * Authentication checks and one-time credential issuance belong here: their
205
+ * route owns rate limiting, origin checks and response shaping.
206
+ */
207
+ exposure?: 'http' | 'internal';
208
+ /**
209
+ * Allow the one-shot `ket provision` command to invoke this internal function.
210
+ * Provisioning is opt-in and never makes a function HTTP- or agent-callable.
211
+ */
212
+ provision?: boolean;
213
+ input?: Record<string, string>;
214
+ output?: Record<string, string>;
215
+ effects?: string[];
216
+ /**
217
+ * Read across legal entities. Consolidated reporting needs it; almost nothing
218
+ * else does. Declaring it puts the function in the manifest, the upgrade diff and
219
+ * the agent descriptor, so a cross-company read is visible rather than lost among
220
+ * ordinary queries.
221
+ */
222
+ crossCompany?: boolean;
223
+ idempotent?: boolean;
224
+ dryRun?: boolean;
225
+ agent?: boolean;
226
+ handler: (ctx: Ctx, args: Record<string, unknown>) => unknown;
227
+ };
228
+ export type FnMeta = {
229
+ by: string;
230
+ anonymous: boolean;
231
+ exposure: 'http' | 'internal';
232
+ provision: boolean;
233
+ input: Record<string, string>;
234
+ output: Record<string, string>;
235
+ effects: string[];
236
+ crossCompany: boolean;
237
+ idempotent: boolean;
238
+ dryRun: boolean;
239
+ agent: boolean;
240
+ };
241
+ /**
242
+ * A durable background operation. Jobs deliberately reuse the function effect
243
+ * vocabulary, including enqueue: moving work out of an HTTP request or chaining
244
+ * it to another job must not become a way around the operation boundary.
245
+ */
246
+ export type JobSpec = {
247
+ queue?: string;
248
+ input?: Record<string, string>;
249
+ effects?: string[];
250
+ crossCompany?: boolean;
251
+ /** At-least-once delivery makes acknowledging idempotency mandatory. */
252
+ idempotent: true;
253
+ maxAttempts?: number;
254
+ timeoutMs?: number;
255
+ handler: (ctx: JobContext, args: Record<string, unknown>) => Promise<void>;
256
+ };
257
+ export type JobMeta = {
258
+ by: string;
259
+ queue: string;
260
+ input: Record<string, string>;
261
+ effects: string[];
262
+ crossCompany: boolean;
263
+ idempotent: true;
264
+ maxAttempts: number;
265
+ timeoutMs: number;
266
+ };
267
+ export type JobEnqueueOptions = {
268
+ runAt?: Date;
269
+ uniqueKey?: string;
270
+ /** Zero is highest priority. */
271
+ priority?: number;
272
+ };
273
+ export type JobEnqueueResult = {
274
+ id: string;
275
+ existing: boolean;
276
+ };
277
+ export type JobExecution = {
278
+ id: string;
279
+ key: string;
280
+ queue: string;
281
+ attempt: number;
282
+ maxAttempts: number;
283
+ };
284
+ export type AppMeta = {
285
+ /** Shown in the app list. A module without this is machinery, not an app. */
286
+ app?: boolean;
287
+ title?: string;
288
+ summary?: string;
289
+ category?: string;
290
+ /**
291
+ * The boundary between what a module permits and what an operator chooses.
292
+ *
293
+ * 'manual' — installed only when someone asks for it by name (the default)
294
+ * 'auto' — installs itself as soon as everything it depends on is installed
295
+ * 'never' — cannot be installed on its own at all; it arrives only by being
296
+ * depended on, which is how machinery stays out of the app list
297
+ *
298
+ * A module says what it permits. Whether 'auto' actually fires is the
299
+ * deployment's call — see RuntimeConfig.autoInstall, which a developer turns off
300
+ * when they want to watch each install happen rather than arrive.
301
+ */
302
+ install?: InstallPolicy;
303
+ /**
304
+ * Whether an operator may remove this module once it is installed.
305
+ *
306
+ * `install` draws the boundary on the way in; this one draws it on the way out,
307
+ * and they are genuinely different axes. The case that forces it: the backend is
308
+ * the screen you would use to put something back, so a deployment that let you
309
+ * remove it would let you remove your way out of ever fixing it. Default true —
310
+ * refusing removal is the exception and has to be argued for.
311
+ */
312
+ removable?: boolean;
313
+ /** The older spelling of `install: 'auto'`. Normalised away by defineModule. */
314
+ autoInstall?: boolean;
315
+ };
316
+ export type InstallPolicy = 'manual' | 'auto' | 'never';
317
+ export type ModuleSpec = AppMeta & {
318
+ kind?: 'module' | 'theme';
319
+ name: string;
320
+ version?: string;
321
+ depends?: string[];
322
+ models?: Record<string, ModelDef>;
323
+ extend?: Record<string, Record<string, string>>;
324
+ /** Navigation entries this module contributes. Keys are ids other menus parent onto. */
325
+ menus?: Record<string, MenuDef>;
326
+ joints?: Record<string, JointDef>;
327
+ fills?: Record<string, string>;
328
+ /**
329
+ * Joints this module wants gone — "owner:joint" keys, as fills use.
330
+ *
331
+ * Removing rather than hiding, because CSS hides at the wrong layer: the data
332
+ * still travels, and the tab order still walks through what nobody can see. A
333
+ * column a business does not track should not be in the HTML at all.
334
+ *
335
+ * It needs the same depends as a fill. Omitting something you never declared a
336
+ * dependency on is a decision about somebody else's screen made by a module that
337
+ * may not even be installed with it.
338
+ */
339
+ omits?: string[];
340
+ functions?: Record<string, FnSpec>;
341
+ jobs?: Record<string, JobSpec>;
342
+ views?: Record<string, ViewDef>;
343
+ requires?: string[];
344
+ tokens?: Record<string, string>;
345
+ templates?: Record<string, string>;
346
+ provides?: string[];
347
+ /**
348
+ * Static files this module ships — stylesheets, icons, fonts. Served under
349
+ * /_ket/asset/<module>/, namespaced so two modules may both ship tokens.css,
350
+ * and only for as long as the module is installed.
351
+ */
352
+ assets?: URL | string;
353
+ /**
354
+ * Stylesheets for the head of every page, relative to `assets`, in the order
355
+ * written. Across modules the order is dependency order, so a module that
356
+ * extends another loads after it and can override it.
357
+ *
358
+ * Declared rather than linked by hand: an app that names another module's
359
+ * stylesheet has to know that module's file layout, and goes on linking it long
360
+ * after the module is uninstalled.
361
+ */
362
+ styles?: string[];
363
+ /**
364
+ * Routes this module serves, one factory per path or dynamic segment pattern.
365
+ *
366
+ * A parameter occupies one whole segment (`/products/{slug}`), and its decoded
367
+ * value reaches the handler in the route params. The path is data so composition
368
+ * can settle ownership — two modules claiming one path is an error at build, not
369
+ * a race at boot. The handler is a factory because it needs the running server,
370
+ * which does not exist yet. Dispatch checks the live manifest, so a route belonging
371
+ * to an uninstalled module is 404 rather than quietly still answering.
372
+ */
373
+ routes?: Record<string, RouteEntry>;
374
+ /** Interactive views a theme may place but never write. */
375
+ islands?: Record<string, import('@ketvietlab/ketjs-view').IslandDefinition>;
376
+ sections?: Record<string, SectionDef>;
377
+ contentTypes?: Record<string, ContentTypeDef>;
378
+ taxonomies?: Record<string, TaxonomyDef>;
379
+ relations?: Record<string, Record<string, RelationDef>>;
380
+ /** Strings this module owns, per locale. Keys get the module name prefixed. */
381
+ messages?: Record<string, Record<string, import('./kernel/i18n.ts').Message>>;
382
+ };
383
+ export type KetModule = Readonly<AppMeta> & {
384
+ readonly kind: 'module' | 'theme';
385
+ readonly name: string;
386
+ readonly version: string;
387
+ readonly depends: readonly string[];
388
+ readonly models: Record<string, ModelDef>;
389
+ readonly extend: Record<string, Record<string, string>>;
390
+ readonly menus: Record<string, MenuDef>;
391
+ readonly joints: Record<string, JointDef>;
392
+ readonly omits: readonly string[];
393
+ readonly fills: Record<string, string>;
394
+ readonly functions: Record<string, FnSpec>;
395
+ readonly jobs: Record<string, JobSpec>;
396
+ readonly views: Record<string, ViewDef>;
397
+ readonly requires: readonly string[];
398
+ readonly tokens: Record<string, string>;
399
+ readonly templates: Record<string, string>;
400
+ readonly provides: readonly string[];
401
+ readonly assets: string | URL | null;
402
+ readonly styles: readonly string[];
403
+ readonly routes: Record<string, RouteEntry>;
404
+ readonly islands: Record<string, import('@ketvietlab/ketjs-view').IslandDefinition>;
405
+ readonly sections: Record<string, SectionDef>;
406
+ readonly contentTypes: Record<string, ContentTypeDef>;
407
+ readonly taxonomies: Record<string, TaxonomyDef>;
408
+ readonly relations: Record<string, Record<string, RelationDef>>;
409
+ readonly messages: Record<string, Record<string, import('./kernel/i18n.ts').Message>>;
410
+ };
411
+ export type Manifest = {
412
+ ket: string;
413
+ order: string[];
414
+ modules: Record<string, {
415
+ version: string;
416
+ kind: string;
417
+ depends: string[];
418
+ install: InstallPolicy;
419
+ removable: boolean;
420
+ } & AppMeta>;
421
+ models: Record<string, ComposedModel>;
422
+ menus: Record<string, MenuDef & {
423
+ by: string;
424
+ }>;
425
+ joints: Record<string, {
426
+ owner: string;
427
+ props: Record<string, string>;
428
+ multiple: boolean;
429
+ omittedBy: string[];
430
+ }>;
431
+ fills: Array<{
432
+ joint: string;
433
+ by: string;
434
+ template: string;
435
+ }>;
436
+ functions: Record<string, FnMeta>;
437
+ jobs: Record<string, JobMeta>;
438
+ views: Record<string, ViewDef & {
439
+ by: string;
440
+ }>;
441
+ regions: {
442
+ required: string[];
443
+ provided: Record<string, string[]>;
444
+ };
445
+ islands: Record<string, {
446
+ by: string;
447
+ props: Record<string, string>;
448
+ key?: string[];
449
+ client?: {
450
+ src: string;
451
+ export: string;
452
+ };
453
+ }>;
454
+ sections: Record<string, SectionDef & {
455
+ by: string;
456
+ }>;
457
+ contentTypes: Record<string, ComposedContentType>;
458
+ taxonomies: Record<string, ComposedTaxonomy>;
459
+ relations: Record<string, Record<string, ComposedRelation>>;
460
+ messages?: import('./kernel/i18n.ts').Messages;
461
+ tokens: Record<string, string>;
462
+ /** Static file directories, per module, behind /_ket/asset/<module>/. */
463
+ assets: Record<string, string>;
464
+ /** Stylesheets in dependency order. A disabled module's are dropped by restrictManifest. */
465
+ styles: Array<{
466
+ by: string;
467
+ href: string;
468
+ }>;
469
+ /** Path -> the module that owns it and the factory that builds its handler. */
470
+ routes: Record<string, {
471
+ by: string;
472
+ anonymous: boolean;
473
+ make: (ctx: import('./server/boot.ts').ServeContext) => import('./server/boot.ts').Route;
474
+ }>;
475
+ patches: Array<{
476
+ by: string;
477
+ target: string;
478
+ reason: string;
479
+ }>;
480
+ /** Set by restrictManifest: modules this deployment ships but this database has off. */
481
+ disabledModules?: string[];
482
+ disabledSections?: string[];
483
+ disabledIslands?: string[];
484
+ diagnostics?: Diagnostic[];
485
+ };
486
+ export type Diagnostic = {
487
+ code: string;
488
+ message: string;
489
+ module?: string | null;
490
+ hint?: string | null;
491
+ at?: string | null;
492
+ };
493
+ export type WriteRecord = {
494
+ op: 'insert' | 'update';
495
+ model: string;
496
+ row?: Row;
497
+ where?: Row;
498
+ patch?: Row;
499
+ };
500
+ export type Row = Record<string, unknown>;
501
+ export type WriteResult = {
502
+ changes: number;
503
+ };
504
+ export type InsertIfAbsentResult = WriteResult & {
505
+ inserted: boolean;
506
+ };
507
+ export type CompareAndSetResult = WriteResult & {
508
+ matched: boolean;
509
+ };
510
+ /** Which company, and which of its branches, this request is acting within. */
511
+ export type Scope = {
512
+ /** The company a new row is stamped with. Writes are always to exactly one. */
513
+ company: string | null;
514
+ /**
515
+ * Companies this request may READ. Absent means just `company` — the safe
516
+ * default, because widening what you can see should take saying so.
517
+ *
518
+ * Reads use the set and writes use `company`, which is Odoo's split between
519
+ * allowed_company_ids and company_id, and the split is right: a report may span
520
+ * three legal entities, but an invoice belongs to exactly one.
521
+ */
522
+ companies?: string[] | null;
523
+ /** The branch a new company+branch row is stamped with. */
524
+ branch?: string | null;
525
+ /** Branches this request may read. Null means every branch of its companies. */
526
+ branches?: string[] | null;
527
+ };
528
+ export type Ctx = {
529
+ fnKey: string;
530
+ scope: Scope;
531
+ /** The composed manifest, so a module can check data against what is installed. */
532
+ manifest: Manifest;
533
+ actor: string | null;
534
+ dryRun: boolean;
535
+ effects: string[];
536
+ writes: WriteRecord[];
537
+ jobs: {
538
+ enqueue(name: string, args: Record<string, unknown>, options?: JobEnqueueOptions): Promise<JobEnqueueResult>;
539
+ };
540
+ /** Column handles for a model, for building queries. */
541
+ table(model: string): import('./data/query.ts').Table;
542
+ /** A changeset bound to this app's manifest. */
543
+ change(model: string, params: Row, base?: Row | null): import('./data/changeset.ts').Changeset;
544
+ /** Run several writes atomically. Stock reservation is unsafe without it. */
545
+ tx<T>(fn: (ctx: Ctx) => Promise<T>): Promise<T>;
546
+ db: {
547
+ all(q: import('./data/query.ts').Query): Promise<Row[]>;
548
+ one(q: import('./data/query.ts').Query): Promise<Row | null>;
549
+ count(q: import('./data/query.ts').Query): Promise<number>;
550
+ group(q: import('./data/query.ts').Query): Promise<import('./data/query.ts').GroupRow[]>;
551
+ del(q: import('./data/query.ts').Query): Promise<{
552
+ changes: number;
553
+ }>;
554
+ /** Write a changeset. An invalid one is refused with its structured errors. */
555
+ commit(cs: import('./data/changeset.ts').Changeset, where?: Row): Promise<{
556
+ changes: number;
557
+ } | {
558
+ dryRun: true;
559
+ }>;
560
+ select(model: string, where?: Row): Promise<Row[]>;
561
+ insert(model: string, row: Row): Promise<WriteResult | {
562
+ dryRun: true;
563
+ }>;
564
+ /** Insert atomically, returning inserted=false when any declared unique constraint wins the race. */
565
+ insertIfAbsent(model: string, row: Row): Promise<InsertIfAbsentResult | {
566
+ dryRun: true;
567
+ }>;
568
+ update(model: string, where: Row, patch: Row): Promise<WriteResult | {
569
+ dryRun: true;
570
+ }>;
571
+ /** Update only when both identity (`where`) and the expected old values still match. */
572
+ compareAndSet(model: string, where: Row, expected: Row, patch: Row): Promise<CompareAndSetResult | {
573
+ dryRun: true;
574
+ }>;
575
+ };
576
+ };
577
+ export type JobContext = Ctx & {
578
+ job: JobExecution;
579
+ signal: AbortSignal;
580
+ storage: import('./server/storage/types.ts').Storage;
581
+ transport: import('./server/transport/types.ts').OutboundTransport;
582
+ };
583
+ export type Adapter = {
584
+ name: string;
585
+ /** True when this adapter is already bound to an open transaction. */
586
+ readonly transaction?: boolean;
587
+ open(): Promise<void>;
588
+ close(): Promise<void>;
589
+ exec(sql: string): Promise<void>;
590
+ all(sql: string, params?: unknown[]): Promise<Row[]>;
591
+ run(sql: string, params?: unknown[]): Promise<{
592
+ changes: number;
593
+ }>;
594
+ /**
595
+ * The callback receives an adapter scoped to the transaction. Without it a
596
+ * network driver would run the body on a different pooled connection than the
597
+ * BEGIN — a bug SQLite could never have shown us.
598
+ */
599
+ tx<T>(fn: (tx: Adapter) => Promise<T>): Promise<T>;
600
+ quoteIdent(name: string): string;
601
+ columnSql(c: {
602
+ base: FieldBase;
603
+ }): string;
604
+ introspect(): Promise<Record<string, Record<string, string>>>;
605
+ /** Optional because SQLite and third-party adapters may rely on polling. */
606
+ notifications?: DatabaseNotifications;
607
+ };
608
+ export type DatabaseNotifications = {
609
+ /** Publish on this adapter's current connection, and therefore its transaction. */
610
+ publish(channel: string, payload: string): Promise<void>;
611
+ /** Root adapters may keep a dedicated listener connection. */
612
+ subscribe?(channel: string, onMessage: (payload: string) => void, onReady: () => void): Promise<() => Promise<void>>;
613
+ };
614
+ //# sourceMappingURL=types.d.ts.map