@aotter/mantle 0.2.0-alpha.1 → 0.2.0-alpha.10

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 (930) hide show
  1. package/README.md +180 -45
  2. package/dist/auth/adapter.d.ts +32 -0
  3. package/dist/auth/adapter.js +288 -0
  4. package/dist/auth/admin.d.ts +3 -0
  5. package/dist/auth/admin.js +21 -0
  6. package/dist/auth/caller.d.ts +86 -0
  7. package/dist/auth/caller.js +155 -0
  8. package/dist/auth/hardening.d.ts +24 -0
  9. package/dist/auth/hardening.js +40 -0
  10. package/dist/auth/identity.d.ts +121 -0
  11. package/dist/auth/identity.js +203 -0
  12. package/dist/auth/oauth.d.ts +63 -0
  13. package/dist/auth/oauth.js +217 -0
  14. package/dist/auth/resource-seeds.js +10 -0
  15. package/dist/auth/routes.d.ts +32 -0
  16. package/dist/auth/routes.js +226 -0
  17. package/dist/build/compiler.d.ts +721 -0
  18. package/dist/build/compiler.js +7029 -0
  19. package/dist/{spec → build/spec}/domain/model/ManifestGrammar.d.ts +30 -43
  20. package/dist/{spec → build/spec}/domain/model/RuntimePlan.d.ts +77 -3
  21. package/dist/{spec → build/spec}/domain/model/SqlIr.d.ts +39 -6
  22. package/dist/build/spec/domain/model/UserFields.d.ts +21 -0
  23. package/dist/{spec → build/spec}/domain/service/PlanFingerprint.d.ts +4 -1
  24. package/dist/{spec → build/spec}/kernel/diagnostic.d.ts +1 -30
  25. package/dist/bun/assets.d.ts +2 -0
  26. package/dist/bun/assets.js +24 -0
  27. package/dist/domain/adminOrder.js +24 -0
  28. package/dist/domain/columns.js +57 -0
  29. package/dist/domain/describe/chunk-ZVXBGP7Y.js +424 -0
  30. package/dist/domain/describe/index.d.ts +58 -0
  31. package/dist/domain/describe/index.js +115 -0
  32. package/dist/domain/describe/target-hash.d.ts +8 -0
  33. package/dist/domain/describe/target-hash.js +12 -0
  34. package/dist/domain/describe/targets.d.ts +40 -0
  35. package/dist/domain/entry.js +33 -0
  36. package/dist/domain/generated-tools.js +19 -0
  37. package/dist/domain/generation.d.ts +16 -0
  38. package/dist/domain/generation.js +1 -0
  39. package/dist/domain/input.js +17 -0
  40. package/dist/domain/model.js +1 -0
  41. package/dist/domain/paging.js +230 -0
  42. package/dist/domain/recipes.js +24 -0
  43. package/dist/domain/userReferences.js +5 -0
  44. package/dist/domain/wireScalars.js +47 -0
  45. package/dist/infrastructure/adminEntries.js +153 -0
  46. package/dist/infrastructure/adminPaging.js +72 -0
  47. package/dist/infrastructure/artifacts.js +71 -0
  48. package/dist/infrastructure/authSchema.js +83 -0
  49. package/dist/infrastructure/bundle.js +188 -0
  50. package/dist/infrastructure/cli.js +118 -0
  51. package/dist/infrastructure/generate.d.ts +12 -0
  52. package/dist/infrastructure/generate.js +189 -0
  53. package/dist/infrastructure/generateAuth.d.ts +8 -0
  54. package/dist/infrastructure/generateAuth.js +74 -0
  55. package/dist/infrastructure/help.js +60 -0
  56. package/dist/infrastructure/init.js +54 -0
  57. package/dist/infrastructure/inputs.js +41 -0
  58. package/dist/infrastructure/mounts.js +25 -0
  59. package/dist/infrastructure/wrangler.js +109 -0
  60. package/dist/kernel/authorization.d.ts +4 -0
  61. package/dist/kernel/authorization.js +16 -0
  62. package/dist/kernel/binds.d.ts +60 -0
  63. package/dist/kernel/binds.js +49 -0
  64. package/dist/kernel/caller-identity.d.ts +24 -0
  65. package/dist/kernel/caller-identity.js +22 -0
  66. package/dist/kernel/contract-error.d.ts +12 -0
  67. package/dist/kernel/contract-error.js +14 -0
  68. package/dist/kernel/contracts.d.ts +143 -0
  69. package/dist/kernel/contracts.js +97 -0
  70. package/dist/{spec/domain/service/CloudflareCron.d.ts → kernel/cron.d.ts} +0 -1
  71. package/dist/{spec/domain/service/CloudflareCron.js → kernel/cron.js} +1 -6
  72. package/dist/kernel/dialect/common.d.ts +38 -0
  73. package/dist/kernel/dialect/common.js +6 -0
  74. package/dist/kernel/dialect/postgres.d.ts +16 -0
  75. package/dist/kernel/dialect/postgres.js +18 -0
  76. package/dist/kernel/dialect/sqlite.d.ts +20 -0
  77. package/dist/kernel/dialect/sqlite.js +34 -0
  78. package/dist/kernel/drain.d.ts +5 -0
  79. package/dist/kernel/drain.js +35 -0
  80. package/dist/kernel/driver/bun-sqlite.d.ts +19 -0
  81. package/dist/kernel/driver/bun-sqlite.js +77 -0
  82. package/dist/kernel/driver/d1.d.ts +24 -0
  83. package/dist/kernel/driver/d1.js +22 -0
  84. package/dist/kernel/driver/postgres.d.ts +25 -0
  85. package/dist/kernel/driver/postgres.js +99 -0
  86. package/dist/kernel/driver/transaction-context.d.ts +10 -0
  87. package/dist/kernel/driver/transaction-context.js +29 -0
  88. package/dist/kernel/email.d.ts +28 -0
  89. package/dist/{auth/emailTemplates.js → kernel/email.js} +13 -2
  90. package/dist/kernel/host-access.d.ts +10 -0
  91. package/dist/kernel/host-access.js +65 -0
  92. package/dist/kernel/host-caller.d.ts +39 -0
  93. package/dist/kernel/host-caller.js +56 -0
  94. package/dist/kernel/json-body.d.ts +4 -0
  95. package/dist/kernel/json-body.js +44 -0
  96. package/dist/kernel/mcp-apps.d.ts +69 -0
  97. package/dist/kernel/mcp-apps.js +40 -0
  98. package/dist/kernel/mcp-call.d.ts +5 -0
  99. package/dist/kernel/mcp-call.js +25 -0
  100. package/dist/kernel/mcp-handler.js +371 -0
  101. package/dist/kernel/mcp.d.ts +110 -0
  102. package/dist/kernel/mcp.js +50 -0
  103. package/dist/kernel/media.d.ts +27 -0
  104. package/dist/kernel/media.js +216 -0
  105. package/dist/kernel/paging.d.ts +55 -0
  106. package/dist/kernel/paging.js +95 -0
  107. package/dist/kernel/publishing.d.ts +4 -0
  108. package/dist/kernel/publishing.js +16 -0
  109. package/dist/{cloudflare/r2Media.d.ts → kernel/r2-media.d.ts} +4 -5
  110. package/dist/kernel/r2-media.js +78 -0
  111. package/dist/kernel/rest-dispatch.js +38 -0
  112. package/dist/kernel/rest-router.js +64 -0
  113. package/dist/kernel/rest.d.ts +58 -0
  114. package/dist/kernel/rest.js +41 -0
  115. package/dist/kernel/scalars.d.ts +6 -0
  116. package/dist/kernel/scalars.js +44 -0
  117. package/dist/kernel/search.js +5 -0
  118. package/dist/{core/site.d.ts → kernel/site-types.d.ts} +38 -9
  119. package/dist/kernel/site-types.js +1 -0
  120. package/dist/kernel/site.d.ts +39 -0
  121. package/dist/kernel/site.js +32 -0
  122. package/dist/kernel/store.d.ts +175 -0
  123. package/dist/kernel/store.js +269 -0
  124. package/dist/kernel/surface.d.ts +10 -0
  125. package/dist/kernel/surface.js +19 -0
  126. package/dist/kernel/sweep.d.ts +17 -0
  127. package/dist/kernel/sweep.js +27 -0
  128. package/dist/kernel/translations.d.ts +1 -0
  129. package/dist/kernel/translations.js +13 -0
  130. package/dist/kernel/user-caller.d.ts +31 -0
  131. package/dist/kernel/user-caller.js +67 -0
  132. package/dist/kernel/user-field-name.js +3 -0
  133. package/dist/kernel/user-fields.d.ts +35 -0
  134. package/dist/kernel/user-fields.js +81 -0
  135. package/dist/kernel/validation.d.ts +15 -0
  136. package/dist/kernel/validation.js +54 -0
  137. package/dist/kernel/web-gateway.js +68 -0
  138. package/dist/kernel/web-head.js +15 -0
  139. package/dist/kernel/web-index.js +40 -0
  140. package/dist/kernel/web-response.js +8 -0
  141. package/dist/kernel/web.d.ts +77 -0
  142. package/dist/kernel/web.js +124 -0
  143. package/dist/kernel/where.js +27 -0
  144. package/dist/testing/index.d.ts +1 -37
  145. package/dist/testing/index.js +1 -43
  146. package/dist/usecase/adminBindings.js +143 -0
  147. package/dist/usecase/adminClient.js +87 -0
  148. package/dist/usecase/adminDescriptors.js +107 -0
  149. package/dist/usecase/generate.js +166 -0
  150. package/dist/usecase/handlers.js +21 -0
  151. package/dist/usecase/mcp-media.js +17 -0
  152. package/dist/usecase/mcp.js +100 -0
  153. package/dist/usecase/paging.js +71 -0
  154. package/dist/usecase/publishing.js +63 -0
  155. package/dist/usecase/refusals.js +27 -0
  156. package/dist/usecase/rest.js +54 -0
  157. package/dist/usecase/search.js +7 -0
  158. package/dist/usecase/site.js +39 -0
  159. package/dist/usecase/stores.js +64 -0
  160. package/dist/usecase/tool-version.js +9 -0
  161. package/dist/usecase/translations.js +78 -0
  162. package/dist/usecase/ttl.js +20 -0
  163. package/dist/usecase/validation.js +241 -0
  164. package/dist/usecase/web.js +23 -0
  165. package/docs/examples/README.md +39 -16
  166. package/docs/examples/app-shell/README.md +121 -0
  167. package/docs/examples/app-shell/accept.mjs +91 -0
  168. package/docs/examples/app-shell/gitignore +7 -0
  169. package/docs/examples/app-shell/package.json +43 -0
  170. package/docs/examples/app-shell/pnpm-workspace.yaml +8 -0
  171. package/docs/examples/app-shell/smoke-kit.mjs +168 -0
  172. package/docs/examples/app-shell/src/auth.ts +58 -0
  173. package/docs/examples/app-shell/src/handlers.ts +5 -0
  174. package/docs/examples/app-shell/src/index.ts +4 -0
  175. package/docs/examples/app-shell/src/routes.ts +14 -0
  176. package/docs/examples/app-shell/src/schedule.ts +8 -0
  177. package/docs/examples/app-shell/src/service.ts +70 -0
  178. package/docs/examples/app-shell/src/sign-in.ts +41 -0
  179. package/docs/examples/app-shell/src/signup.ts +8 -0
  180. package/docs/examples/{reference-service → app-shell}/tsconfig.json +6 -2
  181. package/docs/examples/app-shell/worker-configuration.d.ts +22 -0
  182. package/docs/examples/app-shell/wrangler.jsonc +62 -0
  183. package/docs/examples/club-events/README.md +65 -0
  184. package/docs/examples/club-events/manifests/club-events.yaml +247 -0
  185. package/docs/examples/club-events/smoke.mjs +127 -0
  186. package/docs/examples/club-events/src/handlers.ts +25 -0
  187. package/docs/examples/club-events/src/signup.ts +9 -0
  188. package/docs/examples/generated-web/README.md +78 -0
  189. package/docs/examples/generated-web/posts.yaml +38 -0
  190. package/docs/examples/generated-web/seed.sql +6 -0
  191. package/docs/examples/generated-web/src/worker.ts +43 -0
  192. package/docs/examples/generated-web/tsconfig.json +8 -0
  193. package/docs/examples/generated-web/wrangler.jsonc +8 -0
  194. package/docs/examples/purchasing/.dev.vars.example +6 -0
  195. package/docs/examples/purchasing/README.md +99 -0
  196. package/docs/examples/purchasing/manifests/purchasing.yaml +374 -0
  197. package/docs/examples/purchasing/seed.sql +4 -0
  198. package/docs/examples/purchasing/smoke.mjs +191 -0
  199. package/docs/examples/purchasing/src/admin.tsx +13 -0
  200. package/docs/examples/purchasing/src/handlers.ts +33 -0
  201. package/docs/examples/purchasing/src/notify.ts +29 -0
  202. package/docs/examples/purchasing/src/requests-page.ts +57 -0
  203. package/docs/examples/purchasing/src/routes.ts +53 -0
  204. package/docs/examples/purchasing/src/schedule.ts +31 -0
  205. package/docs/examples/purchasing/src/signup.ts +5 -0
  206. package/docs/examples/purchasing/src/supplier.ts +32 -0
  207. package/docs/examples/purchasing/wrangler.jsonc +69 -0
  208. package/docs/examples/recipes/README.md +60 -0
  209. package/docs/examples/recipes/manifests/recipes.yaml +75 -0
  210. package/docs/examples/recipes/smoke.mjs +102 -0
  211. package/docs/examples/recipes/src/media.ts +13 -0
  212. package/docs/examples/recipes/src/pages.ts +99 -0
  213. package/docs/examples/recipes/src/routes.ts +71 -0
  214. package/docs/examples/recipes/src/site-config.ts +12 -0
  215. package/docs/examples/recipes/wrangler.jsonc +74 -0
  216. package/docs/handbook/cloudflare/authentication.md +186 -108
  217. package/docs/handbook/cloudflare/chatgpt-sites.md +5 -112
  218. package/docs/handbook/cloudflare/deploy-and-operate.md +59 -87
  219. package/docs/handbook/cloudflare/media-r2.md +61 -82
  220. package/docs/handbook/cloudflare/service-entry.md +220 -119
  221. package/docs/handbook/concepts/authorization.md +10 -117
  222. package/docs/handbook/concepts/four-atoms.md +9 -92
  223. package/docs/handbook/concepts/lifecycle-and-locales.md +9 -80
  224. package/docs/handbook/concepts/mcp-and-agents.md +24 -116
  225. package/docs/handbook/concepts/procedures-and-triggers.md +5 -162
  226. package/docs/handbook/concepts/runtime-and-adapters.md +15 -183
  227. package/docs/handbook/concepts/views.md +10 -109
  228. package/docs/handbook/examples/hub.md +2 -18
  229. package/docs/handbook/guides/adapter-guide.md +11 -0
  230. package/docs/handbook/guides/admin-ui.md +113 -136
  231. package/docs/handbook/guides/agent-setup.md +32 -27
  232. package/docs/handbook/guides/agent-workflow-acceptance.md +232 -0
  233. package/docs/handbook/guides/email.md +86 -0
  234. package/docs/handbook/guides/local-testing.md +40 -0
  235. package/docs/handbook/guides/mcp-apps.md +560 -0
  236. package/docs/handbook/guides/native-admin.md +566 -0
  237. package/docs/handbook/guides/native-execution.md +133 -0
  238. package/docs/handbook/guides/oauth.md +219 -0
  239. package/docs/handbook/guides/postgres-adapters.md +56 -0
  240. package/docs/handbook/guides/public-pages.md +87 -0
  241. package/docs/handbook/guides/size-and-startup.md +9 -0
  242. package/docs/handbook/guides/sql-patterns.md +266 -0
  243. package/docs/handbook/guides/typed-queries.md +210 -100
  244. package/docs/handbook/navigation.json +176 -30
  245. package/docs/handbook/reference/admin-descriptors.md +53 -0
  246. package/docs/handbook/reference/authorization.md +44 -52
  247. package/docs/handbook/reference/describe.md +56 -0
  248. package/docs/handbook/reference/diagnostics.md +19 -77
  249. package/docs/handbook/reference/features.md +45 -53
  250. package/docs/handbook/reference/manifest.md +6 -69
  251. package/docs/handbook/reference/procedure.md +11 -115
  252. package/docs/handbook/reference/schema.md +81 -172
  253. package/docs/handbook/reference/site-config.md +5 -65
  254. package/docs/handbook/reference/sql.md +64 -0
  255. package/docs/handbook/reference/surface.md +396 -93
  256. package/docs/handbook/reference/trigger.md +62 -89
  257. package/docs/handbook/reference/view.md +40 -158
  258. package/docs/handbook/releases/index.md +198 -12
  259. package/docs/handbook/start/agents.md +45 -0
  260. package/docs/handbook/start/overview.md +9 -56
  261. package/docs/handbook/start/project-and-cli.md +51 -131
  262. package/docs/handbook/start/quickstart-worker.md +38 -115
  263. package/docs/skills/develop/SKILL.md +77 -121
  264. package/docs/skills/integrate/SKILL.md +37 -84
  265. package/docs/skills/media-gc/SKILL.md +21 -8
  266. package/docs/skills/provision/SKILL.md +31 -87
  267. package/docs/skills/update/SKILL.md +20 -34
  268. package/docs/source/scripts/postgres-adapter.ts +101 -0
  269. package/docs/upgrade-0.1-to-0.2.md +26 -10
  270. package/package.json +120 -112
  271. package/skills/README.md +67 -0
  272. package/skills/mantle/SKILL.md +76 -0
  273. package/dist/admin/consent.d.ts +0 -17
  274. package/dist/admin/consent.d.ts.map +0 -1
  275. package/dist/admin/consent.js +0 -17
  276. package/dist/admin/consent.js.map +0 -1
  277. package/dist/admin/createAdminSurface.d.ts +0 -22
  278. package/dist/admin/createAdminSurface.d.ts.map +0 -1
  279. package/dist/admin/createAdminSurface.js +0 -589
  280. package/dist/admin/createAdminSurface.js.map +0 -1
  281. package/dist/admin/developerConsole.d.ts +0 -264
  282. package/dist/admin/developerConsole.d.ts.map +0 -1
  283. package/dist/admin/developerConsole.js +0 -136
  284. package/dist/admin/developerConsole.js.map +0 -1
  285. package/dist/admin/identity.d.ts +0 -62
  286. package/dist/admin/identity.d.ts.map +0 -1
  287. package/dist/admin/identity.js +0 -7
  288. package/dist/admin/identity.js.map +0 -1
  289. package/dist/admin/index.d.ts +0 -4
  290. package/dist/admin/index.d.ts.map +0 -1
  291. package/dist/admin/index.js +0 -4
  292. package/dist/admin/index.js.map +0 -1
  293. package/dist/admin/procedureFlow.d.ts +0 -25
  294. package/dist/admin/procedureFlow.d.ts.map +0 -1
  295. package/dist/admin/procedureFlow.js +0 -154
  296. package/dist/admin/procedureFlow.js.map +0 -1
  297. package/dist/auth/ConsoleEmailSender.d.ts +0 -15
  298. package/dist/auth/ConsoleEmailSender.d.ts.map +0 -1
  299. package/dist/auth/ConsoleEmailSender.js +0 -19
  300. package/dist/auth/ConsoleEmailSender.js.map +0 -1
  301. package/dist/auth/appleClientSecret.d.ts +0 -75
  302. package/dist/auth/appleClientSecret.d.ts.map +0 -1
  303. package/dist/auth/appleClientSecret.js +0 -116
  304. package/dist/auth/appleClientSecret.js.map +0 -1
  305. package/dist/auth/buildAuth.d.ts +0 -4565
  306. package/dist/auth/buildAuth.d.ts.map +0 -1
  307. package/dist/auth/buildAuth.js +0 -307
  308. package/dist/auth/buildAuth.js.map +0 -1
  309. package/dist/auth/callerResolver.d.ts +0 -48
  310. package/dist/auth/callerResolver.d.ts.map +0 -1
  311. package/dist/auth/callerResolver.js +0 -49
  312. package/dist/auth/callerResolver.js.map +0 -1
  313. package/dist/auth/createAuthRoutes.d.ts +0 -22
  314. package/dist/auth/createAuthRoutes.d.ts.map +0 -1
  315. package/dist/auth/createAuthRoutes.js +0 -128
  316. package/dist/auth/createAuthRoutes.js.map +0 -1
  317. package/dist/auth/createMantleAuth.d.ts +0 -3
  318. package/dist/auth/createMantleAuth.d.ts.map +0 -1
  319. package/dist/auth/createMantleAuth.js +0 -483
  320. package/dist/auth/createMantleAuth.js.map +0 -1
  321. package/dist/auth/db.d.ts +0 -17
  322. package/dist/auth/db.d.ts.map +0 -1
  323. package/dist/auth/db.js +0 -24
  324. package/dist/auth/db.js.map +0 -1
  325. package/dist/auth/emailTemplates.d.ts +0 -7
  326. package/dist/auth/emailTemplates.d.ts.map +0 -1
  327. package/dist/auth/emailTemplates.js.map +0 -1
  328. package/dist/auth/index.d.ts +0 -9
  329. package/dist/auth/index.d.ts.map +0 -1
  330. package/dist/auth/index.js +0 -7
  331. package/dist/auth/index.js.map +0 -1
  332. package/dist/auth/methods.d.ts +0 -717
  333. package/dist/auth/methods.d.ts.map +0 -1
  334. package/dist/auth/methods.js +0 -321
  335. package/dist/auth/methods.js.map +0 -1
  336. package/dist/auth/oauthTokens.d.ts +0 -24
  337. package/dist/auth/oauthTokens.d.ts.map +0 -1
  338. package/dist/auth/oauthTokens.js +0 -146
  339. package/dist/auth/oauthTokens.js.map +0 -1
  340. package/dist/auth/setupIncomplete.d.ts +0 -16
  341. package/dist/auth/setupIncomplete.d.ts.map +0 -1
  342. package/dist/auth/setupIncomplete.js +0 -50
  343. package/dist/auth/setupIncomplete.js.map +0 -1
  344. package/dist/auth/types.d.ts +0 -440
  345. package/dist/auth/types.d.ts.map +0 -1
  346. package/dist/auth/types.js +0 -10
  347. package/dist/auth/types.js.map +0 -1
  348. package/dist/bun/index.d.ts +0 -65
  349. package/dist/bun/index.d.ts.map +0 -1
  350. package/dist/bun/index.js +0 -130
  351. package/dist/bun/index.js.map +0 -1
  352. package/dist/cli/emitModule.d.ts +0 -7
  353. package/dist/cli/emitModule.d.ts.map +0 -1
  354. package/dist/cli/emitModule.js +0 -127
  355. package/dist/cli/emitModule.js.map +0 -1
  356. package/dist/cli/generate.d.ts +0 -8
  357. package/dist/cli/generate.d.ts.map +0 -1
  358. package/dist/cli/generate.js +0 -355
  359. package/dist/cli/generate.js.map +0 -1
  360. package/dist/cli/main.d.ts +0 -3
  361. package/dist/cli/main.d.ts.map +0 -1
  362. package/dist/cli/main.js +0 -21
  363. package/dist/cli/main.js.map +0 -1
  364. package/dist/cli/preset.d.ts +0 -18
  365. package/dist/cli/preset.d.ts.map +0 -1
  366. package/dist/cli/preset.js +0 -352
  367. package/dist/cli/preset.js.map +0 -1
  368. package/dist/cloudflare/index.d.ts +0 -21
  369. package/dist/cloudflare/index.d.ts.map +0 -1
  370. package/dist/cloudflare/index.js +0 -14
  371. package/dist/cloudflare/index.js.map +0 -1
  372. package/dist/cloudflare/r2Media.d.ts.map +0 -1
  373. package/dist/cloudflare/r2Media.js +0 -69
  374. package/dist/cloudflare/r2Media.js.map +0 -1
  375. package/dist/core/caller.d.ts +0 -35
  376. package/dist/core/caller.d.ts.map +0 -1
  377. package/dist/core/caller.js +0 -3
  378. package/dist/core/caller.js.map +0 -1
  379. package/dist/core/createMantle.d.ts +0 -22
  380. package/dist/core/createMantle.d.ts.map +0 -1
  381. package/dist/core/createMantle.js +0 -117
  382. package/dist/core/createMantle.js.map +0 -1
  383. package/dist/core/dialect.d.ts +0 -41
  384. package/dist/core/dialect.d.ts.map +0 -1
  385. package/dist/core/dialect.js +0 -7
  386. package/dist/core/dialect.js.map +0 -1
  387. package/dist/core/driver.d.ts +0 -17
  388. package/dist/core/driver.d.ts.map +0 -1
  389. package/dist/core/driver.js +0 -2
  390. package/dist/core/driver.js.map +0 -1
  391. package/dist/core/email.d.ts +0 -17
  392. package/dist/core/email.d.ts.map +0 -1
  393. package/dist/core/email.js +0 -2
  394. package/dist/core/email.js.map +0 -1
  395. package/dist/core/index.d.ts +0 -16
  396. package/dist/core/index.d.ts.map +0 -1
  397. package/dist/core/index.js +0 -16
  398. package/dist/core/index.js.map +0 -1
  399. package/dist/core/invocation.d.ts +0 -66
  400. package/dist/core/invocation.d.ts.map +0 -1
  401. package/dist/core/invocation.js +0 -3
  402. package/dist/core/invocation.js.map +0 -1
  403. package/dist/core/runtime/auth.d.ts +0 -9
  404. package/dist/core/runtime/auth.d.ts.map +0 -1
  405. package/dist/core/runtime/auth.js +0 -39
  406. package/dist/core/runtime/auth.js.map +0 -1
  407. package/dist/core/runtime/createRuntime.d.ts +0 -23
  408. package/dist/core/runtime/createRuntime.d.ts.map +0 -1
  409. package/dist/core/runtime/createRuntime.js +0 -214
  410. package/dist/core/runtime/createRuntime.js.map +0 -1
  411. package/dist/core/runtime/verifyPlan.d.ts +0 -34
  412. package/dist/core/runtime/verifyPlan.d.ts.map +0 -1
  413. package/dist/core/runtime/verifyPlan.js +0 -301
  414. package/dist/core/runtime/verifyPlan.js.map +0 -1
  415. package/dist/core/service.d.ts +0 -45
  416. package/dist/core/service.d.ts.map +0 -1
  417. package/dist/core/service.js +0 -2
  418. package/dist/core/service.js.map +0 -1
  419. package/dist/core/site.d.ts.map +0 -1
  420. package/dist/core/site.js +0 -2
  421. package/dist/core/site.js.map +0 -1
  422. package/dist/core/siteConfig.d.ts +0 -5
  423. package/dist/core/siteConfig.d.ts.map +0 -1
  424. package/dist/core/siteConfig.js +0 -24
  425. package/dist/core/siteConfig.js.map +0 -1
  426. package/dist/core/sql/allowlist.d.ts +0 -65
  427. package/dist/core/sql/allowlist.d.ts.map +0 -1
  428. package/dist/core/sql/allowlist.js +0 -705
  429. package/dist/core/sql/allowlist.js.map +0 -1
  430. package/dist/core/sql/ast.d.ts +0 -24
  431. package/dist/core/sql/ast.d.ts.map +0 -1
  432. package/dist/core/sql/ast.js +0 -32
  433. package/dist/core/sql/ast.js.map +0 -1
  434. package/dist/core/sql/compile.d.ts +0 -50
  435. package/dist/core/sql/compile.d.ts.map +0 -1
  436. package/dist/core/sql/compile.js +0 -48
  437. package/dist/core/sql/compile.js.map +0 -1
  438. package/dist/core/sql/policy.d.ts +0 -121
  439. package/dist/core/sql/policy.d.ts.map +0 -1
  440. package/dist/core/sql/policy.js +0 -379
  441. package/dist/core/sql/policy.js.map +0 -1
  442. package/dist/core/sql/positions.d.ts +0 -30
  443. package/dist/core/sql/positions.d.ts.map +0 -1
  444. package/dist/core/sql/positions.js +0 -22
  445. package/dist/core/sql/positions.js.map +0 -1
  446. package/dist/core/sql/run.d.ts +0 -76
  447. package/dist/core/sql/run.d.ts.map +0 -1
  448. package/dist/core/sql/run.js +0 -267
  449. package/dist/core/sql/run.js.map +0 -1
  450. package/dist/core/store/createStore.d.ts +0 -49
  451. package/dist/core/store/createStore.d.ts.map +0 -1
  452. package/dist/core/store/createStore.js +0 -227
  453. package/dist/core/store/createStore.js.map +0 -1
  454. package/dist/core/store/cursor.d.ts +0 -4
  455. package/dist/core/store/cursor.d.ts.map +0 -1
  456. package/dist/core/store/cursor.js +0 -22
  457. package/dist/core/store/cursor.js.map +0 -1
  458. package/dist/core/store/json.d.ts +0 -80
  459. package/dist/core/store/json.d.ts.map +0 -1
  460. package/dist/core/store/json.js +0 -342
  461. package/dist/core/store/json.js.map +0 -1
  462. package/dist/core/store.d.ts +0 -160
  463. package/dist/core/store.d.ts.map +0 -1
  464. package/dist/core/store.js +0 -2
  465. package/dist/core/store.js.map +0 -1
  466. package/dist/core/wire.d.ts +0 -20
  467. package/dist/core/wire.d.ts.map +0 -1
  468. package/dist/core/wire.js +0 -84
  469. package/dist/core/wire.js.map +0 -1
  470. package/dist/core/withCaller.d.ts +0 -16
  471. package/dist/core/withCaller.d.ts.map +0 -1
  472. package/dist/core/withCaller.js +0 -29
  473. package/dist/core/withCaller.js.map +0 -1
  474. package/dist/d1/adapter.d.ts +0 -12
  475. package/dist/d1/adapter.d.ts.map +0 -1
  476. package/dist/d1/adapter.js +0 -23
  477. package/dist/d1/adapter.js.map +0 -1
  478. package/dist/d1/codec.d.ts +0 -14
  479. package/dist/d1/codec.d.ts.map +0 -1
  480. package/dist/d1/codec.js +0 -136
  481. package/dist/d1/codec.js.map +0 -1
  482. package/dist/d1/compile/index.d.ts +0 -9
  483. package/dist/d1/compile/index.d.ts.map +0 -1
  484. package/dist/d1/compile/index.js +0 -9
  485. package/dist/d1/compile/index.js.map +0 -1
  486. package/dist/d1/dialect.d.ts +0 -4
  487. package/dist/d1/dialect.d.ts.map +0 -1
  488. package/dist/d1/dialect.js +0 -13
  489. package/dist/d1/dialect.js.map +0 -1
  490. package/dist/d1/executor.d.ts +0 -13
  491. package/dist/d1/executor.d.ts.map +0 -1
  492. package/dist/d1/executor.js +0 -91
  493. package/dist/d1/executor.js.map +0 -1
  494. package/dist/d1/index.d.ts +0 -6
  495. package/dist/d1/index.d.ts.map +0 -1
  496. package/dist/d1/index.js +0 -6
  497. package/dist/d1/index.js.map +0 -1
  498. package/dist/d1/lower.d.ts +0 -8
  499. package/dist/d1/lower.d.ts.map +0 -1
  500. package/dist/d1/lower.js +0 -167
  501. package/dist/d1/lower.js.map +0 -1
  502. package/dist/d1/media.d.ts +0 -5
  503. package/dist/d1/media.d.ts.map +0 -1
  504. package/dist/d1/media.js +0 -157
  505. package/dist/d1/media.js.map +0 -1
  506. package/dist/d1/migrations.d.ts +0 -12
  507. package/dist/d1/migrations.d.ts.map +0 -1
  508. package/dist/d1/migrations.js +0 -111
  509. package/dist/d1/migrations.js.map +0 -1
  510. package/dist/d1/print.d.ts +0 -28
  511. package/dist/d1/print.d.ts.map +0 -1
  512. package/dist/d1/print.js +0 -54
  513. package/dist/d1/print.js.map +0 -1
  514. package/dist/d1/site.d.ts +0 -17
  515. package/dist/d1/site.d.ts.map +0 -1
  516. package/dist/d1/site.js +0 -63
  517. package/dist/d1/site.js.map +0 -1
  518. package/dist/d1/storage.d.ts +0 -34
  519. package/dist/d1/storage.d.ts.map +0 -1
  520. package/dist/d1/storage.js +0 -288
  521. package/dist/d1/storage.js.map +0 -1
  522. package/dist/d1/tz.d.ts +0 -21
  523. package/dist/d1/tz.d.ts.map +0 -1
  524. package/dist/d1/tz.js +0 -54
  525. package/dist/d1/tz.js.map +0 -1
  526. package/dist/d1/validator.d.ts +0 -10
  527. package/dist/d1/validator.d.ts.map +0 -1
  528. package/dist/d1/validator.js +0 -33
  529. package/dist/d1/validator.js.map +0 -1
  530. package/dist/mcp/apps.d.ts +0 -43
  531. package/dist/mcp/apps.d.ts.map +0 -1
  532. package/dist/mcp/apps.js +0 -57
  533. package/dist/mcp/apps.js.map +0 -1
  534. package/dist/mcp/catalog.d.ts +0 -55
  535. package/dist/mcp/catalog.d.ts.map +0 -1
  536. package/dist/mcp/catalog.js +0 -81
  537. package/dist/mcp/catalog.js.map +0 -1
  538. package/dist/mcp/createMcpSurface.d.ts +0 -34
  539. package/dist/mcp/createMcpSurface.d.ts.map +0 -1
  540. package/dist/mcp/createMcpSurface.js +0 -150
  541. package/dist/mcp/createMcpSurface.js.map +0 -1
  542. package/dist/mcp/index.d.ts +0 -4
  543. package/dist/mcp/index.d.ts.map +0 -1
  544. package/dist/mcp/index.js +0 -4
  545. package/dist/mcp/index.js.map +0 -1
  546. package/dist/postgres/adapter.d.ts +0 -20
  547. package/dist/postgres/adapter.d.ts.map +0 -1
  548. package/dist/postgres/adapter.js +0 -44
  549. package/dist/postgres/adapter.js.map +0 -1
  550. package/dist/postgres/codec.d.ts +0 -13
  551. package/dist/postgres/codec.d.ts.map +0 -1
  552. package/dist/postgres/codec.js +0 -110
  553. package/dist/postgres/codec.js.map +0 -1
  554. package/dist/postgres/compile/index.d.ts +0 -9
  555. package/dist/postgres/compile/index.d.ts.map +0 -1
  556. package/dist/postgres/compile/index.js +0 -9
  557. package/dist/postgres/compile/index.js.map +0 -1
  558. package/dist/postgres/driver.d.ts +0 -92
  559. package/dist/postgres/driver.d.ts.map +0 -1
  560. package/dist/postgres/driver.js +0 -110
  561. package/dist/postgres/driver.js.map +0 -1
  562. package/dist/postgres/executor.d.ts +0 -20
  563. package/dist/postgres/executor.d.ts.map +0 -1
  564. package/dist/postgres/executor.js +0 -69
  565. package/dist/postgres/executor.js.map +0 -1
  566. package/dist/postgres/index.d.ts +0 -7
  567. package/dist/postgres/index.d.ts.map +0 -1
  568. package/dist/postgres/index.js +0 -7
  569. package/dist/postgres/index.js.map +0 -1
  570. package/dist/postgres/lower.d.ts +0 -6
  571. package/dist/postgres/lower.d.ts.map +0 -1
  572. package/dist/postgres/lower.js +0 -157
  573. package/dist/postgres/lower.js.map +0 -1
  574. package/dist/postgres/print.d.ts +0 -11
  575. package/dist/postgres/print.d.ts.map +0 -1
  576. package/dist/postgres/print.js +0 -121
  577. package/dist/postgres/print.js.map +0 -1
  578. package/dist/postgres/storage.d.ts +0 -19
  579. package/dist/postgres/storage.d.ts.map +0 -1
  580. package/dist/postgres/storage.js +0 -201
  581. package/dist/postgres/storage.js.map +0 -1
  582. package/dist/postgres/textRows.d.ts +0 -10
  583. package/dist/postgres/textRows.d.ts.map +0 -1
  584. package/dist/postgres/textRows.js +0 -44
  585. package/dist/postgres/textRows.js.map +0 -1
  586. package/dist/postgres/validator.d.ts +0 -12
  587. package/dist/postgres/validator.d.ts.map +0 -1
  588. package/dist/postgres/validator.js +0 -46
  589. package/dist/postgres/validator.js.map +0 -1
  590. package/dist/spec/domain/index.d.ts +0 -3
  591. package/dist/spec/domain/index.d.ts.map +0 -1
  592. package/dist/spec/domain/index.js +0 -3
  593. package/dist/spec/domain/index.js.map +0 -1
  594. package/dist/spec/domain/model/ContentState.d.ts +0 -8
  595. package/dist/spec/domain/model/ContentState.d.ts.map +0 -1
  596. package/dist/spec/domain/model/ContentState.js +0 -7
  597. package/dist/spec/domain/model/ContentState.js.map +0 -1
  598. package/dist/spec/domain/model/Entry.d.ts +0 -29
  599. package/dist/spec/domain/model/Entry.d.ts.map +0 -1
  600. package/dist/spec/domain/model/Entry.js +0 -16
  601. package/dist/spec/domain/model/Entry.js.map +0 -1
  602. package/dist/spec/domain/model/Locale.d.ts +0 -30
  603. package/dist/spec/domain/model/Locale.d.ts.map +0 -1
  604. package/dist/spec/domain/model/Locale.js +0 -33
  605. package/dist/spec/domain/model/Locale.js.map +0 -1
  606. package/dist/spec/domain/model/ManifestGrammar.d.ts.map +0 -1
  607. package/dist/spec/domain/model/ManifestGrammar.js +0 -149
  608. package/dist/spec/domain/model/ManifestGrammar.js.map +0 -1
  609. package/dist/spec/domain/model/MediaMimeAccept.d.ts +0 -55
  610. package/dist/spec/domain/model/MediaMimeAccept.d.ts.map +0 -1
  611. package/dist/spec/domain/model/MediaMimeAccept.js +0 -90
  612. package/dist/spec/domain/model/MediaMimeAccept.js.map +0 -1
  613. package/dist/spec/domain/model/RuntimePlan.d.ts.map +0 -1
  614. package/dist/spec/domain/model/RuntimePlan.js +0 -4
  615. package/dist/spec/domain/model/RuntimePlan.js.map +0 -1
  616. package/dist/spec/domain/model/SiteConfig.d.ts +0 -173
  617. package/dist/spec/domain/model/SiteConfig.d.ts.map +0 -1
  618. package/dist/spec/domain/model/SiteConfig.js +0 -13
  619. package/dist/spec/domain/model/SiteConfig.js.map +0 -1
  620. package/dist/spec/domain/model/SqlIr.d.ts.map +0 -1
  621. package/dist/spec/domain/model/SqlIr.js +0 -95
  622. package/dist/spec/domain/model/SqlIr.js.map +0 -1
  623. package/dist/spec/domain/model/index.d.ts +0 -14
  624. package/dist/spec/domain/model/index.d.ts.map +0 -1
  625. package/dist/spec/domain/model/index.js +0 -14
  626. package/dist/spec/domain/model/index.js.map +0 -1
  627. package/dist/spec/domain/service/CloudflareCron.d.ts.map +0 -1
  628. package/dist/spec/domain/service/CloudflareCron.js.map +0 -1
  629. package/dist/spec/domain/service/CrossSchemaChecker.d.ts +0 -34
  630. package/dist/spec/domain/service/CrossSchemaChecker.d.ts.map +0 -1
  631. package/dist/spec/domain/service/CrossSchemaChecker.js +0 -114
  632. package/dist/spec/domain/service/CrossSchemaChecker.js.map +0 -1
  633. package/dist/spec/domain/service/EntryDataValidator.d.ts +0 -56
  634. package/dist/spec/domain/service/EntryDataValidator.d.ts.map +0 -1
  635. package/dist/spec/domain/service/EntryDataValidator.js +0 -88
  636. package/dist/spec/domain/service/EntryDataValidator.js.map +0 -1
  637. package/dist/spec/domain/service/JsonSchemaToZod.d.ts +0 -29
  638. package/dist/spec/domain/service/JsonSchemaToZod.d.ts.map +0 -1
  639. package/dist/spec/domain/service/JsonSchemaToZod.js +0 -156
  640. package/dist/spec/domain/service/JsonSchemaToZod.js.map +0 -1
  641. package/dist/spec/domain/service/LifecycleStateMachine.d.ts +0 -75
  642. package/dist/spec/domain/service/LifecycleStateMachine.d.ts.map +0 -1
  643. package/dist/spec/domain/service/LifecycleStateMachine.js +0 -76
  644. package/dist/spec/domain/service/LifecycleStateMachine.js.map +0 -1
  645. package/dist/spec/domain/service/LocaleCanonicalizer.d.ts +0 -29
  646. package/dist/spec/domain/service/LocaleCanonicalizer.d.ts.map +0 -1
  647. package/dist/spec/domain/service/LocaleCanonicalizer.js +0 -96
  648. package/dist/spec/domain/service/LocaleCanonicalizer.js.map +0 -1
  649. package/dist/spec/domain/service/ManifestFieldChecks.d.ts +0 -60
  650. package/dist/spec/domain/service/ManifestFieldChecks.d.ts.map +0 -1
  651. package/dist/spec/domain/service/ManifestFieldChecks.js +0 -116
  652. package/dist/spec/domain/service/ManifestFieldChecks.js.map +0 -1
  653. package/dist/spec/domain/service/ManifestGraphValidator.d.ts +0 -15
  654. package/dist/spec/domain/service/ManifestGraphValidator.d.ts.map +0 -1
  655. package/dist/spec/domain/service/ManifestGraphValidator.js +0 -225
  656. package/dist/spec/domain/service/ManifestGraphValidator.js.map +0 -1
  657. package/dist/spec/domain/service/ManifestLinker.d.ts +0 -45
  658. package/dist/spec/domain/service/ManifestLinker.d.ts.map +0 -1
  659. package/dist/spec/domain/service/ManifestLinker.js +0 -92
  660. package/dist/spec/domain/service/ManifestLinker.js.map +0 -1
  661. package/dist/spec/domain/service/ManifestLocaleTrimmer.d.ts +0 -16
  662. package/dist/spec/domain/service/ManifestLocaleTrimmer.d.ts.map +0 -1
  663. package/dist/spec/domain/service/ManifestLocaleTrimmer.js +0 -76
  664. package/dist/spec/domain/service/ManifestLocaleTrimmer.js.map +0 -1
  665. package/dist/spec/domain/service/ManifestParser.d.ts +0 -49
  666. package/dist/spec/domain/service/ManifestParser.d.ts.map +0 -1
  667. package/dist/spec/domain/service/ManifestParser.js +0 -203
  668. package/dist/spec/domain/service/ManifestParser.js.map +0 -1
  669. package/dist/spec/domain/service/ManifestPartition.d.ts +0 -9
  670. package/dist/spec/domain/service/ManifestPartition.d.ts.map +0 -1
  671. package/dist/spec/domain/service/ManifestPartition.js +0 -19
  672. package/dist/spec/domain/service/ManifestPartition.js.map +0 -1
  673. package/dist/spec/domain/service/ManifestPathDiagnoser.d.ts +0 -33
  674. package/dist/spec/domain/service/ManifestPathDiagnoser.d.ts.map +0 -1
  675. package/dist/spec/domain/service/ManifestPathDiagnoser.js +0 -43
  676. package/dist/spec/domain/service/ManifestPathDiagnoser.js.map +0 -1
  677. package/dist/spec/domain/service/ManifestSourceSpans.d.ts +0 -10
  678. package/dist/spec/domain/service/ManifestSourceSpans.d.ts.map +0 -1
  679. package/dist/spec/domain/service/ManifestSourceSpans.js +0 -76
  680. package/dist/spec/domain/service/ManifestSourceSpans.js.map +0 -1
  681. package/dist/spec/domain/service/McpToolNaming.d.ts +0 -25
  682. package/dist/spec/domain/service/McpToolNaming.d.ts.map +0 -1
  683. package/dist/spec/domain/service/McpToolNaming.js +0 -50
  684. package/dist/spec/domain/service/McpToolNaming.js.map +0 -1
  685. package/dist/spec/domain/service/PlanFingerprint.d.ts.map +0 -1
  686. package/dist/spec/domain/service/PlanFingerprint.js +0 -14
  687. package/dist/spec/domain/service/PlanFingerprint.js.map +0 -1
  688. package/dist/spec/domain/service/ProcedureViewSpecChecks.d.ts +0 -4
  689. package/dist/spec/domain/service/ProcedureViewSpecChecks.d.ts.map +0 -1
  690. package/dist/spec/domain/service/ProcedureViewSpecChecks.js +0 -211
  691. package/dist/spec/domain/service/ProcedureViewSpecChecks.js.map +0 -1
  692. package/dist/spec/domain/service/SchemaAdminUiChecker.d.ts +0 -42
  693. package/dist/spec/domain/service/SchemaAdminUiChecker.d.ts.map +0 -1
  694. package/dist/spec/domain/service/SchemaAdminUiChecker.js +0 -289
  695. package/dist/spec/domain/service/SchemaAdminUiChecker.js.map +0 -1
  696. package/dist/spec/domain/service/SchemaIndexChecker.d.ts +0 -33
  697. package/dist/spec/domain/service/SchemaIndexChecker.d.ts.map +0 -1
  698. package/dist/spec/domain/service/SchemaIndexChecker.js +0 -256
  699. package/dist/spec/domain/service/SchemaIndexChecker.js.map +0 -1
  700. package/dist/spec/domain/service/SchemaSearchChecker.d.ts +0 -11
  701. package/dist/spec/domain/service/SchemaSearchChecker.d.ts.map +0 -1
  702. package/dist/spec/domain/service/SchemaSearchChecker.js +0 -78
  703. package/dist/spec/domain/service/SchemaSearchChecker.js.map +0 -1
  704. package/dist/spec/domain/service/SchemaSpecChecks.d.ts +0 -31
  705. package/dist/spec/domain/service/SchemaSpecChecks.d.ts.map +0 -1
  706. package/dist/spec/domain/service/SchemaSpecChecks.js +0 -445
  707. package/dist/spec/domain/service/SchemaSpecChecks.js.map +0 -1
  708. package/dist/spec/domain/service/SiteDefaultsValidator.d.ts +0 -39
  709. package/dist/spec/domain/service/SiteDefaultsValidator.d.ts.map +0 -1
  710. package/dist/spec/domain/service/SiteDefaultsValidator.js +0 -194
  711. package/dist/spec/domain/service/SiteDefaultsValidator.js.map +0 -1
  712. package/dist/spec/domain/service/SqlClassify.d.ts +0 -19
  713. package/dist/spec/domain/service/SqlClassify.d.ts.map +0 -1
  714. package/dist/spec/domain/service/SqlClassify.js +0 -42
  715. package/dist/spec/domain/service/SqlClassify.js.map +0 -1
  716. package/dist/spec/domain/service/SqlRefusal.d.ts +0 -16
  717. package/dist/spec/domain/service/SqlRefusal.d.ts.map +0 -1
  718. package/dist/spec/domain/service/SqlRefusal.js +0 -20
  719. package/dist/spec/domain/service/SqlRefusal.js.map +0 -1
  720. package/dist/spec/domain/service/SqlTypes.d.ts +0 -40
  721. package/dist/spec/domain/service/SqlTypes.d.ts.map +0 -1
  722. package/dist/spec/domain/service/SqlTypes.js +0 -110
  723. package/dist/spec/domain/service/SqlTypes.js.map +0 -1
  724. package/dist/spec/domain/service/StaffRoleHierarchy.d.ts +0 -3
  725. package/dist/spec/domain/service/StaffRoleHierarchy.d.ts.map +0 -1
  726. package/dist/spec/domain/service/StaffRoleHierarchy.js +0 -19
  727. package/dist/spec/domain/service/StaffRoleHierarchy.js.map +0 -1
  728. package/dist/spec/domain/service/TriggerGraphChecks.d.ts +0 -16
  729. package/dist/spec/domain/service/TriggerGraphChecks.d.ts.map +0 -1
  730. package/dist/spec/domain/service/TriggerGraphChecks.js +0 -337
  731. package/dist/spec/domain/service/TriggerGraphChecks.js.map +0 -1
  732. package/dist/spec/domain/service/TriggerSpecChecks.d.ts +0 -4
  733. package/dist/spec/domain/service/TriggerSpecChecks.d.ts.map +0 -1
  734. package/dist/spec/domain/service/TriggerSpecChecks.js +0 -89
  735. package/dist/spec/domain/service/TriggerSpecChecks.js.map +0 -1
  736. package/dist/spec/domain/service/ValidatedAdminUiProjection.d.ts +0 -11
  737. package/dist/spec/domain/service/ValidatedAdminUiProjection.d.ts.map +0 -1
  738. package/dist/spec/domain/service/ValidatedAdminUiProjection.js +0 -26
  739. package/dist/spec/domain/service/ValidatedAdminUiProjection.js.map +0 -1
  740. package/dist/spec/domain/service/ViewAdminUiChecker.d.ts +0 -14
  741. package/dist/spec/domain/service/ViewAdminUiChecker.d.ts.map +0 -1
  742. package/dist/spec/domain/service/ViewAdminUiChecker.js +0 -53
  743. package/dist/spec/domain/service/ViewAdminUiChecker.js.map +0 -1
  744. package/dist/spec/domain/service/index.d.ts +0 -24
  745. package/dist/spec/domain/service/index.d.ts.map +0 -1
  746. package/dist/spec/domain/service/index.js +0 -24
  747. package/dist/spec/domain/service/index.js.map +0 -1
  748. package/dist/spec/index.d.ts +0 -10
  749. package/dist/spec/index.d.ts.map +0 -1
  750. package/dist/spec/index.js +0 -12
  751. package/dist/spec/index.js.map +0 -1
  752. package/dist/spec/infrastructure/cli/parseArgsError.d.ts +0 -16
  753. package/dist/spec/infrastructure/cli/parseArgsError.d.ts.map +0 -1
  754. package/dist/spec/infrastructure/cli/parseArgsError.js +0 -34
  755. package/dist/spec/infrastructure/cli/parseArgsError.js.map +0 -1
  756. package/dist/spec/infrastructure/index.d.ts +0 -2
  757. package/dist/spec/infrastructure/index.d.ts.map +0 -1
  758. package/dist/spec/infrastructure/index.js +0 -2
  759. package/dist/spec/infrastructure/index.js.map +0 -1
  760. package/dist/spec/infrastructure/sql/PgQueryParser.d.ts +0 -18
  761. package/dist/spec/infrastructure/sql/PgQueryParser.d.ts.map +0 -1
  762. package/dist/spec/infrastructure/sql/PgQueryParser.js +0 -17
  763. package/dist/spec/infrastructure/sql/PgQueryParser.js.map +0 -1
  764. package/dist/spec/infrastructure/sql/compilePlan.d.ts +0 -37
  765. package/dist/spec/infrastructure/sql/compilePlan.d.ts.map +0 -1
  766. package/dist/spec/infrastructure/sql/compilePlan.js +0 -282
  767. package/dist/spec/infrastructure/sql/compilePlan.js.map +0 -1
  768. package/dist/spec/infrastructure/sql/compileSql.d.ts +0 -35
  769. package/dist/spec/infrastructure/sql/compileSql.d.ts.map +0 -1
  770. package/dist/spec/infrastructure/sql/compileSql.js +0 -92
  771. package/dist/spec/infrastructure/sql/compileSql.js.map +0 -1
  772. package/dist/spec/infrastructure/sql/frontEnd.d.ts +0 -8
  773. package/dist/spec/infrastructure/sql/frontEnd.d.ts.map +0 -1
  774. package/dist/spec/infrastructure/sql/frontEnd.js +0 -100
  775. package/dist/spec/infrastructure/sql/frontEnd.js.map +0 -1
  776. package/dist/spec/infrastructure/sql/index.d.ts +0 -4
  777. package/dist/spec/infrastructure/sql/index.d.ts.map +0 -1
  778. package/dist/spec/infrastructure/sql/index.js +0 -4
  779. package/dist/spec/infrastructure/sql/index.js.map +0 -1
  780. package/dist/spec/kernel/diagnostic.d.ts.map +0 -1
  781. package/dist/spec/kernel/diagnostic.js +0 -296
  782. package/dist/spec/kernel/diagnostic.js.map +0 -1
  783. package/dist/spec/kernel/index.d.ts +0 -8
  784. package/dist/spec/kernel/index.d.ts.map +0 -1
  785. package/dist/spec/kernel/index.js +0 -8
  786. package/dist/spec/kernel/index.js.map +0 -1
  787. package/dist/spec/usecase/EmitTypes.d.ts +0 -22
  788. package/dist/spec/usecase/EmitTypes.d.ts.map +0 -1
  789. package/dist/spec/usecase/EmitTypes.js +0 -182
  790. package/dist/spec/usecase/EmitTypes.js.map +0 -1
  791. package/dist/spec/usecase/ValidateManifestsUseCase.d.ts +0 -8
  792. package/dist/spec/usecase/ValidateManifestsUseCase.d.ts.map +0 -1
  793. package/dist/spec/usecase/ValidateManifestsUseCase.js +0 -101
  794. package/dist/spec/usecase/ValidateManifestsUseCase.js.map +0 -1
  795. package/dist/spec/usecase/dto/ValidateManifestsRequest.d.ts +0 -27
  796. package/dist/spec/usecase/dto/ValidateManifestsRequest.d.ts.map +0 -1
  797. package/dist/spec/usecase/dto/ValidateManifestsRequest.js +0 -2
  798. package/dist/spec/usecase/dto/ValidateManifestsRequest.js.map +0 -1
  799. package/dist/spec/usecase/dto/ValidateManifestsResponse.d.ts +0 -14
  800. package/dist/spec/usecase/dto/ValidateManifestsResponse.d.ts.map +0 -1
  801. package/dist/spec/usecase/dto/ValidateManifestsResponse.js +0 -2
  802. package/dist/spec/usecase/dto/ValidateManifestsResponse.js.map +0 -1
  803. package/dist/spec/usecase/dto/index.d.ts +0 -3
  804. package/dist/spec/usecase/dto/index.d.ts.map +0 -1
  805. package/dist/spec/usecase/dto/index.js +0 -3
  806. package/dist/spec/usecase/dto/index.js.map +0 -1
  807. package/dist/spec/usecase/index.d.ts +0 -10
  808. package/dist/spec/usecase/index.d.ts.map +0 -1
  809. package/dist/spec/usecase/index.js +0 -10
  810. package/dist/spec/usecase/index.js.map +0 -1
  811. package/dist/testing/cases/before-hook.d.ts +0 -4
  812. package/dist/testing/cases/before-hook.d.ts.map +0 -1
  813. package/dist/testing/cases/before-hook.js +0 -71
  814. package/dist/testing/cases/before-hook.js.map +0 -1
  815. package/dist/testing/cases/corpus.d.ts +0 -12
  816. package/dist/testing/cases/corpus.d.ts.map +0 -1
  817. package/dist/testing/cases/corpus.js +0 -91
  818. package/dist/testing/cases/corpus.js.map +0 -1
  819. package/dist/testing/cases/policy.d.ts +0 -74
  820. package/dist/testing/cases/policy.d.ts.map +0 -1
  821. package/dist/testing/cases/policy.js +0 -142
  822. package/dist/testing/cases/policy.js.map +0 -1
  823. package/dist/testing/cases/printer.d.ts +0 -4
  824. package/dist/testing/cases/printer.d.ts.map +0 -1
  825. package/dist/testing/cases/printer.js +0 -32
  826. package/dist/testing/cases/printer.js.map +0 -1
  827. package/dist/testing/cases/report-view.d.ts +0 -5
  828. package/dist/testing/cases/report-view.d.ts.map +0 -1
  829. package/dist/testing/cases/report-view.js +0 -33
  830. package/dist/testing/cases/report-view.js.map +0 -1
  831. package/dist/testing/cases/requisition.d.ts +0 -4
  832. package/dist/testing/cases/requisition.d.ts.map +0 -1
  833. package/dist/testing/cases/requisition.js +0 -38
  834. package/dist/testing/cases/requisition.js.map +0 -1
  835. package/dist/testing/cases/search-places.d.ts +0 -4
  836. package/dist/testing/cases/search-places.d.ts.map +0 -1
  837. package/dist/testing/cases/search-places.js +0 -60
  838. package/dist/testing/cases/search-places.js.map +0 -1
  839. package/dist/testing/cases/stock.d.ts +0 -4
  840. package/dist/testing/cases/stock.d.ts.map +0 -1
  841. package/dist/testing/cases/stock.js +0 -35
  842. package/dist/testing/cases/stock.js.map +0 -1
  843. package/dist/testing/cases/store.d.ts +0 -4
  844. package/dist/testing/cases/store.d.ts.map +0 -1
  845. package/dist/testing/cases/store.js +0 -100
  846. package/dist/testing/cases/store.js.map +0 -1
  847. package/dist/testing/harness.d.ts +0 -94
  848. package/dist/testing/harness.d.ts.map +0 -1
  849. package/dist/testing/harness.js +0 -168
  850. package/dist/testing/harness.js.map +0 -1
  851. package/dist/testing/index.d.ts.map +0 -1
  852. package/dist/testing/index.js.map +0 -1
  853. package/dist/testing/report.d.ts +0 -15
  854. package/dist/testing/report.d.ts.map +0 -1
  855. package/dist/testing/report.js +0 -22
  856. package/dist/testing/report.js.map +0 -1
  857. package/dist/web/createRestSurface.d.ts +0 -7
  858. package/dist/web/createRestSurface.d.ts.map +0 -1
  859. package/dist/web/createRestSurface.js +0 -39
  860. package/dist/web/createRestSurface.js.map +0 -1
  861. package/dist/web/index.d.ts +0 -2
  862. package/dist/web/index.d.ts.map +0 -1
  863. package/dist/web/index.js +0 -2
  864. package/dist/web/index.js.map +0 -1
  865. package/docs/adr/0001-four-atom-manifest-model.md +0 -440
  866. package/docs/adr/0002-closed-enums-for-bindings.md +0 -253
  867. package/docs/adr/0007-ai-as-primary-author.md +0 -473
  868. package/docs/adr/0008-structured-diagnostic-shape.md +0 -338
  869. package/docs/adr/0009-consumer-supplied-manifests.md +0 -149
  870. package/docs/adr/0010-locale-and-translates.md +0 -453
  871. package/docs/adr/0011-adapter-port-spec.md +0 -281
  872. package/docs/adr/0012-views-as-public-rest.md +0 -161
  873. package/docs/adr/0013-agent-provisioned-consumer-projects.md +0 -154
  874. package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +0 -919
  875. package/docs/adr/0016-site-semantic-layer.md +0 -70
  876. package/docs/adr/0017-media-multi-variant-agent-side-optimization.md +0 -181
  877. package/docs/adr/0018-core-starters-repository-boundary.md +0 -179
  878. package/docs/adr/0019-sealed-manifest-runtime-pipeline.md +0 -244
  879. package/docs/adr/0020-builtin-handler-contracts-and-matched-upsert.md +0 -199
  880. package/docs/adr/0021-retire-starter-scaffolding.md +0 -30
  881. package/docs/adr/0022-caller-observed-version-occ.md +0 -212
  882. package/docs/adr/0023-port-failure-contract.md +0 -69
  883. package/docs/adr/0024-manifest-native-schema-tables.md +0 -264
  884. package/docs/adr/0025-reserved-native-columns-and-published-only-public-views.md +0 -105
  885. package/docs/adr/0026-progressive-generate.md +0 -36
  886. package/docs/adr/0027-scheduled-procedure-triggers.md +0 -31
  887. package/docs/adr/0028-schema-ttl-expiry.md +0 -33
  888. package/docs/adr/0029-mcp-apps-interaction-contracts.md +0 -324
  889. package/docs/adr/0030-store.md +0 -40
  890. package/docs/adr/0031-host-cloud-core-contract.md +0 -25
  891. package/docs/adr/0032-store-centric-core-and-service-contract.md +0 -361
  892. package/docs/adr/0033-storage-converges-to-the-plan.md +0 -92
  893. package/docs/adr/0034-store-is-authored-as-sql.md +0 -192
  894. package/docs/adr/0035-sql-dialects.md +0 -154
  895. package/docs/adr/0036-host-and-dialect.md +0 -65
  896. package/docs/adr/0037-postgresql-is-the-reference-dialect.md +0 -124
  897. package/docs/adr/0038-bun-native-host.md +0 -63
  898. package/docs/adr/README.md +0 -96
  899. package/docs/adr/adr-lite-803-request-diagnostics.md +0 -38
  900. package/docs/adr/adr-lite-808-route-readiness.md +0 -47
  901. package/docs/adr/adr-lite-809-bounded-public-content.md +0 -71
  902. package/docs/adr/adr-lite-812-native-parity.md +0 -150
  903. package/docs/adr/adr-lite-823-home-statistics.md +0 -64
  904. package/docs/adr/adr-lite-842-mcp-authoring.md +0 -30
  905. package/docs/adr/adr-lite-845-frontend-client.md +0 -38
  906. package/docs/adr/adr-lite-861-admin-webmcp.md +0 -48
  907. package/docs/adr/adr-lite-909-admin-ui-kit.md +0 -37
  908. package/docs/examples/commerce-inventory.md +0 -481
  909. package/docs/examples/commerce.md +0 -254
  910. package/docs/examples/guarded-api.md +0 -238
  911. package/docs/examples/intake-hooks.md +0 -237
  912. package/docs/examples/intake.md +0 -127
  913. package/docs/examples/legal-documents.md +0 -218
  914. package/docs/examples/procurement.md +0 -215
  915. package/docs/examples/publication.md +0 -211
  916. package/docs/examples/reference-service/.mantle/generated/mantle.ts +0 -174
  917. package/docs/examples/reference-service/.mantle/generated/plan.json +0 -1936
  918. package/docs/examples/reference-service/README.md +0 -33
  919. package/docs/examples/reference-service/manifests/catalog.yaml +0 -103
  920. package/docs/examples/reference-service/manifests/orders.yaml +0 -120
  921. package/docs/examples/reference-service/mantle.config.json +0 -1
  922. package/docs/examples/reference-service/package.json +0 -27
  923. package/docs/examples/reference-service/smoke.mjs +0 -156
  924. package/docs/examples/reference-service/src/handlers.ts +0 -36
  925. package/docs/examples/reference-service/src/index.ts +0 -33
  926. package/docs/examples/reference-service/src/service.ts +0 -78
  927. package/docs/examples/reference-service/wrangler.jsonc +0 -26
  928. package/docs/examples/reservation.md +0 -142
  929. package/docs/skills/develop/mcp-app.md +0 -79
  930. /package/docs/examples/{reference-service → app-shell}/.dev.vars.example +0 -0
@@ -0,0 +1,191 @@
1
+ // Purchase requests end to end: submit, guarded transitions, admin-only approve/reject (Admin + admin MCP),
2
+ // optimistic concurrency, one-transaction approval with a budget check, supplier order + signed callback, emails, cron.
3
+ import assert from "node:assert/strict";
4
+ import { createHmac } from "node:crypto";
5
+ import { createServer } from "node:http";
6
+ import { assertMcpDiscovery, smoke } from "./smoke-kit.mjs";
7
+ import { developerConsole } from "./generated/admin-descriptors.ts";
8
+
9
+ const SECRET = "local-supplier-secret", DAILY = "0 9 * * *", DAY_US = 86_400 * 1_000_000;
10
+
11
+ // A stand-in supplier API: accepts an order and answers with its order id, or 503 while `supplierDown`.
12
+ const received = [];
13
+ let supplierDown = false;
14
+ const supplier = createServer((request, response) => {
15
+ let body = "";
16
+ request.on("data", chunk => { body += chunk; });
17
+ request.on("end", () => {
18
+ if (supplierDown) return response.writeHead(503).end();
19
+ received.push({ ...JSON.parse(body), idempotencyKey: request.headers["idempotency-key"] });
20
+ response.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ orderId: `SUP-${received.length}` }));
21
+ });
22
+ });
23
+ await new Promise(resolve => supplier.listen(0, "127.0.0.1", resolve));
24
+
25
+ try {
26
+ await smoke(async ({ origin, owner, call, mcp, signIn, sql, cron, waitForLog, step, logs }) => {
27
+ const query = sql;
28
+ const row = async id => (await query(`SELECT requeststatus AS status, version, supplierorderid AS "supplierOrderId" FROM requests WHERE id = '${id}'`))[0];
29
+ const statusAndOrder = async id => { const { status, supplierOrderId } = await row(id); return { status, supplierOrderId }; };
30
+ const remaining = async department => (await query(`SELECT remaining FROM budgets WHERE department = '${department}'`))[0].remaining;
31
+ const ledgerRows = () => query("SELECT requestid AS \"requestId\", amount, remainingafter AS \"remainingAfter\" FROM ledger");
32
+ const operation = (cookie, name, body) => call("POST", `/admin/api/operations/${name}`, { cookie, body });
33
+ const signed = (payload, secret = SECRET) => {
34
+ const body = JSON.stringify(payload), timestamp = String(Math.floor(Date.now() / 1000));
35
+ const signature = createHmac("sha256", secret).update(`${timestamp}.${body}`).digest("hex");
36
+ return call("POST", "/callbacks/supplier", { body, headers: { "x-supplier-timestamp": timestamp, "x-supplier-signature": signature } });
37
+ };
38
+
39
+ const admin = await signIn(owner), alice = await signIn("alice@example.test"), bob = await signIn("bob@example.test");
40
+ const carol = await signIn("carol@example.test");
41
+ await query(`UPDATE "user" SET role = 'admin' WHERE email = 'carol@example.test'`);
42
+ assert.equal((await call("GET", "/admin/api/me", { cookie: carol })).status, 200);
43
+
44
+ const submit = async (cookie, title, amount, department) => {
45
+ const response = await call("POST", "/api/requests", { cookie, body: { title, amount, department } });
46
+ assert.equal(response.status, 200, JSON.stringify(response.body));
47
+ return response.body.results[0][0]?.id;
48
+ };
49
+ assert.equal((await call("POST", "/api/requests", { body: { title: "Pens", amount: 5, department: "Design" } })).status, 401);
50
+ const laptop = await submit(alice, "Laptop", 1200, "Engineering");
51
+ const monitors = await submit(alice, "Monitors", 1500, "Design");
52
+ const chairs = await submit(alice, "Chairs", 300, "Design");
53
+ const desk = await submit(bob, "Desk", 400, "Engineering");
54
+ const projector = await submit(bob, "Projector", 600, "Engineering");
55
+ assert.equal(await submit(bob, "Rocket", 10, "Space"), undefined);
56
+ assert.equal((await query("SELECT count(*) AS n FROM requests"))[0].n, 5);
57
+ const mine = (await call("GET", "/api/views/my-requests", { cookie: alice })).body.rows;
58
+ assert.deepEqual(mine.map(r => r.title).sort(), ["Chairs", "Laptop", "Monitors"]);
59
+ assert.deepEqual((await call("GET", "/api/views/my-requests", { cookie: bob })).body.rows.map(r => r.title).sort(), ["Desk", "Projector"]);
60
+ step("employees submit requests (unknown department creates nothing) and see only their own");
61
+
62
+ // fetch follows the 303, so an anonymous visitor ends on the sign-in page.
63
+ assert.match((await call("GET", "/requests")).body, /<h1>Sign in<\/h1>/);
64
+ assert.match((await call("GET", "/sign-in?return=%2Frequests")).body, /sign-in\/email-otp/);
65
+ const page = (await call("GET", "/requests", { cookie: bob })).body;
66
+ assert.match(page, /Projector/); assert.doesNotMatch(page, /Laptop/); assert.match(page, /<option>Design<\/option>/);
67
+ // Writes go through the REST Trigger the page's form names, never through the page itself.
68
+ assert.match(page, /<form action="\/api\/requests" method="post"/);
69
+ assert.equal((await call("POST", "/requests", { cookie: bob })).status, 404);
70
+ assert.equal((await call("POST", "/api/requests", { cookie: bob, body: { title: "Pens", amount: 0, department: "Design" } })).status, 400);
71
+ step("the /requests page sends anonymous visitors to /sign-in, lists only the employee's own requests and submits to the REST Trigger");
72
+
73
+ assert.equal((await operation(admin, "open-budget", { department: "Marketing", remaining: 500 })).status, 200);
74
+ assert.equal((await operation(admin, "open-budget", { department: "Design", remaining: 1 })).status, 409);
75
+ assert.equal(await remaining("Marketing"), 500);
76
+ assert.equal((await operation(alice, "open-budget", { department: "Ops", remaining: 1 })).status, 403);
77
+ step("admins open a department budget from the Budgets list header (open-budget; a second one for the same department is 409); employees cannot");
78
+
79
+ const version = async id => (await row(id)).version;
80
+ assert.equal((await operation(alice, "approve-request", { id: laptop, expectedVersion: await version(laptop) })).status, 403);
81
+ // Generated static metadata uses the MCP tool names; the app chooses the surface and test identity.
82
+ const declared = developerConsole.interfaces.callable;
83
+ const publicTools = await assertMcpDiscovery(`${origin}/mcp`, { headers: { cookie: alice, origin }, tools: declared.filter(tool => tool.surface === "public") });
84
+ assert.ok(!publicTools.includes("approve_request") && !publicTools.includes("reject_request"), publicTools.join());
85
+ const adminTools = await assertMcpDiscovery(`${origin}/mcp/admin`, { headers: { cookie: admin, origin }, tools: declared.filter(tool => tool.surface === "admin") });
86
+ // Denial is separate from successful initialized discovery.
87
+ assert.equal((await mcp("/mcp/admin")).status, 401);
88
+ assert.equal((await mcp("/mcp/admin", alice)).status, 403);
89
+ assert.ok(adminTools.includes("approve_request") && adminTools.includes("reject_request"), adminTools.join());
90
+ for (const hidden of ["confirm_order", "mark_ordered", "approve_request_write", "reject_request_write"])
91
+ assert.ok(!adminTools.includes(hidden) && !publicTools.includes(hidden), hidden);
92
+ step("approve/reject are admin-only: an employee gets 403 in Admin and the tools exist only on /mcp/admin");
93
+
94
+ const rejected = await mcp("/mcp/admin", admin, "reject_request", { id: chairs, expectedVersion: await version(chairs) });
95
+ assert.equal(rejected.body.result.isError, undefined, JSON.stringify(rejected.body));
96
+ assert.equal((await row(chairs)).status, "rejected");
97
+ await waitForLog(/purchasing\.rejected → alice@example\.test[\s\S]*?Rejected: Chairs/);
98
+ const guarded = await operation(admin, "approve-request", { id: chairs, expectedVersion: await version(chairs) });
99
+ assert.equal(guarded.status, 409, JSON.stringify(guarded.body));
100
+ assert.equal((await row(chairs)).status, "rejected");
101
+ step("admin MCP rejects (requester emailed); approving it with its current version is 409: WHERE requestStatus = 'submitted' matched 0 rows");
102
+
103
+ const overBudget = await operation(admin, "approve-request", { id: monitors, expectedVersion: await version(monitors) });
104
+ assert.equal(overBudget.status, 409, JSON.stringify(overBudget.body));
105
+ assert.match(overBudget.body.error.message, /budget cannot cover/);
106
+ assert.equal((await row(monitors)).status, "submitted");
107
+ assert.equal(await remaining("Design"), 1000);
108
+ assert.equal((await ledgerRows()).length, 0);
109
+ step("over budget: remaining >= 0 fails statement 2, so the status change and ledger insert roll back too");
110
+
111
+ const seen = await version(monitors); // the version the admin reviewed
112
+ const generic = await call("PATCH", `/admin/api/entries/${monitors}?collection=requests`, { cookie: admin, body: { expectedVersion: seen, data: { requestStatus: "approved" } } });
113
+ assert.ok(generic.status >= 400 && generic.status < 500, JSON.stringify(generic.body));
114
+ assert.deepEqual([(await row(monitors)).status, await version(monitors)], ["submitted", seen]);
115
+ const changeAmount = (cookie, expectedVersion) => call("POST", `/api/requests/${monitors}/amount`, { cookie, body: { expectedVersion, amount: 900 } });
116
+ assert.equal((await changeAmount(bob, seen)).status, 409);
117
+ const changed = await changeAmount(alice, seen);
118
+ assert.equal(changed.status, 200, JSON.stringify(changed.body));
119
+ assert.equal((await changeAmount(alice, seen)).status, 409);
120
+ const stale = await operation(admin, "approve-request", { id: monitors, expectedVersion: seen });
121
+ assert.equal(stale.status, 409, JSON.stringify(stale.body));
122
+ assert.equal((await row(monitors)).status, "submitted");
123
+ assert.equal(await remaining("Design"), 1000);
124
+ step("Admin cannot edit requests generically; once the requester changes the amount, approving the reviewed version is 409");
125
+
126
+ const lock = (await call("GET", "/api/views/my-requests", { cookie: alice })).body.rows.find(r => r.id === laptop).version;
127
+ const race = await Promise.all([admin, carol].map(cookie => operation(cookie, "approve-request", { id: laptop, expectedVersion: lock })));
128
+ assert.deepEqual(race.map(r => r.status).sort(), [200, 409], JSON.stringify(race.map(r => r.body)));
129
+ assert.equal(await remaining("Engineering"), 3800);
130
+ assert.deepEqual(await ledgerRows(), [{ requestId: laptop, amount: 1200, remainingAfter: 3800 }]);
131
+ step("two admins approve the same version at once: one 200, one 409; the budget and ledger change once");
132
+
133
+ assert.deepEqual(received, [{ reference: laptop, item: "Laptop", amount: 1200, idempotencyKey: laptop }]);
134
+ assert.deepEqual(await row(laptop), { status: "ordered", version: lock + 2, supplierOrderId: "SUP-1" });
135
+ await waitForLog(/purchasing\.approved → alice@example\.test[\s\S]*?Approved: Laptop/);
136
+ step("approval places the supplier order (status 'ordered') and emails the requester");
137
+
138
+ supplierDown = true;
139
+ const viaAi = await mcp("/mcp/admin", admin, "approve_request", { id: desk, expectedVersion: await version(desk) });
140
+ assert.equal(viaAi.body.result.isError, undefined, JSON.stringify(viaAi.body));
141
+ assert.equal((await operation(carol, "approve-request", { id: projector, expectedVersion: await version(projector) })).status, 200);
142
+ assert.deepEqual([(await row(desk)).status, (await row(projector)).status], ["approved", "approved"]);
143
+ assert.equal(await remaining("Engineering"), 2800);
144
+ await waitForLog(/purchasing\.approved → bob@example\.test[\s\S]*?Approved: Desk/);
145
+ step("the admin MCP tool approves too; with the supplier down, approvals still commit and stay 'approved'");
146
+
147
+ assert.equal((await signed({ reference: laptop, orderId: "SUP-1" }, "wrong-secret")).status, 401);
148
+ const tampered = await call("POST", "/callbacks/supplier", { body: JSON.stringify({ reference: laptop, orderId: "SUP-1" }),
149
+ headers: { "x-supplier-timestamp": String(Math.floor(Date.now() / 1000)), "x-supplier-signature": "0".repeat(64) } });
150
+ assert.equal(tampered.status, 401);
151
+ assert.equal((await row(laptop)).status, "ordered");
152
+ assert.equal((await signed({ reference: laptop, orderId: "SUP-9" })).status, 409);
153
+ assert.equal((await signed({ reference: laptop, orderId: "SUP-1" })).status, 204);
154
+ assert.equal((await row(laptop)).status, "confirmed");
155
+ assert.equal((await signed({ reference: laptop, orderId: "SUP-1" })).status, 204);
156
+ assert.equal((await signed({ reference: monitors, orderId: "SUP-1" })).status, 409);
157
+ // The callback can beat mark-ordered: an 'approved' request is confirmed and keeps the supplier's order id.
158
+ assert.equal((await signed({ reference: desk, orderId: "SUP-EARLY" })).status, 204);
159
+ assert.deepEqual(await statusAndOrder(desk), { status: "confirmed", supplierOrderId: "SUP-EARLY" });
160
+ step("supplier callback: bad signatures are 401 with no change; a signed one confirms (also before 'ordered'); a replay is 204; a wrong order id or a submitted request is 409");
161
+
162
+ const late = await submit(bob, "Whiteboard", 150, "Engineering");
163
+ await query(`UPDATE requests SET created_at = created_at - ${4 * DAY_US} WHERE id = '${late}'`);
164
+ const before = logs().length;
165
+ supplierDown = false;
166
+ assert.equal(await cron(DAILY), 200);
167
+ for (const to of [owner, "carol@example.test"])
168
+ await waitForLog(new RegExp(`purchasing\\.reminder → ${to.replace(/[.+]/g, "\\$&")}[\\s\\S]*?Whiteboard`));
169
+ const reminder = logs().slice(before);
170
+ assert.ok(!reminder.includes("Monitors"), "a request submitted today is not in the reminder");
171
+ assert.deepEqual(received.map(order => order.reference), [laptop, projector]);
172
+ assert.deepEqual(await statusAndOrder(projector), { status: "ordered", supplierOrderId: "SUP-2" });
173
+ step("daily cron retries the failed supplier order (system caller) and emails every admin about requests pending more than 3 days, and only those");
174
+
175
+ const small = await call("POST", "/api/requests", { cookie: bob, body: { title: "Pens", amount: 50, department: "Engineering" } });
176
+ assert.equal(small.status, 200, JSON.stringify(small.body));
177
+ const pens = small.body.results[0][0];
178
+ assert.equal(pens.requestStatus, "approved");
179
+ const spent = await remaining("Engineering");
180
+ assert.deepEqual((await ledgerRows()).filter(r => r.requestId === pens.id), [{ requestId: pens.id, amount: 50, remainingAfter: spent }]);
181
+ await query(`UPDATE budgets SET remaining = 20 WHERE department = 'Design'`);
182
+ const ledgerBefore = (await ledgerRows()).length, requestsBefore = (await query("SELECT count(*) AS n FROM requests"))[0].n;
183
+ assert.equal((await call("POST", "/api/requests", { cookie: alice, body: { title: "Tape", amount: 30, department: "Design" } })).status, 409);
184
+ assert.equal(await remaining("Design"), 20);
185
+ assert.equal((await ledgerRows()).length, ledgerBefore);
186
+ assert.equal((await query("SELECT count(*) AS n FROM requests"))[0].n, requestsBefore);
187
+ step("CASE approves a request of 100 or less at submission: the status, the budget and the ledger row commit together, and one that would overspend is refused with nothing written");
188
+ }, { vars: { EMPLOYEE_DOMAIN: "example.test", SUPPLIER_API_URL: `http://127.0.0.1:${supplier.address().port}/orders`, SUPPLIER_WEBHOOK_SECRET: SECRET } });
189
+ } finally {
190
+ supplier.close();
191
+ }
@@ -0,0 +1,13 @@
1
+ import { defineAdmin } from "@aotter/mantle-admin/app";
2
+ import { admin } from "../generated/admin-client.js";
3
+
4
+ /**
5
+ * The Admin entry, built by `mantle bundle`. The approver's daily work gets its own sidebar group above Admin's
6
+ * built-in ones (content 10, operations 20, reports 30; More stays last); manifests place `pending-requests` and
7
+ * `requests` in it with `uiSchema.nav`. `tsc` refuses a manifest group left undeclared here.
8
+ * AGENT: add groups for how the staff work; titles take one string or a { locale: text } map.
9
+ */
10
+ export default defineAdmin(admin, {
11
+ nav: { groups: { approvals: { title: { en: "Approvals", "zh-TW": "簽核" }, order: 5 } } },
12
+ pages: [{ path: "guide", title: "Approver guide", element: <p>Approve a request when the amount fits the budget; reject it with a reason otherwise.</p>, nav: { group: "approvals", order: 9 } }],
13
+ });
@@ -0,0 +1,33 @@
1
+ import { ContractError } from "@aotter/mantle/contracts";
2
+ import type { Handlers } from "../generated/generated.js";
3
+ import { notifyUser } from "./notify.js";
4
+ import { placeOrder } from "./supplier.js";
5
+
6
+ type Decided = { id: string; title: string; amount: number; authorId: string };
7
+
8
+ export const handlers: Handlers = {
9
+ "approve-request": async ({ store }, input) => {
10
+ let decided: Decided;
11
+ try {
12
+ // One transaction: the guarded status change, the budget deduction and the ledger row.
13
+ decided = (await store.procedures["approve-request-write"](input)).results[0]![0] as Decided;
14
+ } catch (error) {
15
+ // The declared budget check is a business refusal, not a server error. Nothing was written.
16
+ // The approval's only Schema check is the budget one.
17
+ if (error instanceof ContractError && error.code === "CONFLICT" && error.fields?.kind === "check" && error.fields.schema === "budgets")
18
+ throw new ContractError("CONFLICT", "The department budget cannot cover this request.", "procedures/approve-request");
19
+ throw error;
20
+ }
21
+ // After commit: nothing below may turn the committed approval into an error response.
22
+ const ordered = await placeOrder(store, decided);
23
+ await notifyUser(decided.authorId, `Approved: ${decided.title}`,
24
+ `Your purchase request "${decided.title}" was approved${ordered ? " and ordered from the supplier" : ""}.`, "purchasing.approved");
25
+ return { id: decided.id, requestStatus: ordered ? "ordered" : "approved" };
26
+ },
27
+ "reject-request": async ({ store }, input) => {
28
+ const decided = (await store.procedures["reject-request-write"](input)).results[0]![0] as Decided;
29
+ await notifyUser(decided.authorId, `Rejected: ${decided.title}`,
30
+ `Your purchase request "${decided.title}" was rejected.`, "purchasing.rejected");
31
+ return { id: decided.id, requestStatus: "rejected" };
32
+ },
33
+ };
@@ -0,0 +1,29 @@
1
+ import { ConsoleEmailSender, type EmailSender } from "@aotter/mantle/email";
2
+ import { env } from "cloudflare:workers";
3
+
4
+ // AGENT: replace with a real sender before deploying (Cloudflare `send_email` binding, docs/handbook/guides/email.md).
5
+ // ConsoleEmailSender only logs; it sends nothing.
6
+ export const sender: EmailSender = new ConsoleEmailSender();
7
+
8
+ // Recipients come from Better Auth through Mantle's server-side read (no session, no SQL on Better Auth's tables).
9
+ // Loaded lazily: Better Auth stays out of the Worker's startup graph.
10
+ const identity = async () => (await import("@aotter/mantle/auth/identity")).readIdentity((await import("./auth.js")).createAuth(env));
11
+
12
+ /** Active admins (Better Auth adminRoles / adminUserIds). */
13
+ export async function adminEmails(): Promise<string[]> {
14
+ return (await (await identity()).admins()).map(user => user.email);
15
+ }
16
+
17
+ /** Email failures never undo a decision that has already committed. */
18
+ export async function notify(to: string, subject: string, text: string, category: string) {
19
+ try { await sender.send({ to, subject, text, locale: "en", category }); }
20
+ catch (error) { console.error(`[purchasing] ${category} email failed`, error); }
21
+ }
22
+
23
+ /** Looks up the user's email and sends; a failed lookup is logged like a failed send. */
24
+ export async function notifyUser(userId: string, subject: string, text: string, category: string) {
25
+ let to: string | undefined;
26
+ try { to = (await (await identity()).user(userId))?.email; }
27
+ catch (error) { console.error(`[purchasing] ${category} recipient lookup failed`, error); }
28
+ if (to) await notify(to, subject, text, category);
29
+ }
@@ -0,0 +1,57 @@
1
+ import type { Caller } from "@aotter/mantle/contracts";
2
+ import { store } from "../generated/generated.js";
3
+ import myRequests from "../generated/paged/views/my-requests.js";
4
+ import { handlers } from "./handlers.js";
5
+ import { signInRedirect } from "./sign-in.js";
6
+
7
+ /**
8
+ * GET /requests: the employee's own page. Anonymous visitors go to /sign-in and come back.
9
+ * Reads run here as the signed-in caller (`my-requests` restricts rows to `author_id = auth.uid()`). Writes do not:
10
+ * the form submits to `POST /api/requests`, the REST Trigger the manifest declares for `submit-request`, so input
11
+ * validation, `requires` and the origin check happen there. Request data never reaches a Procedure through app code.
12
+ * AGENT: unstyled on purpose; add the app's styling, copy and a change-amount action (its own Trigger) as needed.
13
+ */
14
+ export async function requestsPage(request: Request, env: Env, caller: Caller): Promise<Response> {
15
+ if (caller.kind === "anonymous") return signInRedirect(request);
16
+ const mine = await myRequests(env.DB, caller, {}, { limit: 50 });
17
+ // Department names only; the budget amounts stay admin-side.
18
+ const budgets = await store(env.DB, { kind: "system", reason: "department list" }, { handlers }).collection("budgets").find({ limit: 100 });
19
+ return new Response(`<!doctype html>
20
+ <html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
21
+ <meta name="robots" content="noindex"><title>My purchase requests</title>
22
+ <h1>My purchase requests</h1>
23
+ <form action="/api/requests" method="post" id="new-request">
24
+ <label>What <input name="title" required></label>
25
+ <label>Amount <input name="amount" type="number" min="1" step="1" required></label>
26
+ <label>Department <select name="department" required>${budgets.rows.map(b => `<option>${escape(String(b.department))}</option>`).join("")}</select></label>
27
+ <button>Submit</button>
28
+ <p role="alert" id="error" hidden></p>
29
+ </form>
30
+ <table>
31
+ <tr><th>What</th><th>Amount</th><th>Department</th><th>Status</th></tr>
32
+ ${mine.rows.map(r => `<tr><td>${escape(String(r.title))}</td><td>${escape(String(r.amount))}</td><td>${escape(String(r.department))}</td><td>${escape(String(r.requestStatus))}</td></tr>`).join("\n ")}
33
+ </table>
34
+ <script type="module">
35
+ // The submit event fires only after the browser's own checks (required, min) pass; the Trigger checks again.
36
+ const form = document.getElementById("new-request"), error = document.getElementById("error");
37
+ form.addEventListener("submit", async event => {
38
+ event.preventDefault();
39
+ // Field types come from the inputs, so a number input sends a JSON number.
40
+ const body = Object.fromEntries([...form.elements].filter(field => field.name).map(field => [field.name, field.type === "number" ? field.valueAsNumber : field.value]));
41
+ const button = form.querySelector("button");
42
+ button.disabled = true;
43
+ try {
44
+ const response = await fetch(form.action, { method: form.method, headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
45
+ if (response.ok) return location.reload(); // the page reads the list again
46
+ error.textContent = (await response.json().catch(() => null))?.error?.message ?? "Not submitted. Try again.";
47
+ } catch {
48
+ error.textContent = "Not submitted: the network is unavailable.";
49
+ }
50
+ error.hidden = false;
51
+ button.disabled = false;
52
+ });
53
+ </script>
54
+ `, { headers: { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" } });
55
+ }
56
+
57
+ const escape = (text: string) => text.replace(/[&<>"']/g, c => `&#${c.charCodeAt(0)};`);
@@ -0,0 +1,53 @@
1
+ import { ContractError, type Caller } from "@aotter/mantle/contracts";
2
+ import { store } from "../generated/generated.js";
3
+ import confirmOrder from "../generated/procedures/confirm-order.js";
4
+ import { handlers } from "./handlers.js";
5
+ import { requestsPage } from "./requests-page.js";
6
+ import { signInPage } from "./sign-in.js";
7
+
8
+ const MAX_BODY = 64 * 1024, MAX_SKEW_SECONDS = 300;
9
+
10
+ /**
11
+ * App-owned routes, tried before Admin, MCP and REST. Return undefined to fall through.
12
+ * GET /sign-in: member sign-in (src/sign-in.ts). GET /requests: the employee's page (src/requests-page.ts).
13
+ * POST /callbacks/supplier: the supplier confirms an order. The signature is checked over the RAW body before parsing.
14
+ * AGENT: match the real supplier's signature scheme (header names, what is signed, encoding).
15
+ */
16
+ export async function appRoutes(request: Request, env: Env, context: { caller: () => Promise<Caller>; waitUntil: (task: Promise<unknown>) => void }): Promise<Response | undefined> {
17
+ const path = new URL(request.url).pathname;
18
+ if (path === "/sign-in" && request.method === "GET") return signInPage();
19
+ if (path === "/requests" && request.method === "GET") return requestsPage(request, env, await context.caller());
20
+ if (path !== "/callbacks/supplier") return undefined;
21
+ if (request.method !== "POST") return new Response(null, { status: 405, headers: { allow: "POST" } });
22
+ if (Number(request.headers.get("content-length") ?? MAX_BODY + 1) > MAX_BODY) return new Response(null, { status: 413 });
23
+ const raw = await request.arrayBuffer();
24
+ const timestamp = request.headers.get("x-supplier-timestamp") ?? "";
25
+ if (!(await verified(env.SUPPLIER_WEBHOOK_SECRET, timestamp, raw, request.headers.get("x-supplier-signature") ?? "")))
26
+ return new Response(null, { status: 401 });
27
+
28
+ let body: { reference?: unknown; orderId?: unknown };
29
+ try { body = JSON.parse(new TextDecoder().decode(raw)); } catch { return new Response(null, { status: 400 }); }
30
+ if (typeof body.reference !== "string" || typeof body.orderId !== "string") return new Response(null, { status: 400 });
31
+ // Trusted path: a system caller, after the signature proved the sender.
32
+ const system: Caller = { kind: "system", reason: "supplier callback" };
33
+ try {
34
+ await confirmOrder(env.DB, system, { id: body.reference, supplierOrderId: body.orderId });
35
+ } catch (error) {
36
+ if (!(error instanceof ContractError && error.code === "CONFLICT")) throw error;
37
+ // A replay of a callback already applied succeeds again, so the supplier stops retrying.
38
+ const current = await store(env.DB, system, { handlers }).collection("requests").findById(body.reference);
39
+ if (current?.requeststatus === "confirmed" && current.supplierorderid === body.orderId) return new Response(null, { status: 204 });
40
+ return new Response(null, { status: 409 }); // unknown request, another order id, or not approved/ordered
41
+ }
42
+ return new Response(null, { status: 204 });
43
+ }
44
+
45
+ /** HMAC-SHA256 over `${timestamp}.${raw body}`, hex encoded; crypto.subtle.verify compares in constant time. */
46
+ async function verified(secret: string, timestamp: string, raw: ArrayBuffer, signature: string) {
47
+ if (!/^\d+$/.test(timestamp) || Math.abs(Date.now() / 1000 - Number(timestamp)) > MAX_SKEW_SECONDS) return false;
48
+ if (!/^[0-9a-f]{64}$/.test(signature)) return false;
49
+ const key = await crypto.subtle.importKey("raw", new TextEncoder().encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["verify"]);
50
+ const signed = new Uint8Array([...new TextEncoder().encode(`${timestamp}.`), ...new Uint8Array(raw)]);
51
+ const bytes = new Uint8Array(signature.match(/../g)!.map(pair => parseInt(pair, 16)));
52
+ return crypto.subtle.verify("HMAC", key, bytes, signed);
53
+ }
@@ -0,0 +1,31 @@
1
+ import { toCloudflareCron } from "@aotter/mantle/cron";
2
+ import type { Caller } from "@aotter/mantle/contracts";
3
+ import { store } from "../generated/generated.js";
4
+ import approvedRequests from "../generated/views/approved-requests.js";
5
+ import staleRequests from "../generated/views/stale-requests.js";
6
+ import { handlers } from "./handlers.js";
7
+ import { adminEmails, notify } from "./notify.js";
8
+ import { placeOrder } from "./supplier.js";
9
+
10
+ /** Daily 09:00 UTC; list the same expression in wrangler.jsonc `triggers.crons`. */
11
+ export const DAILY = toCloudflareCron("0 9 * * *");
12
+ /** Remind about requests still 'submitted' after this many days. */
13
+ const PENDING_DAYS = 3;
14
+
15
+ export async function scheduled(controller: ScheduledController, env: Env): Promise<void> {
16
+ if (controller.cron !== DAILY) throw new Error(`No application schedule runs on '${controller.cron}'`);
17
+ const system: Caller = { kind: "system", reason: "daily purchasing cron" };
18
+
19
+ // 1. Retry supplier orders that failed right after approval.
20
+ const data = store(env.DB, system, { handlers });
21
+ for (const { id, title, amount } of await approvedRequests(env.DB, system, {}))
22
+ await placeOrder(data, { id: String(id), title: String(title), amount: Number(amount) }); // View rows are typed loosely
23
+
24
+ // 2. Remind admins about requests waiting too long.
25
+ const before = new Date(controller.scheduledTime - PENDING_DAYS * 86_400_000).toISOString();
26
+ const stale = await staleRequests(env.DB, system, { before });
27
+ if (stale.length === 0) return;
28
+ const text = `${stale.length} purchase request(s) have waited more than ${PENDING_DAYS} days:\n` +
29
+ stale.map(r => `- ${r.title} (${r.department}, ${r.amount})`).join("\n");
30
+ for (const to of await adminEmails()) await notify(to, "Purchase requests waiting for approval", text, "purchasing.reminder");
31
+ }
@@ -0,0 +1,5 @@
1
+ /** Employees only: an email may create an account only on the company's domain (ADMIN_EMAIL always signs in). */
2
+ export async function allowSignUp(env: Env, email: string): Promise<boolean> {
3
+ // AGENT: set EMPLOYEE_DOMAIN in wrangler.jsonc vars; for a per-person list, see the club-events example.
4
+ return email.toLowerCase().endsWith(`@${env.EMPLOYEE_DOMAIN.toLowerCase()}`);
5
+ }
@@ -0,0 +1,32 @@
1
+ import { env } from "cloudflare:workers";
2
+ import { ContractError } from "@aotter/mantle/contracts";
3
+ import type { HandlerContext } from "../generated/generated.js";
4
+
5
+ /**
6
+ * Orders an approved request from the supplier, then marks it 'ordered'. Returns false when the supplier could not be
7
+ * reached; the request stays 'approved' and the daily cron (src/schedule.ts) retries it.
8
+ * AGENT: use the supplier's real API, auth and payload.
9
+ */
10
+ export async function placeOrder(store: HandlerContext["store"], request: { id: string; title: string; amount: number }) {
11
+ let orderId: string;
12
+ try {
13
+ const response = await fetch(env.SUPPLIER_API_URL, {
14
+ method: "POST",
15
+ // The request id as idempotency key: a retry after a lost response does not order twice.
16
+ headers: { "content-type": "application/json", "idempotency-key": request.id },
17
+ body: JSON.stringify({ reference: request.id, item: request.title, amount: request.amount }),
18
+ signal: AbortSignal.timeout(10_000),
19
+ });
20
+ if (!response.ok) throw new Error(`supplier answered ${response.status}`);
21
+ ({ orderId } = await response.json() as { orderId: string });
22
+ } catch (error) {
23
+ console.error(`[purchasing] supplier order for ${request.id} failed; it stays approved for the retry`, error);
24
+ return false;
25
+ }
26
+ try { await store.procedures["mark-ordered"]({ id: request.id, supplierOrderId: orderId }); }
27
+ catch (error) {
28
+ // The supplier's callback already confirmed it (it can arrive before this write).
29
+ if (!(error instanceof ContractError && error.code === "CONFLICT")) throw error;
30
+ }
31
+ return true;
32
+ }
@@ -0,0 +1,69 @@
1
+ {
2
+ "$schema": "node_modules/wrangler/config-schema.json",
3
+ "name": "mantle-app",
4
+ "main": "dist/worker/index.js",
5
+ "compatibility_date": "2026-09-01",
6
+ "compatibility_flags": [
7
+ "nodejs_compat",
8
+ "global_fetch_strictly_public"
9
+ ],
10
+ "vars": {
11
+ "EMPLOYEE_DOMAIN": "example.com"
12
+ },
13
+ "secrets": {
14
+ "required": [
15
+ "BETTER_AUTH_SECRET",
16
+ "PUBLIC_ORIGIN",
17
+ "ADMIN_EMAIL",
18
+ "SUPPLIER_API_URL",
19
+ "SUPPLIER_WEBHOOK_SECRET"
20
+ ]
21
+ },
22
+ "observability": {
23
+ "enabled": true,
24
+ "logs": {
25
+ "enabled": true,
26
+ "head_sampling_rate": 1
27
+ },
28
+ "traces": {
29
+ "enabled": true,
30
+ "head_sampling_rate": 0.01
31
+ }
32
+ },
33
+ "d1_databases": [
34
+ {
35
+ "binding": "DB",
36
+ "database_name": "mantle-app"
37
+ }
38
+ ],
39
+ "assets": {
40
+ "directory": "dist/client",
41
+ "binding": "ASSETS",
42
+ "run_worker_first": true,
43
+ "html_handling": "none"
44
+ },
45
+ "triggers": {
46
+ "crons": [
47
+ "0 9 * * *"
48
+ ]
49
+ },
50
+ "no_bundle": true,
51
+ "find_additional_modules": true,
52
+ "base_dir": "dist/worker",
53
+ "build": {
54
+ "command": "mantle bundle src/index.ts dist",
55
+ "watch_dir": [
56
+ "src",
57
+ "generated"
58
+ ]
59
+ },
60
+ "rules": [
61
+ {
62
+ "type": "ESModule",
63
+ "globs": [
64
+ "**/*.js"
65
+ ],
66
+ "fallthrough": true
67
+ }
68
+ ]
69
+ }
@@ -0,0 +1,60 @@
1
+ # Recipes
2
+
3
+ A multilingual recipe site. Admins write recipes in Admin as drafts and publish them; visitors read the published
4
+ recipes in Traditional Chinese (`zh-TW`, the default) and English (`en`) without signing in. The visitor pages are
5
+ plain app-owned HTML with the head tags search and answer engines read, plus Markdown and `llms.txt` for AI agents.
6
+
7
+ ## Capabilities shown
8
+
9
+ - Publishing lifecycle (draft → publish): `lifecycle: publishing` on both Schemas in `manifests/recipes.yaml`;
10
+ Admin's publish action runs the generated `recipes.publish` Procedure. Public Views and anonymous Store reads see
11
+ published rows only.
12
+ - Translations: `recipe_translations` is `localized` and `translates: { parent: recipes, on: slug }`. A translation
13
+ publishes only while its parent is published, and its reads merge the parent's `total_minutes`, `servings` and
14
+ `cover_url` (`src/routes.ts` `read`).
15
+ - Visitor pages in `src/routes.ts` (routing, reads) and `src/pages.ts` (rendering), always read as an anonymous caller:
16
+ - every item of [public pages](../../handbook/guides/public-pages.md): head tags, Open Graph, JSON-LD `Recipe`, the
17
+ short answer block, hreflang and `x-default`, per-language Markdown alternates, `llms.txt`, sitemap and robots.
18
+ - Cover images on R2: `wrangler.jsonc` (the shell's file plus the `MEDIA` bucket and two vars), `src/site-config.ts`
19
+ (media purpose `cover`) and `src/media.ts` (`r2MediaStorage`). See "Cover images" below to switch it on.
20
+
21
+ Search for `AGENT:` for what an app must still provide: layout and styling, cache policy, list paging, the S3 signer.
22
+
23
+ ## Combine with
24
+
25
+ - Need members to see or edit only their own rows? Add club-events' `restrict: { recipes: "author_id = auth.uid()" }` on a View.
26
+ - Need review before publishing, or an AI that approves? Add purchasing's guarded status transitions and admin MCP.
27
+
28
+ ## Run it
29
+
30
+ This runs the example by itself, to read and try. For your own app, run `mantle init` and take only the patterns you need into its manifests and `src/` (`mantle help examples`).
31
+
32
+ ```sh
33
+ npx mantle init my-app
34
+ cp -R node_modules/@aotter/mantle/docs/examples/recipes/. my-app/ # run the example as is
35
+ cd my-app && pnpm install && pnpm generate && pnpm types && pnpm check
36
+ ```
37
+
38
+ Then follow the shell README (`db:init`, `dev`). Sign in at `/admin`, add a recipe, publish it, open `/zh-TW/`.
39
+ Add a translation from the recipe's language tab and publish it to get `/en/`.
40
+
41
+ ## Cover images
42
+
43
+ The media library needs the generated site module, which the shell does not generate. To switch it on:
44
+
45
+ 1. In `package.json`, add `--site src/site-config.ts` to both the `generate` and `check` commands, then
46
+ `pnpm generate` (this adds the `site_config`/`media_assets` tables to `generated/schema.sql`).
47
+ 2. In `src/service.ts`, import `site` from `../generated/site.js` and `coverStorage` from `./media.js`, and add
48
+ `site, media: coverStorage(env, signer)` to `adminOptions`. The signer is app-owned (see `src/media.ts`).
49
+ 3. Create the bucket, set CORS and the two `vars` in `wrangler.jsonc` (see `docs/handbook/cloudflare/media-r2.md`).
50
+
51
+ Admin's media library then uploads straight to R2 with presigned URLs; copy a committed image's URL into `cover_url`.
52
+ The smoke does not cover upload: local `wrangler dev` has an R2 binding but no S3 endpoint to presign against.
53
+
54
+ ## `pnpm smoke` proves
55
+
56
+ - Drafts (zh-TW and en) are absent from detail, `.md`, list and `llms.txt`, even when the request has an admin session.
57
+ - A translation cannot publish before its parent; publishing both through the Admin API makes them visible.
58
+ - Each locale's detail page has title, description, canonical, hreflang (`zh-TW`, `en`, `x-default`) and a switcher link.
59
+ - JSON-LD parses as a `Recipe` with the recipe name; the en page inherits `totalTime` from its parent; the short answer is present.
60
+ - `.md` returns `text/markdown`; `llms.txt` and both locale lists show the published recipe; `/` redirects to `/zh-TW/`.