@aotter/mantle 0.2.0-alpha.6 → 0.2.0-alpha.7

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 (942) hide show
  1. package/README.md +152 -45
  2. package/dist/auth/adapter.d.ts +32 -0
  3. package/dist/auth/adapter.js +285 -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 +154 -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 +31 -0
  13. package/dist/auth/oauth.js +124 -0
  14. package/dist/auth/resource-seeds.js +10 -0
  15. package/dist/auth/routes.d.ts +32 -0
  16. package/dist/auth/{createAuthRoutes.js → routes.js} +130 -35
  17. package/dist/build/compiler.d.ts +790 -0
  18. package/dist/build/compiler.js +6971 -0
  19. package/dist/{spec → build/spec}/domain/model/ManifestGrammar.d.ts +24 -42
  20. package/dist/{spec → build/spec}/domain/model/RuntimePlan.d.ts +77 -3
  21. package/dist/{spec → build/spec}/domain/model/SqlIr.d.ts +35 -6
  22. package/dist/build/spec/domain/model/UserFields.d.ts +20 -0
  23. package/dist/{spec → build/spec}/domain/service/PlanFingerprint.d.ts +2 -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 +55 -0
  29. package/dist/domain/describe/chunk-ZIQVLA7X.js +363 -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 +17 -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 +204 -0
  42. package/dist/domain/recipes.js +19 -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 +84 -0
  50. package/dist/infrastructure/cli.js +101 -0
  51. package/dist/infrastructure/generate.d.ts +14 -0
  52. package/dist/infrastructure/generate.js +174 -0
  53. package/dist/infrastructure/generateAuth.d.ts +8 -0
  54. package/dist/infrastructure/generateAuth.js +17 -0
  55. package/dist/infrastructure/inputs.js +36 -0
  56. package/dist/infrastructure/wrangler.js +71 -0
  57. package/dist/kernel/authorization.d.ts +4 -0
  58. package/dist/kernel/authorization.js +16 -0
  59. package/dist/kernel/binds.d.ts +51 -0
  60. package/dist/kernel/binds.js +45 -0
  61. package/dist/kernel/caller-identity.d.ts +24 -0
  62. package/dist/kernel/caller-identity.js +22 -0
  63. package/dist/kernel/contract-error.d.ts +12 -0
  64. package/dist/kernel/contract-error.js +14 -0
  65. package/dist/kernel/contracts.d.ts +139 -0
  66. package/dist/kernel/contracts.js +92 -0
  67. package/dist/{spec/domain/service/CloudflareCron.d.ts → kernel/cron.d.ts} +0 -1
  68. package/dist/{spec/domain/service/CloudflareCron.js → kernel/cron.js} +1 -6
  69. package/dist/kernel/dialect/common.d.ts +39 -0
  70. package/dist/kernel/dialect/common.js +6 -0
  71. package/dist/kernel/dialect/postgres.d.ts +16 -0
  72. package/dist/kernel/dialect/postgres.js +18 -0
  73. package/dist/kernel/dialect/sqlite.d.ts +20 -0
  74. package/dist/kernel/dialect/sqlite.js +34 -0
  75. package/dist/kernel/driver/bun-sqlite.d.ts +19 -0
  76. package/dist/kernel/driver/bun-sqlite.js +77 -0
  77. package/dist/kernel/driver/d1.d.ts +24 -0
  78. package/dist/kernel/driver/d1.js +22 -0
  79. package/dist/kernel/driver/postgres.d.ts +25 -0
  80. package/dist/kernel/driver/postgres.js +95 -0
  81. package/dist/kernel/driver/transaction-context.d.ts +10 -0
  82. package/dist/kernel/driver/transaction-context.js +29 -0
  83. package/dist/kernel/email.d.ts +28 -0
  84. package/dist/{auth/emailTemplates.js → kernel/email.js} +13 -2
  85. package/dist/kernel/host-access.d.ts +10 -0
  86. package/dist/kernel/host-access.js +65 -0
  87. package/dist/kernel/host-caller.d.ts +38 -0
  88. package/dist/kernel/host-caller.js +55 -0
  89. package/dist/kernel/json-body.d.ts +4 -0
  90. package/dist/kernel/json-body.js +44 -0
  91. package/dist/kernel/mcp-apps.d.ts +49 -0
  92. package/dist/kernel/mcp-apps.js +40 -0
  93. package/dist/kernel/mcp-call.d.ts +5 -0
  94. package/dist/kernel/mcp-call.js +25 -0
  95. package/dist/kernel/mcp-handler.js +288 -0
  96. package/dist/kernel/mcp.d.ts +101 -0
  97. package/dist/kernel/mcp.js +25 -0
  98. package/dist/kernel/media.d.ts +27 -0
  99. package/dist/kernel/media.js +216 -0
  100. package/dist/kernel/paging.d.ts +55 -0
  101. package/dist/kernel/paging.js +95 -0
  102. package/dist/kernel/publishing.d.ts +4 -0
  103. package/dist/kernel/publishing.js +16 -0
  104. package/dist/{cloudflare/r2Media.d.ts → kernel/r2-media.d.ts} +4 -5
  105. package/dist/kernel/r2-media.js +78 -0
  106. package/dist/kernel/rest-dispatch.js +38 -0
  107. package/dist/kernel/rest-router.js +63 -0
  108. package/dist/kernel/rest.d.ts +58 -0
  109. package/dist/kernel/rest.js +41 -0
  110. package/dist/kernel/scalars.d.ts +6 -0
  111. package/dist/kernel/scalars.js +43 -0
  112. package/dist/kernel/search.js +5 -0
  113. package/dist/{core/site.d.ts → kernel/site-types.d.ts} +38 -9
  114. package/dist/kernel/site-types.js +1 -0
  115. package/dist/kernel/site.d.ts +41 -0
  116. package/dist/kernel/site.js +32 -0
  117. package/dist/kernel/store.d.ts +165 -0
  118. package/dist/kernel/store.js +257 -0
  119. package/dist/kernel/surface.d.ts +10 -0
  120. package/dist/kernel/surface.js +19 -0
  121. package/dist/kernel/sweep.d.ts +17 -0
  122. package/dist/kernel/sweep.js +27 -0
  123. package/dist/kernel/translations.d.ts +1 -0
  124. package/dist/kernel/translations.js +13 -0
  125. package/dist/kernel/user-caller.d.ts +31 -0
  126. package/dist/kernel/user-caller.js +67 -0
  127. package/dist/kernel/user-field-name.js +3 -0
  128. package/dist/kernel/user-fields.d.ts +34 -0
  129. package/dist/kernel/user-fields.js +71 -0
  130. package/dist/kernel/validation.d.ts +15 -0
  131. package/dist/kernel/validation.js +54 -0
  132. package/dist/kernel/web-gateway.js +68 -0
  133. package/dist/kernel/web-head.js +15 -0
  134. package/dist/kernel/web-response.js +8 -0
  135. package/dist/kernel/web.d.ts +75 -0
  136. package/dist/kernel/web.js +155 -0
  137. package/dist/kernel/where.js +27 -0
  138. package/dist/testing/index.d.ts +1 -37
  139. package/dist/testing/index.js +1 -43
  140. package/dist/usecase/adminBindings.js +142 -0
  141. package/dist/usecase/adminDescriptors.js +153 -0
  142. package/dist/usecase/generate.js +164 -0
  143. package/dist/usecase/handlers.js +21 -0
  144. package/dist/usecase/mcp-media.js +17 -0
  145. package/dist/usecase/mcp.js +96 -0
  146. package/dist/usecase/paging.js +72 -0
  147. package/dist/usecase/publishing.js +63 -0
  148. package/dist/usecase/refusals.js +27 -0
  149. package/dist/usecase/rest.js +54 -0
  150. package/dist/usecase/search.js +7 -0
  151. package/dist/usecase/site.js +40 -0
  152. package/dist/usecase/stores.js +62 -0
  153. package/dist/usecase/translations.js +78 -0
  154. package/dist/usecase/ttl.js +20 -0
  155. package/dist/usecase/validation.js +132 -0
  156. package/dist/usecase/web.js +22 -0
  157. package/docs/examples/README.md +31 -16
  158. package/docs/examples/app-shell/README.md +48 -0
  159. package/docs/examples/app-shell/package.json +34 -0
  160. package/docs/examples/app-shell/pnpm-workspace.yaml +8 -0
  161. package/docs/examples/app-shell/smoke-kit.mjs +100 -0
  162. package/docs/examples/app-shell/src/admin.ts +10 -0
  163. package/docs/examples/app-shell/src/auth.ts +55 -0
  164. package/docs/examples/app-shell/src/handlers.ts +5 -0
  165. package/docs/examples/app-shell/src/index.ts +4 -0
  166. package/docs/examples/app-shell/src/routes.ts +13 -0
  167. package/docs/examples/app-shell/src/schedule.ts +8 -0
  168. package/docs/examples/app-shell/src/service.ts +70 -0
  169. package/docs/examples/app-shell/src/sign-in.ts +41 -0
  170. package/docs/examples/app-shell/src/signup.ts +8 -0
  171. package/docs/examples/{reference-service → app-shell}/tsconfig.json +2 -1
  172. package/docs/examples/app-shell/worker-configuration.d.ts +22 -0
  173. package/docs/examples/app-shell/wrangler.jsonc +61 -0
  174. package/docs/examples/club-events/README.md +60 -0
  175. package/docs/examples/club-events/manifests/club-events.yaml +225 -0
  176. package/docs/examples/club-events/smoke.mjs +121 -0
  177. package/docs/examples/club-events/src/handlers.ts +25 -0
  178. package/docs/examples/club-events/src/signup.ts +9 -0
  179. package/docs/examples/generated-web/README.md +74 -0
  180. package/docs/examples/generated-web/posts.yaml +38 -0
  181. package/docs/examples/generated-web/seed.sql +6 -0
  182. package/docs/examples/generated-web/src/worker.ts +43 -0
  183. package/docs/examples/generated-web/tsconfig.json +8 -0
  184. package/docs/examples/generated-web/wrangler.jsonc +7 -0
  185. package/docs/examples/purchasing/.dev.vars.example +6 -0
  186. package/docs/examples/purchasing/README.md +90 -0
  187. package/docs/examples/purchasing/manifests/purchasing.yaml +341 -0
  188. package/docs/examples/purchasing/seed.sql +4 -0
  189. package/docs/examples/purchasing/smoke.mjs +171 -0
  190. package/docs/examples/purchasing/src/admin.ts +12 -0
  191. package/docs/examples/purchasing/src/handlers.ts +33 -0
  192. package/docs/examples/purchasing/src/notify.ts +29 -0
  193. package/docs/examples/purchasing/src/requests-page.ts +57 -0
  194. package/docs/examples/purchasing/src/routes.ts +53 -0
  195. package/docs/examples/purchasing/src/schedule.ts +31 -0
  196. package/docs/examples/purchasing/src/signup.ts +5 -0
  197. package/docs/examples/purchasing/src/supplier.ts +32 -0
  198. package/docs/examples/purchasing/wrangler.jsonc +68 -0
  199. package/docs/examples/recipes/README.md +58 -0
  200. package/docs/examples/recipes/manifests/recipes.yaml +75 -0
  201. package/docs/examples/recipes/smoke.mjs +102 -0
  202. package/docs/examples/recipes/src/media.ts +13 -0
  203. package/docs/examples/recipes/src/pages.ts +99 -0
  204. package/docs/examples/recipes/src/routes.ts +71 -0
  205. package/docs/examples/recipes/src/site-config.ts +12 -0
  206. package/docs/examples/recipes/wrangler.jsonc +73 -0
  207. package/docs/handbook/cloudflare/authentication.md +142 -212
  208. package/docs/handbook/cloudflare/chatgpt-sites.md +4 -113
  209. package/docs/handbook/cloudflare/deploy-and-operate.md +20 -106
  210. package/docs/handbook/cloudflare/media-r2.md +62 -133
  211. package/docs/handbook/cloudflare/service-entry.md +180 -116
  212. package/docs/handbook/concepts/authorization.md +10 -123
  213. package/docs/handbook/concepts/four-atoms.md +9 -92
  214. package/docs/handbook/concepts/lifecycle-and-locales.md +9 -80
  215. package/docs/handbook/concepts/mcp-and-agents.md +10 -141
  216. package/docs/handbook/concepts/procedures-and-triggers.md +5 -162
  217. package/docs/handbook/concepts/runtime-and-adapters.md +15 -224
  218. package/docs/handbook/concepts/views.md +10 -109
  219. package/docs/handbook/examples/hub.md +2 -18
  220. package/docs/handbook/guides/adapter-guide.md +11 -0
  221. package/docs/handbook/guides/admin-extensions.md +12 -183
  222. package/docs/handbook/guides/admin-ui.md +5 -164
  223. package/docs/handbook/guides/agent-setup.md +5 -18
  224. package/docs/handbook/guides/email.md +86 -0
  225. package/docs/handbook/guides/mcp-apps.md +44 -0
  226. package/docs/handbook/guides/native-admin.md +298 -0
  227. package/docs/handbook/guides/native-execution.md +132 -0
  228. package/docs/handbook/guides/oauth.md +105 -0
  229. package/docs/handbook/guides/postgres-adapters.md +56 -0
  230. package/docs/handbook/guides/public-pages.md +53 -0
  231. package/docs/handbook/guides/size-and-startup.md +9 -0
  232. package/docs/handbook/guides/typed-queries.md +39 -102
  233. package/docs/handbook/navigation.json +164 -31
  234. package/docs/handbook/reference/admin-descriptors.md +88 -0
  235. package/docs/handbook/reference/authorization.md +44 -52
  236. package/docs/handbook/reference/describe.md +56 -0
  237. package/docs/handbook/reference/diagnostics.md +15 -79
  238. package/docs/handbook/reference/features.md +45 -53
  239. package/docs/handbook/reference/manifest.md +6 -69
  240. package/docs/handbook/reference/procedure.md +9 -118
  241. package/docs/handbook/reference/schema.md +75 -185
  242. package/docs/handbook/reference/site-config.md +5 -65
  243. package/docs/handbook/reference/surface.md +279 -97
  244. package/docs/handbook/reference/trigger.md +24 -93
  245. package/docs/handbook/reference/view.md +32 -160
  246. package/docs/handbook/releases/index.md +48 -13
  247. package/docs/handbook/start/overview.md +9 -56
  248. package/docs/handbook/start/project-and-cli.md +40 -149
  249. package/docs/handbook/start/quickstart-worker.md +38 -116
  250. package/docs/skills/develop/SKILL.md +34 -123
  251. package/docs/skills/develop/mcp-app.md +37 -77
  252. package/docs/skills/integrate/SKILL.md +42 -84
  253. package/docs/skills/media-gc/SKILL.md +14 -3
  254. package/docs/skills/provision/SKILL.md +25 -91
  255. package/docs/skills/update/SKILL.md +19 -43
  256. package/docs/source/scripts/postgres-adapter.ts +101 -0
  257. package/docs/upgrade-0.1-to-0.2.md +25 -9
  258. package/package.json +96 -112
  259. package/skills/.keep +0 -0
  260. package/skills/README.md +72 -0
  261. package/skills/mantle/SKILL.md +135 -0
  262. package/skills/mantle/references/local-testing.md +11 -0
  263. package/skills/mantle/references/web.md +41 -0
  264. package/skills/mantle/references/website.md +47 -0
  265. package/dist/admin/consent.d.ts +0 -17
  266. package/dist/admin/consent.d.ts.map +0 -1
  267. package/dist/admin/consent.js +0 -17
  268. package/dist/admin/consent.js.map +0 -1
  269. package/dist/admin/createAdminSurface.d.ts +0 -25
  270. package/dist/admin/createAdminSurface.d.ts.map +0 -1
  271. package/dist/admin/createAdminSurface.js +0 -635
  272. package/dist/admin/createAdminSurface.js.map +0 -1
  273. package/dist/admin/design.d.ts +0 -7
  274. package/dist/admin/design.d.ts.map +0 -1
  275. package/dist/admin/design.js +0 -18
  276. package/dist/admin/design.js.map +0 -1
  277. package/dist/admin/developerConsole.d.ts +0 -267
  278. package/dist/admin/developerConsole.d.ts.map +0 -1
  279. package/dist/admin/developerConsole.js +0 -136
  280. package/dist/admin/developerConsole.js.map +0 -1
  281. package/dist/admin/extensions.d.ts +0 -290
  282. package/dist/admin/extensions.d.ts.map +0 -1
  283. package/dist/admin/extensions.js +0 -532
  284. package/dist/admin/extensions.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 -7
  290. package/dist/admin/index.d.ts.map +0 -1
  291. package/dist/admin/index.js +0 -7
  292. package/dist/admin/index.js.map +0 -1
  293. package/dist/admin/procedureFlow.d.ts +0 -38
  294. package/dist/admin/procedureFlow.d.ts.map +0 -1
  295. package/dist/admin/procedureFlow.js +0 -188
  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 -4568
  306. package/dist/auth/buildAuth.d.ts.map +0 -1
  307. package/dist/auth/buildAuth.js +0 -312
  308. package/dist/auth/buildAuth.js.map +0 -1
  309. package/dist/auth/callerResolver.d.ts +0 -52
  310. package/dist/auth/callerResolver.d.ts.map +0 -1
  311. package/dist/auth/callerResolver.js +0 -52
  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.map +0 -1
  316. package/dist/auth/createMantleAuth.d.ts +0 -3
  317. package/dist/auth/createMantleAuth.d.ts.map +0 -1
  318. package/dist/auth/createMantleAuth.js +0 -504
  319. package/dist/auth/createMantleAuth.js.map +0 -1
  320. package/dist/auth/db.d.ts +0 -18
  321. package/dist/auth/db.d.ts.map +0 -1
  322. package/dist/auth/db.js +0 -27
  323. package/dist/auth/db.js.map +0 -1
  324. package/dist/auth/emailTemplates.d.ts +0 -7
  325. package/dist/auth/emailTemplates.d.ts.map +0 -1
  326. package/dist/auth/emailTemplates.js.map +0 -1
  327. package/dist/auth/index.d.ts +0 -9
  328. package/dist/auth/index.d.ts.map +0 -1
  329. package/dist/auth/index.js +0 -7
  330. package/dist/auth/index.js.map +0 -1
  331. package/dist/auth/methods.d.ts +0 -717
  332. package/dist/auth/methods.d.ts.map +0 -1
  333. package/dist/auth/methods.js +0 -321
  334. package/dist/auth/methods.js.map +0 -1
  335. package/dist/auth/oauthTokens.d.ts +0 -24
  336. package/dist/auth/oauthTokens.d.ts.map +0 -1
  337. package/dist/auth/oauthTokens.js +0 -146
  338. package/dist/auth/oauthTokens.js.map +0 -1
  339. package/dist/auth/setupIncomplete.d.ts +0 -16
  340. package/dist/auth/setupIncomplete.d.ts.map +0 -1
  341. package/dist/auth/setupIncomplete.js +0 -50
  342. package/dist/auth/setupIncomplete.js.map +0 -1
  343. package/dist/auth/types.d.ts +0 -440
  344. package/dist/auth/types.d.ts.map +0 -1
  345. package/dist/auth/types.js +0 -10
  346. package/dist/auth/types.js.map +0 -1
  347. package/dist/bun/index.d.ts +0 -21
  348. package/dist/bun/index.d.ts.map +0 -1
  349. package/dist/bun/index.js +0 -46
  350. package/dist/bun/index.js.map +0 -1
  351. package/dist/cli/emitModule.d.ts +0 -7
  352. package/dist/cli/emitModule.d.ts.map +0 -1
  353. package/dist/cli/emitModule.js +0 -127
  354. package/dist/cli/emitModule.js.map +0 -1
  355. package/dist/cli/generate.d.ts +0 -8
  356. package/dist/cli/generate.d.ts.map +0 -1
  357. package/dist/cli/generate.js +0 -391
  358. package/dist/cli/generate.js.map +0 -1
  359. package/dist/cli/main.d.ts +0 -3
  360. package/dist/cli/main.d.ts.map +0 -1
  361. package/dist/cli/main.js +0 -21
  362. package/dist/cli/main.js.map +0 -1
  363. package/dist/cli/preset.d.ts +0 -18
  364. package/dist/cli/preset.d.ts.map +0 -1
  365. package/dist/cli/preset.js +0 -405
  366. package/dist/cli/preset.js.map +0 -1
  367. package/dist/cloudflare/index.d.ts +0 -25
  368. package/dist/cloudflare/index.d.ts.map +0 -1
  369. package/dist/cloudflare/index.js +0 -16
  370. package/dist/cloudflare/index.js.map +0 -1
  371. package/dist/cloudflare/r2Media.d.ts.map +0 -1
  372. package/dist/cloudflare/r2Media.js +0 -69
  373. package/dist/cloudflare/r2Media.js.map +0 -1
  374. package/dist/core/caller.d.ts +0 -35
  375. package/dist/core/caller.d.ts.map +0 -1
  376. package/dist/core/caller.js +0 -3
  377. package/dist/core/caller.js.map +0 -1
  378. package/dist/core/createMantle.d.ts +0 -22
  379. package/dist/core/createMantle.d.ts.map +0 -1
  380. package/dist/core/createMantle.js +0 -117
  381. package/dist/core/createMantle.js.map +0 -1
  382. package/dist/core/dialect.d.ts +0 -53
  383. package/dist/core/dialect.d.ts.map +0 -1
  384. package/dist/core/dialect.js +0 -7
  385. package/dist/core/dialect.js.map +0 -1
  386. package/dist/core/driver.d.ts +0 -24
  387. package/dist/core/driver.d.ts.map +0 -1
  388. package/dist/core/driver.js +0 -2
  389. package/dist/core/driver.js.map +0 -1
  390. package/dist/core/email.d.ts +0 -17
  391. package/dist/core/email.d.ts.map +0 -1
  392. package/dist/core/email.js +0 -2
  393. package/dist/core/email.js.map +0 -1
  394. package/dist/core/index.d.ts +0 -16
  395. package/dist/core/index.d.ts.map +0 -1
  396. package/dist/core/index.js +0 -16
  397. package/dist/core/index.js.map +0 -1
  398. package/dist/core/invocation.d.ts +0 -66
  399. package/dist/core/invocation.d.ts.map +0 -1
  400. package/dist/core/invocation.js +0 -3
  401. package/dist/core/invocation.js.map +0 -1
  402. package/dist/core/observation.d.ts +0 -3
  403. package/dist/core/observation.d.ts.map +0 -1
  404. package/dist/core/observation.js +0 -10
  405. package/dist/core/observation.js.map +0 -1
  406. package/dist/core/runtime/auth.d.ts +0 -9
  407. package/dist/core/runtime/auth.d.ts.map +0 -1
  408. package/dist/core/runtime/auth.js +0 -39
  409. package/dist/core/runtime/auth.js.map +0 -1
  410. package/dist/core/runtime/createRuntime.d.ts +0 -23
  411. package/dist/core/runtime/createRuntime.d.ts.map +0 -1
  412. package/dist/core/runtime/createRuntime.js +0 -214
  413. package/dist/core/runtime/createRuntime.js.map +0 -1
  414. package/dist/core/runtime/verifyPlan.d.ts +0 -34
  415. package/dist/core/runtime/verifyPlan.d.ts.map +0 -1
  416. package/dist/core/runtime/verifyPlan.js +0 -301
  417. package/dist/core/runtime/verifyPlan.js.map +0 -1
  418. package/dist/core/service.d.ts +0 -45
  419. package/dist/core/service.d.ts.map +0 -1
  420. package/dist/core/service.js +0 -2
  421. package/dist/core/service.js.map +0 -1
  422. package/dist/core/sessionUser.d.ts +0 -14
  423. package/dist/core/sessionUser.d.ts.map +0 -1
  424. package/dist/core/sessionUser.js +0 -4
  425. package/dist/core/sessionUser.js.map +0 -1
  426. package/dist/core/site.d.ts.map +0 -1
  427. package/dist/core/site.js +0 -2
  428. package/dist/core/site.js.map +0 -1
  429. package/dist/core/siteConfig.d.ts +0 -5
  430. package/dist/core/siteConfig.d.ts.map +0 -1
  431. package/dist/core/siteConfig.js +0 -24
  432. package/dist/core/siteConfig.js.map +0 -1
  433. package/dist/core/sql/allowlist.d.ts +0 -67
  434. package/dist/core/sql/allowlist.d.ts.map +0 -1
  435. package/dist/core/sql/allowlist.js +0 -737
  436. package/dist/core/sql/allowlist.js.map +0 -1
  437. package/dist/core/sql/ast.d.ts +0 -24
  438. package/dist/core/sql/ast.d.ts.map +0 -1
  439. package/dist/core/sql/ast.js +0 -32
  440. package/dist/core/sql/ast.js.map +0 -1
  441. package/dist/core/sql/compile.d.ts +0 -50
  442. package/dist/core/sql/compile.d.ts.map +0 -1
  443. package/dist/core/sql/compile.js +0 -48
  444. package/dist/core/sql/compile.js.map +0 -1
  445. package/dist/core/sql/policy.d.ts +0 -123
  446. package/dist/core/sql/policy.d.ts.map +0 -1
  447. package/dist/core/sql/policy.js +0 -398
  448. package/dist/core/sql/policy.js.map +0 -1
  449. package/dist/core/sql/positions.d.ts +0 -30
  450. package/dist/core/sql/positions.d.ts.map +0 -1
  451. package/dist/core/sql/positions.js +0 -22
  452. package/dist/core/sql/positions.js.map +0 -1
  453. package/dist/core/sql/run.d.ts +0 -68
  454. package/dist/core/sql/run.d.ts.map +0 -1
  455. package/dist/core/sql/run.js +0 -384
  456. package/dist/core/sql/run.js.map +0 -1
  457. package/dist/core/store/createStore.d.ts +0 -49
  458. package/dist/core/store/createStore.d.ts.map +0 -1
  459. package/dist/core/store/createStore.js +0 -227
  460. package/dist/core/store/createStore.js.map +0 -1
  461. package/dist/core/store/cursor.d.ts +0 -4
  462. package/dist/core/store/cursor.d.ts.map +0 -1
  463. package/dist/core/store/cursor.js +0 -22
  464. package/dist/core/store/cursor.js.map +0 -1
  465. package/dist/core/store/json.d.ts +0 -80
  466. package/dist/core/store/json.d.ts.map +0 -1
  467. package/dist/core/store/json.js +0 -342
  468. package/dist/core/store/json.js.map +0 -1
  469. package/dist/core/store.d.ts +0 -160
  470. package/dist/core/store.d.ts.map +0 -1
  471. package/dist/core/store.js +0 -2
  472. package/dist/core/store.js.map +0 -1
  473. package/dist/core/wire.d.ts +0 -20
  474. package/dist/core/wire.d.ts.map +0 -1
  475. package/dist/core/wire.js +0 -84
  476. package/dist/core/wire.js.map +0 -1
  477. package/dist/core/withCaller.d.ts +0 -24
  478. package/dist/core/withCaller.d.ts.map +0 -1
  479. package/dist/core/withCaller.js +0 -35
  480. package/dist/core/withCaller.js.map +0 -1
  481. package/dist/d1/adapter.d.ts +0 -12
  482. package/dist/d1/adapter.d.ts.map +0 -1
  483. package/dist/d1/adapter.js +0 -23
  484. package/dist/d1/adapter.js.map +0 -1
  485. package/dist/d1/codec.d.ts +0 -14
  486. package/dist/d1/codec.d.ts.map +0 -1
  487. package/dist/d1/codec.js +0 -136
  488. package/dist/d1/codec.js.map +0 -1
  489. package/dist/d1/compile/index.d.ts +0 -9
  490. package/dist/d1/compile/index.d.ts.map +0 -1
  491. package/dist/d1/compile/index.js +0 -9
  492. package/dist/d1/compile/index.js.map +0 -1
  493. package/dist/d1/dialect.d.ts +0 -4
  494. package/dist/d1/dialect.d.ts.map +0 -1
  495. package/dist/d1/dialect.js +0 -13
  496. package/dist/d1/dialect.js.map +0 -1
  497. package/dist/d1/executor.d.ts +0 -15
  498. package/dist/d1/executor.d.ts.map +0 -1
  499. package/dist/d1/executor.js +0 -96
  500. package/dist/d1/executor.js.map +0 -1
  501. package/dist/d1/index.d.ts +0 -6
  502. package/dist/d1/index.d.ts.map +0 -1
  503. package/dist/d1/index.js +0 -6
  504. package/dist/d1/index.js.map +0 -1
  505. package/dist/d1/lower.d.ts +0 -8
  506. package/dist/d1/lower.d.ts.map +0 -1
  507. package/dist/d1/lower.js +0 -167
  508. package/dist/d1/lower.js.map +0 -1
  509. package/dist/d1/media.d.ts +0 -5
  510. package/dist/d1/media.d.ts.map +0 -1
  511. package/dist/d1/media.js +0 -157
  512. package/dist/d1/media.js.map +0 -1
  513. package/dist/d1/migrations.d.ts +0 -12
  514. package/dist/d1/migrations.d.ts.map +0 -1
  515. package/dist/d1/migrations.js +0 -111
  516. package/dist/d1/migrations.js.map +0 -1
  517. package/dist/d1/print.d.ts +0 -28
  518. package/dist/d1/print.d.ts.map +0 -1
  519. package/dist/d1/print.js +0 -54
  520. package/dist/d1/print.js.map +0 -1
  521. package/dist/d1/site.d.ts +0 -17
  522. package/dist/d1/site.d.ts.map +0 -1
  523. package/dist/d1/site.js +0 -63
  524. package/dist/d1/site.js.map +0 -1
  525. package/dist/d1/storage.d.ts +0 -34
  526. package/dist/d1/storage.d.ts.map +0 -1
  527. package/dist/d1/storage.js +0 -288
  528. package/dist/d1/storage.js.map +0 -1
  529. package/dist/d1/tz.d.ts +0 -21
  530. package/dist/d1/tz.d.ts.map +0 -1
  531. package/dist/d1/tz.js +0 -54
  532. package/dist/d1/tz.js.map +0 -1
  533. package/dist/d1/validator.d.ts +0 -10
  534. package/dist/d1/validator.d.ts.map +0 -1
  535. package/dist/d1/validator.js +0 -33
  536. package/dist/d1/validator.js.map +0 -1
  537. package/dist/mcp/apps.d.ts +0 -43
  538. package/dist/mcp/apps.d.ts.map +0 -1
  539. package/dist/mcp/apps.js +0 -57
  540. package/dist/mcp/apps.js.map +0 -1
  541. package/dist/mcp/catalog.d.ts +0 -55
  542. package/dist/mcp/catalog.d.ts.map +0 -1
  543. package/dist/mcp/catalog.js +0 -81
  544. package/dist/mcp/catalog.js.map +0 -1
  545. package/dist/mcp/createMcpSurface.d.ts +0 -60
  546. package/dist/mcp/createMcpSurface.d.ts.map +0 -1
  547. package/dist/mcp/createMcpSurface.js +0 -182
  548. package/dist/mcp/createMcpSurface.js.map +0 -1
  549. package/dist/mcp/index.d.ts +0 -4
  550. package/dist/mcp/index.d.ts.map +0 -1
  551. package/dist/mcp/index.js +0 -4
  552. package/dist/mcp/index.js.map +0 -1
  553. package/dist/mcp/media.d.ts +0 -9
  554. package/dist/mcp/media.d.ts.map +0 -1
  555. package/dist/mcp/media.js +0 -33
  556. package/dist/mcp/media.js.map +0 -1
  557. package/dist/postgres/adapter.d.ts +0 -23
  558. package/dist/postgres/adapter.d.ts.map +0 -1
  559. package/dist/postgres/adapter.js +0 -50
  560. package/dist/postgres/adapter.js.map +0 -1
  561. package/dist/postgres/codec.d.ts +0 -13
  562. package/dist/postgres/codec.d.ts.map +0 -1
  563. package/dist/postgres/codec.js +0 -110
  564. package/dist/postgres/codec.js.map +0 -1
  565. package/dist/postgres/compile/index.d.ts +0 -9
  566. package/dist/postgres/compile/index.d.ts.map +0 -1
  567. package/dist/postgres/compile/index.js +0 -9
  568. package/dist/postgres/compile/index.js.map +0 -1
  569. package/dist/postgres/driver.d.ts +0 -109
  570. package/dist/postgres/driver.d.ts.map +0 -1
  571. package/dist/postgres/driver.js +0 -186
  572. package/dist/postgres/driver.js.map +0 -1
  573. package/dist/postgres/executor.d.ts +0 -28
  574. package/dist/postgres/executor.d.ts.map +0 -1
  575. package/dist/postgres/executor.js +0 -96
  576. package/dist/postgres/executor.js.map +0 -1
  577. package/dist/postgres/index.d.ts +0 -8
  578. package/dist/postgres/index.d.ts.map +0 -1
  579. package/dist/postgres/index.js +0 -8
  580. package/dist/postgres/index.js.map +0 -1
  581. package/dist/postgres/lower.d.ts +0 -6
  582. package/dist/postgres/lower.d.ts.map +0 -1
  583. package/dist/postgres/lower.js +0 -152
  584. package/dist/postgres/lower.js.map +0 -1
  585. package/dist/postgres/print.d.ts +0 -11
  586. package/dist/postgres/print.d.ts.map +0 -1
  587. package/dist/postgres/print.js +0 -108
  588. package/dist/postgres/print.js.map +0 -1
  589. package/dist/postgres/session.d.ts +0 -9
  590. package/dist/postgres/session.d.ts.map +0 -1
  591. package/dist/postgres/session.js +0 -111
  592. package/dist/postgres/session.js.map +0 -1
  593. package/dist/postgres/storage.d.ts +0 -32
  594. package/dist/postgres/storage.d.ts.map +0 -1
  595. package/dist/postgres/storage.js +0 -283
  596. package/dist/postgres/storage.js.map +0 -1
  597. package/dist/postgres/validator.d.ts +0 -13
  598. package/dist/postgres/validator.d.ts.map +0 -1
  599. package/dist/postgres/validator.js +0 -53
  600. package/dist/postgres/validator.js.map +0 -1
  601. package/dist/spec/domain/index.d.ts +0 -3
  602. package/dist/spec/domain/index.d.ts.map +0 -1
  603. package/dist/spec/domain/index.js +0 -3
  604. package/dist/spec/domain/index.js.map +0 -1
  605. package/dist/spec/domain/model/ContentState.d.ts +0 -8
  606. package/dist/spec/domain/model/ContentState.d.ts.map +0 -1
  607. package/dist/spec/domain/model/ContentState.js +0 -7
  608. package/dist/spec/domain/model/ContentState.js.map +0 -1
  609. package/dist/spec/domain/model/Entry.d.ts +0 -29
  610. package/dist/spec/domain/model/Entry.d.ts.map +0 -1
  611. package/dist/spec/domain/model/Entry.js +0 -16
  612. package/dist/spec/domain/model/Entry.js.map +0 -1
  613. package/dist/spec/domain/model/Locale.d.ts +0 -30
  614. package/dist/spec/domain/model/Locale.d.ts.map +0 -1
  615. package/dist/spec/domain/model/Locale.js +0 -33
  616. package/dist/spec/domain/model/Locale.js.map +0 -1
  617. package/dist/spec/domain/model/ManifestGrammar.d.ts.map +0 -1
  618. package/dist/spec/domain/model/ManifestGrammar.js +0 -153
  619. package/dist/spec/domain/model/ManifestGrammar.js.map +0 -1
  620. package/dist/spec/domain/model/MediaMimeAccept.d.ts +0 -55
  621. package/dist/spec/domain/model/MediaMimeAccept.d.ts.map +0 -1
  622. package/dist/spec/domain/model/MediaMimeAccept.js +0 -90
  623. package/dist/spec/domain/model/MediaMimeAccept.js.map +0 -1
  624. package/dist/spec/domain/model/RuntimePlan.d.ts.map +0 -1
  625. package/dist/spec/domain/model/RuntimePlan.js +0 -4
  626. package/dist/spec/domain/model/RuntimePlan.js.map +0 -1
  627. package/dist/spec/domain/model/SiteConfig.d.ts +0 -173
  628. package/dist/spec/domain/model/SiteConfig.d.ts.map +0 -1
  629. package/dist/spec/domain/model/SiteConfig.js +0 -13
  630. package/dist/spec/domain/model/SiteConfig.js.map +0 -1
  631. package/dist/spec/domain/model/SqlIr.d.ts.map +0 -1
  632. package/dist/spec/domain/model/SqlIr.js +0 -95
  633. package/dist/spec/domain/model/SqlIr.js.map +0 -1
  634. package/dist/spec/domain/model/index.d.ts +0 -14
  635. package/dist/spec/domain/model/index.d.ts.map +0 -1
  636. package/dist/spec/domain/model/index.js +0 -14
  637. package/dist/spec/domain/model/index.js.map +0 -1
  638. package/dist/spec/domain/service/CloudflareCron.d.ts.map +0 -1
  639. package/dist/spec/domain/service/CloudflareCron.js.map +0 -1
  640. package/dist/spec/domain/service/CrossSchemaChecker.d.ts +0 -34
  641. package/dist/spec/domain/service/CrossSchemaChecker.d.ts.map +0 -1
  642. package/dist/spec/domain/service/CrossSchemaChecker.js +0 -114
  643. package/dist/spec/domain/service/CrossSchemaChecker.js.map +0 -1
  644. package/dist/spec/domain/service/EntryDataValidator.d.ts +0 -56
  645. package/dist/spec/domain/service/EntryDataValidator.d.ts.map +0 -1
  646. package/dist/spec/domain/service/EntryDataValidator.js +0 -88
  647. package/dist/spec/domain/service/EntryDataValidator.js.map +0 -1
  648. package/dist/spec/domain/service/JsonSchemaToZod.d.ts +0 -29
  649. package/dist/spec/domain/service/JsonSchemaToZod.d.ts.map +0 -1
  650. package/dist/spec/domain/service/JsonSchemaToZod.js +0 -156
  651. package/dist/spec/domain/service/JsonSchemaToZod.js.map +0 -1
  652. package/dist/spec/domain/service/LifecycleStateMachine.d.ts +0 -75
  653. package/dist/spec/domain/service/LifecycleStateMachine.d.ts.map +0 -1
  654. package/dist/spec/domain/service/LifecycleStateMachine.js +0 -76
  655. package/dist/spec/domain/service/LifecycleStateMachine.js.map +0 -1
  656. package/dist/spec/domain/service/LocaleCanonicalizer.d.ts +0 -29
  657. package/dist/spec/domain/service/LocaleCanonicalizer.d.ts.map +0 -1
  658. package/dist/spec/domain/service/LocaleCanonicalizer.js +0 -96
  659. package/dist/spec/domain/service/LocaleCanonicalizer.js.map +0 -1
  660. package/dist/spec/domain/service/ManifestFieldChecks.d.ts +0 -60
  661. package/dist/spec/domain/service/ManifestFieldChecks.d.ts.map +0 -1
  662. package/dist/spec/domain/service/ManifestFieldChecks.js +0 -116
  663. package/dist/spec/domain/service/ManifestFieldChecks.js.map +0 -1
  664. package/dist/spec/domain/service/ManifestGraphValidator.d.ts +0 -15
  665. package/dist/spec/domain/service/ManifestGraphValidator.d.ts.map +0 -1
  666. package/dist/spec/domain/service/ManifestGraphValidator.js +0 -225
  667. package/dist/spec/domain/service/ManifestGraphValidator.js.map +0 -1
  668. package/dist/spec/domain/service/ManifestLinker.d.ts +0 -45
  669. package/dist/spec/domain/service/ManifestLinker.d.ts.map +0 -1
  670. package/dist/spec/domain/service/ManifestLinker.js +0 -92
  671. package/dist/spec/domain/service/ManifestLinker.js.map +0 -1
  672. package/dist/spec/domain/service/ManifestLocaleTrimmer.d.ts +0 -16
  673. package/dist/spec/domain/service/ManifestLocaleTrimmer.d.ts.map +0 -1
  674. package/dist/spec/domain/service/ManifestLocaleTrimmer.js +0 -76
  675. package/dist/spec/domain/service/ManifestLocaleTrimmer.js.map +0 -1
  676. package/dist/spec/domain/service/ManifestParser.d.ts +0 -49
  677. package/dist/spec/domain/service/ManifestParser.d.ts.map +0 -1
  678. package/dist/spec/domain/service/ManifestParser.js +0 -203
  679. package/dist/spec/domain/service/ManifestParser.js.map +0 -1
  680. package/dist/spec/domain/service/ManifestPartition.d.ts +0 -9
  681. package/dist/spec/domain/service/ManifestPartition.d.ts.map +0 -1
  682. package/dist/spec/domain/service/ManifestPartition.js +0 -19
  683. package/dist/spec/domain/service/ManifestPartition.js.map +0 -1
  684. package/dist/spec/domain/service/ManifestPathDiagnoser.d.ts +0 -33
  685. package/dist/spec/domain/service/ManifestPathDiagnoser.d.ts.map +0 -1
  686. package/dist/spec/domain/service/ManifestPathDiagnoser.js +0 -43
  687. package/dist/spec/domain/service/ManifestPathDiagnoser.js.map +0 -1
  688. package/dist/spec/domain/service/ManifestSourceSpans.d.ts +0 -10
  689. package/dist/spec/domain/service/ManifestSourceSpans.d.ts.map +0 -1
  690. package/dist/spec/domain/service/ManifestSourceSpans.js +0 -76
  691. package/dist/spec/domain/service/ManifestSourceSpans.js.map +0 -1
  692. package/dist/spec/domain/service/McpToolNaming.d.ts +0 -25
  693. package/dist/spec/domain/service/McpToolNaming.d.ts.map +0 -1
  694. package/dist/spec/domain/service/McpToolNaming.js +0 -50
  695. package/dist/spec/domain/service/McpToolNaming.js.map +0 -1
  696. package/dist/spec/domain/service/PlanFingerprint.d.ts.map +0 -1
  697. package/dist/spec/domain/service/PlanFingerprint.js +0 -14
  698. package/dist/spec/domain/service/PlanFingerprint.js.map +0 -1
  699. package/dist/spec/domain/service/ProcedureViewSpecChecks.d.ts +0 -4
  700. package/dist/spec/domain/service/ProcedureViewSpecChecks.d.ts.map +0 -1
  701. package/dist/spec/domain/service/ProcedureViewSpecChecks.js +0 -211
  702. package/dist/spec/domain/service/ProcedureViewSpecChecks.js.map +0 -1
  703. package/dist/spec/domain/service/SchemaAdminUiChecker.d.ts +0 -44
  704. package/dist/spec/domain/service/SchemaAdminUiChecker.d.ts.map +0 -1
  705. package/dist/spec/domain/service/SchemaAdminUiChecker.js +0 -333
  706. package/dist/spec/domain/service/SchemaAdminUiChecker.js.map +0 -1
  707. package/dist/spec/domain/service/SchemaIndexChecker.d.ts +0 -33
  708. package/dist/spec/domain/service/SchemaIndexChecker.d.ts.map +0 -1
  709. package/dist/spec/domain/service/SchemaIndexChecker.js +0 -256
  710. package/dist/spec/domain/service/SchemaIndexChecker.js.map +0 -1
  711. package/dist/spec/domain/service/SchemaSearchChecker.d.ts +0 -11
  712. package/dist/spec/domain/service/SchemaSearchChecker.d.ts.map +0 -1
  713. package/dist/spec/domain/service/SchemaSearchChecker.js +0 -78
  714. package/dist/spec/domain/service/SchemaSearchChecker.js.map +0 -1
  715. package/dist/spec/domain/service/SchemaSpecChecks.d.ts +0 -31
  716. package/dist/spec/domain/service/SchemaSpecChecks.d.ts.map +0 -1
  717. package/dist/spec/domain/service/SchemaSpecChecks.js +0 -445
  718. package/dist/spec/domain/service/SchemaSpecChecks.js.map +0 -1
  719. package/dist/spec/domain/service/SiteDefaultsValidator.d.ts +0 -39
  720. package/dist/spec/domain/service/SiteDefaultsValidator.d.ts.map +0 -1
  721. package/dist/spec/domain/service/SiteDefaultsValidator.js +0 -194
  722. package/dist/spec/domain/service/SiteDefaultsValidator.js.map +0 -1
  723. package/dist/spec/domain/service/SqlClassify.d.ts +0 -19
  724. package/dist/spec/domain/service/SqlClassify.d.ts.map +0 -1
  725. package/dist/spec/domain/service/SqlClassify.js +0 -42
  726. package/dist/spec/domain/service/SqlClassify.js.map +0 -1
  727. package/dist/spec/domain/service/SqlRefusal.d.ts +0 -16
  728. package/dist/spec/domain/service/SqlRefusal.d.ts.map +0 -1
  729. package/dist/spec/domain/service/SqlRefusal.js +0 -20
  730. package/dist/spec/domain/service/SqlRefusal.js.map +0 -1
  731. package/dist/spec/domain/service/SqlTypes.d.ts +0 -40
  732. package/dist/spec/domain/service/SqlTypes.d.ts.map +0 -1
  733. package/dist/spec/domain/service/SqlTypes.js +0 -110
  734. package/dist/spec/domain/service/SqlTypes.js.map +0 -1
  735. package/dist/spec/domain/service/StaffRoleHierarchy.d.ts +0 -3
  736. package/dist/spec/domain/service/StaffRoleHierarchy.d.ts.map +0 -1
  737. package/dist/spec/domain/service/StaffRoleHierarchy.js +0 -19
  738. package/dist/spec/domain/service/StaffRoleHierarchy.js.map +0 -1
  739. package/dist/spec/domain/service/TriggerGraphChecks.d.ts +0 -16
  740. package/dist/spec/domain/service/TriggerGraphChecks.d.ts.map +0 -1
  741. package/dist/spec/domain/service/TriggerGraphChecks.js +0 -337
  742. package/dist/spec/domain/service/TriggerGraphChecks.js.map +0 -1
  743. package/dist/spec/domain/service/TriggerSpecChecks.d.ts +0 -4
  744. package/dist/spec/domain/service/TriggerSpecChecks.d.ts.map +0 -1
  745. package/dist/spec/domain/service/TriggerSpecChecks.js +0 -89
  746. package/dist/spec/domain/service/TriggerSpecChecks.js.map +0 -1
  747. package/dist/spec/domain/service/ValidatedAdminUiProjection.d.ts +0 -11
  748. package/dist/spec/domain/service/ValidatedAdminUiProjection.d.ts.map +0 -1
  749. package/dist/spec/domain/service/ValidatedAdminUiProjection.js +0 -26
  750. package/dist/spec/domain/service/ValidatedAdminUiProjection.js.map +0 -1
  751. package/dist/spec/domain/service/ViewAdminUiChecker.d.ts +0 -14
  752. package/dist/spec/domain/service/ViewAdminUiChecker.d.ts.map +0 -1
  753. package/dist/spec/domain/service/ViewAdminUiChecker.js +0 -58
  754. package/dist/spec/domain/service/ViewAdminUiChecker.js.map +0 -1
  755. package/dist/spec/domain/service/index.d.ts +0 -24
  756. package/dist/spec/domain/service/index.d.ts.map +0 -1
  757. package/dist/spec/domain/service/index.js +0 -24
  758. package/dist/spec/domain/service/index.js.map +0 -1
  759. package/dist/spec/index.d.ts +0 -10
  760. package/dist/spec/index.d.ts.map +0 -1
  761. package/dist/spec/index.js +0 -12
  762. package/dist/spec/index.js.map +0 -1
  763. package/dist/spec/infrastructure/cli/parseArgsError.d.ts +0 -16
  764. package/dist/spec/infrastructure/cli/parseArgsError.d.ts.map +0 -1
  765. package/dist/spec/infrastructure/cli/parseArgsError.js +0 -34
  766. package/dist/spec/infrastructure/cli/parseArgsError.js.map +0 -1
  767. package/dist/spec/infrastructure/index.d.ts +0 -2
  768. package/dist/spec/infrastructure/index.d.ts.map +0 -1
  769. package/dist/spec/infrastructure/index.js +0 -2
  770. package/dist/spec/infrastructure/index.js.map +0 -1
  771. package/dist/spec/infrastructure/sql/PgQueryParser.d.ts +0 -18
  772. package/dist/spec/infrastructure/sql/PgQueryParser.d.ts.map +0 -1
  773. package/dist/spec/infrastructure/sql/PgQueryParser.js +0 -17
  774. package/dist/spec/infrastructure/sql/PgQueryParser.js.map +0 -1
  775. package/dist/spec/infrastructure/sql/compilePlan.d.ts +0 -37
  776. package/dist/spec/infrastructure/sql/compilePlan.d.ts.map +0 -1
  777. package/dist/spec/infrastructure/sql/compilePlan.js +0 -287
  778. package/dist/spec/infrastructure/sql/compilePlan.js.map +0 -1
  779. package/dist/spec/infrastructure/sql/compileSql.d.ts +0 -35
  780. package/dist/spec/infrastructure/sql/compileSql.d.ts.map +0 -1
  781. package/dist/spec/infrastructure/sql/compileSql.js +0 -92
  782. package/dist/spec/infrastructure/sql/compileSql.js.map +0 -1
  783. package/dist/spec/infrastructure/sql/frontEnd.d.ts +0 -8
  784. package/dist/spec/infrastructure/sql/frontEnd.d.ts.map +0 -1
  785. package/dist/spec/infrastructure/sql/frontEnd.js +0 -100
  786. package/dist/spec/infrastructure/sql/frontEnd.js.map +0 -1
  787. package/dist/spec/infrastructure/sql/index.d.ts +0 -4
  788. package/dist/spec/infrastructure/sql/index.d.ts.map +0 -1
  789. package/dist/spec/infrastructure/sql/index.js +0 -4
  790. package/dist/spec/infrastructure/sql/index.js.map +0 -1
  791. package/dist/spec/kernel/diagnostic.d.ts.map +0 -1
  792. package/dist/spec/kernel/diagnostic.js +0 -301
  793. package/dist/spec/kernel/diagnostic.js.map +0 -1
  794. package/dist/spec/kernel/index.d.ts +0 -8
  795. package/dist/spec/kernel/index.d.ts.map +0 -1
  796. package/dist/spec/kernel/index.js +0 -8
  797. package/dist/spec/kernel/index.js.map +0 -1
  798. package/dist/spec/usecase/EmitTypes.d.ts +0 -22
  799. package/dist/spec/usecase/EmitTypes.d.ts.map +0 -1
  800. package/dist/spec/usecase/EmitTypes.js +0 -182
  801. package/dist/spec/usecase/EmitTypes.js.map +0 -1
  802. package/dist/spec/usecase/ValidateManifestsUseCase.d.ts +0 -8
  803. package/dist/spec/usecase/ValidateManifestsUseCase.d.ts.map +0 -1
  804. package/dist/spec/usecase/ValidateManifestsUseCase.js +0 -101
  805. package/dist/spec/usecase/ValidateManifestsUseCase.js.map +0 -1
  806. package/dist/spec/usecase/dto/ValidateManifestsRequest.d.ts +0 -27
  807. package/dist/spec/usecase/dto/ValidateManifestsRequest.d.ts.map +0 -1
  808. package/dist/spec/usecase/dto/ValidateManifestsRequest.js +0 -2
  809. package/dist/spec/usecase/dto/ValidateManifestsRequest.js.map +0 -1
  810. package/dist/spec/usecase/dto/ValidateManifestsResponse.d.ts +0 -14
  811. package/dist/spec/usecase/dto/ValidateManifestsResponse.d.ts.map +0 -1
  812. package/dist/spec/usecase/dto/ValidateManifestsResponse.js +0 -2
  813. package/dist/spec/usecase/dto/ValidateManifestsResponse.js.map +0 -1
  814. package/dist/spec/usecase/dto/index.d.ts +0 -3
  815. package/dist/spec/usecase/dto/index.d.ts.map +0 -1
  816. package/dist/spec/usecase/dto/index.js +0 -3
  817. package/dist/spec/usecase/dto/index.js.map +0 -1
  818. package/dist/spec/usecase/index.d.ts +0 -10
  819. package/dist/spec/usecase/index.d.ts.map +0 -1
  820. package/dist/spec/usecase/index.js +0 -10
  821. package/dist/spec/usecase/index.js.map +0 -1
  822. package/dist/testing/cases/before-hook.d.ts +0 -4
  823. package/dist/testing/cases/before-hook.d.ts.map +0 -1
  824. package/dist/testing/cases/before-hook.js +0 -71
  825. package/dist/testing/cases/before-hook.js.map +0 -1
  826. package/dist/testing/cases/corpus.d.ts +0 -14
  827. package/dist/testing/cases/corpus.d.ts.map +0 -1
  828. package/dist/testing/cases/corpus.js +0 -93
  829. package/dist/testing/cases/corpus.js.map +0 -1
  830. package/dist/testing/cases/policy.d.ts +0 -87
  831. package/dist/testing/cases/policy.d.ts.map +0 -1
  832. package/dist/testing/cases/policy.js +0 -143
  833. package/dist/testing/cases/policy.js.map +0 -1
  834. package/dist/testing/cases/printer.d.ts +0 -4
  835. package/dist/testing/cases/printer.d.ts.map +0 -1
  836. package/dist/testing/cases/printer.js +0 -34
  837. package/dist/testing/cases/printer.js.map +0 -1
  838. package/dist/testing/cases/report-view.d.ts +0 -5
  839. package/dist/testing/cases/report-view.d.ts.map +0 -1
  840. package/dist/testing/cases/report-view.js +0 -51
  841. package/dist/testing/cases/report-view.js.map +0 -1
  842. package/dist/testing/cases/requisition.d.ts +0 -4
  843. package/dist/testing/cases/requisition.d.ts.map +0 -1
  844. package/dist/testing/cases/requisition.js +0 -38
  845. package/dist/testing/cases/requisition.js.map +0 -1
  846. package/dist/testing/cases/search-places.d.ts +0 -4
  847. package/dist/testing/cases/search-places.d.ts.map +0 -1
  848. package/dist/testing/cases/search-places.js +0 -60
  849. package/dist/testing/cases/search-places.js.map +0 -1
  850. package/dist/testing/cases/stock.d.ts +0 -4
  851. package/dist/testing/cases/stock.d.ts.map +0 -1
  852. package/dist/testing/cases/stock.js +0 -35
  853. package/dist/testing/cases/stock.js.map +0 -1
  854. package/dist/testing/cases/store.d.ts +0 -4
  855. package/dist/testing/cases/store.d.ts.map +0 -1
  856. package/dist/testing/cases/store.js +0 -100
  857. package/dist/testing/cases/store.js.map +0 -1
  858. package/dist/testing/harness.d.ts +0 -94
  859. package/dist/testing/harness.d.ts.map +0 -1
  860. package/dist/testing/harness.js +0 -168
  861. package/dist/testing/harness.js.map +0 -1
  862. package/dist/testing/index.d.ts.map +0 -1
  863. package/dist/testing/index.js.map +0 -1
  864. package/dist/testing/report.d.ts +0 -15
  865. package/dist/testing/report.d.ts.map +0 -1
  866. package/dist/testing/report.js +0 -22
  867. package/dist/testing/report.js.map +0 -1
  868. package/dist/web/createRestSurface.d.ts +0 -7
  869. package/dist/web/createRestSurface.d.ts.map +0 -1
  870. package/dist/web/createRestSurface.js +0 -41
  871. package/dist/web/createRestSurface.js.map +0 -1
  872. package/dist/web/index.d.ts +0 -2
  873. package/dist/web/index.d.ts.map +0 -1
  874. package/dist/web/index.js +0 -2
  875. package/dist/web/index.js.map +0 -1
  876. package/docs/adr/0001-four-atom-manifest-model.md +0 -440
  877. package/docs/adr/0002-closed-enums-for-bindings.md +0 -253
  878. package/docs/adr/0007-ai-as-primary-author.md +0 -473
  879. package/docs/adr/0008-structured-diagnostic-shape.md +0 -338
  880. package/docs/adr/0009-consumer-supplied-manifests.md +0 -149
  881. package/docs/adr/0010-locale-and-translates.md +0 -453
  882. package/docs/adr/0011-adapter-port-spec.md +0 -281
  883. package/docs/adr/0012-views-as-public-rest.md +0 -161
  884. package/docs/adr/0013-agent-provisioned-consumer-projects.md +0 -154
  885. package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +0 -939
  886. package/docs/adr/0016-site-semantic-layer.md +0 -70
  887. package/docs/adr/0017-media-multi-variant-agent-side-optimization.md +0 -181
  888. package/docs/adr/0018-core-starters-repository-boundary.md +0 -179
  889. package/docs/adr/0019-sealed-manifest-runtime-pipeline.md +0 -244
  890. package/docs/adr/0020-builtin-handler-contracts-and-matched-upsert.md +0 -199
  891. package/docs/adr/0021-retire-starter-scaffolding.md +0 -30
  892. package/docs/adr/0022-caller-observed-version-occ.md +0 -212
  893. package/docs/adr/0023-port-failure-contract.md +0 -69
  894. package/docs/adr/0024-manifest-native-schema-tables.md +0 -264
  895. package/docs/adr/0025-reserved-native-columns-and-published-only-public-views.md +0 -105
  896. package/docs/adr/0026-progressive-generate.md +0 -36
  897. package/docs/adr/0027-scheduled-procedure-triggers.md +0 -31
  898. package/docs/adr/0028-schema-ttl-expiry.md +0 -33
  899. package/docs/adr/0029-mcp-apps-interaction-contracts.md +0 -324
  900. package/docs/adr/0030-store.md +0 -40
  901. package/docs/adr/0031-host-cloud-core-contract.md +0 -102
  902. package/docs/adr/0032-store-centric-core-and-service-contract.md +0 -384
  903. package/docs/adr/0033-storage-converges-to-the-plan.md +0 -92
  904. package/docs/adr/0034-store-is-authored-as-sql.md +0 -192
  905. package/docs/adr/0035-sql-dialects.md +0 -154
  906. package/docs/adr/0036-host-and-dialect.md +0 -65
  907. package/docs/adr/0037-postgresql-is-the-reference-dialect.md +0 -125
  908. package/docs/adr/0038-bun-native-host.md +0 -63
  909. package/docs/adr/0039-postgresql-is-native.md +0 -130
  910. package/docs/adr/README.md +0 -98
  911. package/docs/adr/adr-lite-1376-admin-extensions.md +0 -330
  912. package/docs/adr/adr-lite-803-request-diagnostics.md +0 -38
  913. package/docs/adr/adr-lite-808-route-readiness.md +0 -47
  914. package/docs/adr/adr-lite-809-bounded-public-content.md +0 -71
  915. package/docs/adr/adr-lite-812-native-parity.md +0 -150
  916. package/docs/adr/adr-lite-823-home-statistics.md +0 -64
  917. package/docs/adr/adr-lite-842-mcp-authoring.md +0 -30
  918. package/docs/adr/adr-lite-845-frontend-client.md +0 -38
  919. package/docs/adr/adr-lite-861-admin-webmcp.md +0 -48
  920. package/docs/adr/adr-lite-909-admin-ui-kit.md +0 -37
  921. package/docs/examples/commerce-inventory.md +0 -498
  922. package/docs/examples/commerce.md +0 -254
  923. package/docs/examples/guarded-api.md +0 -238
  924. package/docs/examples/intake-hooks.md +0 -237
  925. package/docs/examples/intake.md +0 -127
  926. package/docs/examples/legal-documents.md +0 -218
  927. package/docs/examples/procurement.md +0 -226
  928. package/docs/examples/publication.md +0 -211
  929. package/docs/examples/reference-service/.mantle/generated/mantle.ts +0 -174
  930. package/docs/examples/reference-service/.mantle/generated/plan.json +0 -1936
  931. package/docs/examples/reference-service/README.md +0 -33
  932. package/docs/examples/reference-service/manifests/catalog.yaml +0 -103
  933. package/docs/examples/reference-service/manifests/orders.yaml +0 -120
  934. package/docs/examples/reference-service/mantle.config.json +0 -1
  935. package/docs/examples/reference-service/package.json +0 -27
  936. package/docs/examples/reference-service/smoke.mjs +0 -160
  937. package/docs/examples/reference-service/src/handlers.ts +0 -36
  938. package/docs/examples/reference-service/src/index.ts +0 -33
  939. package/docs/examples/reference-service/src/service.ts +0 -78
  940. package/docs/examples/reference-service/wrangler.jsonc +0 -26
  941. package/docs/examples/reservation.md +0 -142
  942. /package/docs/examples/{reference-service → app-shell}/.dev.vars.example +0 -0
@@ -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,68 @@
1
+ {
2
+ "$schema": "node_modules/wrangler/config-schema.json",
3
+ "name": "mantle-app",
4
+ "main": "dist/index.js",
5
+ "compatibility_date": "2026-09-01",
6
+ "compatibility_flags": [
7
+ "nodejs_compat"
8
+ ],
9
+ "vars": {
10
+ "EMPLOYEE_DOMAIN": "example.com"
11
+ },
12
+ "secrets": {
13
+ "required": [
14
+ "BETTER_AUTH_SECRET",
15
+ "PUBLIC_ORIGIN",
16
+ "ADMIN_EMAIL",
17
+ "SUPPLIER_API_URL",
18
+ "SUPPLIER_WEBHOOK_SECRET"
19
+ ]
20
+ },
21
+ "observability": {
22
+ "enabled": true,
23
+ "logs": {
24
+ "enabled": true,
25
+ "head_sampling_rate": 1
26
+ },
27
+ "traces": {
28
+ "enabled": true,
29
+ "head_sampling_rate": 0.01
30
+ }
31
+ },
32
+ "d1_databases": [
33
+ {
34
+ "binding": "DB",
35
+ "database_name": "mantle-app"
36
+ }
37
+ ],
38
+ "assets": {
39
+ "directory": "node_modules/@aotter/mantle-admin/dist/admin",
40
+ "binding": "ASSETS",
41
+ "run_worker_first": true,
42
+ "html_handling": "none"
43
+ },
44
+ "triggers": {
45
+ "crons": [
46
+ "0 9 * * *"
47
+ ]
48
+ },
49
+ "no_bundle": true,
50
+ "find_additional_modules": true,
51
+ "base_dir": "dist",
52
+ "build": {
53
+ "command": "mantle bundle src/index.ts dist",
54
+ "watch_dir": [
55
+ "src",
56
+ "generated"
57
+ ]
58
+ },
59
+ "rules": [
60
+ {
61
+ "type": "ESModule",
62
+ "globs": [
63
+ "**/*.js"
64
+ ],
65
+ "fallthrough": true
66
+ }
67
+ ]
68
+ }
@@ -0,0 +1,58 @@
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
+ ## Copy
29
+
30
+ ```sh
31
+ cp -R node_modules/@aotter/mantle/docs/examples/app-shell/. my-app/
32
+ cp -R node_modules/@aotter/mantle/docs/examples/recipes/. my-app/
33
+ cd my-app && pnpm install && pnpm generate && pnpm types && pnpm check
34
+ ```
35
+
36
+ Then follow the shell README (`db:init`, `dev`). Sign in at `/admin`, add a recipe, publish it, open `/zh-TW/`.
37
+ Add a translation from the recipe's language tab and publish it to get `/en/`.
38
+
39
+ ## Cover images
40
+
41
+ The media library needs the generated site module, which the shell does not generate. To switch it on:
42
+
43
+ 1. In `package.json`, add `--site src/site-config.ts` to both the `generate` and `check` commands, then
44
+ `pnpm generate` (this adds the `site_config`/`media_assets` tables to `generated/schema.sql`).
45
+ 2. In `src/service.ts`, import `site` from `../generated/site.js` and `coverStorage` from `./media.js`, and add
46
+ `site, media: coverStorage(env, signer)` to `adminOptions`. The signer is app-owned (see `src/media.ts`).
47
+ 3. Create the bucket, set CORS and the two `vars` in `wrangler.jsonc` (see `docs/handbook/cloudflare/media-r2.md`).
48
+
49
+ Admin's media library then uploads straight to R2 with presigned URLs; copy a committed image's URL into `cover_url`.
50
+ The smoke does not cover upload: local `wrangler dev` has an R2 binding but no S3 endpoint to presign against.
51
+
52
+ ## `pnpm smoke` proves
53
+
54
+ - Drafts (zh-TW and en) are absent from detail, `.md`, list and `llms.txt`, even when the request has an admin session.
55
+ - A translation cannot publish before its parent; publishing both through the Admin API makes them visible.
56
+ - Each locale's detail page has title, description, canonical, hreflang (`zh-TW`, `en`, `x-default`) and a switcher link.
57
+ - JSON-LD parses as a `Recipe` with the recipe name; the en page inherits `totalTime` from its parent; the short answer is present.
58
+ - `.md` returns `text/markdown`; `llms.txt` and both locale lists show the published recipe; `/` redirects to `/zh-TW/`.
@@ -0,0 +1,75 @@
1
+ # Recipes. The parent row is the default locale (zh-TW); recipe_translations holds each other locale (en, ...).
2
+ # Both use the publishing lifecycle: Admin creates drafts, publish makes them visible to visitors.
3
+ apiVersion: cms.mantle.aotter.net/v2
4
+ kind: Schema
5
+ metadata: {name: recipes}
6
+ spec:
7
+ title: Recipes
8
+ lifecycle: publishing
9
+ uniqueIndexes: [[slug]]
10
+ searchableFields: [title]
11
+ schema:
12
+ type: object
13
+ required: [slug, title, summary, ingredients, steps, total_minutes, servings]
14
+ properties:
15
+ slug: {type: string, pattern: "^[a-z0-9-]+$", maxLength: 80}
16
+ title: {type: string}
17
+ summary: {type: string, description: "One or two sentences; used as the meta description."}
18
+ ingredients: {type: string, description: "One ingredient per line."}
19
+ steps: {type: string, description: "One step per line."}
20
+ total_minutes: {type: integer, minimum: 1}
21
+ servings: {type: integer, minimum: 1}
22
+ cover_url: {type: string, description: "Public URL of a committed cover image from the media library."}
23
+ uiSchema:
24
+ fields:
25
+ summary: {widget: textarea}
26
+ ingredients: {widget: textarea}
27
+ steps: {widget: textarea}
28
+ ---
29
+ apiVersion: cms.mantle.aotter.net/v2
30
+ kind: Schema
31
+ metadata: {name: recipe_translations}
32
+ spec:
33
+ title: Recipe translations
34
+ lifecycle: publishing
35
+ localized: true
36
+ # Shares `slug` with its parent; reads merge the parent's total_minutes, servings and cover_url.
37
+ # A translation publishes only while its parent is published.
38
+ translates: {parent: recipes, on: slug}
39
+ uniqueIndexes: [[slug, locale]]
40
+ schema:
41
+ type: object
42
+ required: [slug, locale, title, summary, ingredients, steps]
43
+ properties:
44
+ slug: {type: string, maxLength: 80}
45
+ locale: {type: string, description: "BCP 47 tag, e.g. en"}
46
+ title: {type: string}
47
+ summary: {type: string}
48
+ ingredients: {type: string}
49
+ steps: {type: string}
50
+ uiSchema:
51
+ fields:
52
+ summary: {widget: textarea}
53
+ ingredients: {widget: textarea}
54
+ steps: {widget: textarea}
55
+ ---
56
+ # Visitor list for the default locale. Public Views return published rows only.
57
+ apiVersion: cms.mantle.aotter.net/v2
58
+ kind: View
59
+ metadata: {name: published_recipes}
60
+ spec:
61
+ surface: public
62
+ sql: SELECT id, slug, title, summary FROM recipes ORDER BY id
63
+ ---
64
+ # Visitor list for one translated locale; a translation is hidden while its parent is unpublished.
65
+ apiVersion: cms.mantle.aotter.net/v2
66
+ kind: View
67
+ metadata: {name: published_translations}
68
+ spec:
69
+ surface: public
70
+ input:
71
+ type: object
72
+ required: [locale]
73
+ additionalProperties: false
74
+ properties: {locale: {type: string, minLength: 2, maxLength: 35}}
75
+ sql: SELECT id, slug, title, summary FROM recipe_translations WHERE locale = input.locale ORDER BY id
@@ -0,0 +1,102 @@
1
+ // Recipes acceptance: drafts stay hidden, publishing via the Admin API makes both locales visible with SEO, JSON-LD,
2
+ // hreflang, Markdown alternates, llms.txt, sitemap and robots.
3
+ import assert from "node:assert/strict";
4
+ import { smoke } from "./smoke-kit.mjs";
5
+
6
+ const recipe = {
7
+ slug: "scallion-pancake", title: "蔥油餅", summary: "外酥內軟的蔥油餅 </script>", total_minutes: 25, servings: 2,
8
+ ingredients: "中筋麵粉 200 克\n熱水 120 毫升\n青蔥 3 根", steps: "揉麵團並靜置\n擀開撒蔥捲起\n小火煎至金黃",
9
+ };
10
+ const english = {
11
+ slug: "scallion-pancake", locale: "en", title: "Scallion Pancake", summary: "Crisp outside, soft inside.",
12
+ ingredients: "200 g all-purpose flour\n120 ml hot water\n3 scallions", steps: "Knead and rest the dough\nRoll, add scallions, coil\nPan-fry until golden",
13
+ };
14
+ const jsonLd = html => JSON.parse(/<script type="application\/ld\+json">(.*?)<\/script>/s.exec(html)[1]);
15
+
16
+ await smoke(async ({ origin, owner, call, signIn, step }) => {
17
+ const admin = await signIn(owner);
18
+ const create = async (collection, data) => {
19
+ const created = await call("POST", "/admin/api/entries", { cookie: admin, body: { collection, data } });
20
+ assert.equal(created.status, 200, JSON.stringify(created.body));
21
+ return created.body.entry;
22
+ };
23
+ const publish = (collection, entry) => call("POST", `/admin/api/entries/${entry.id}/publish?collection=${collection}`, { cookie: admin, body: { expectedVersion: entry.version } });
24
+ const parent = await create("recipes", recipe), translation = await create("recipe_translations", english);
25
+ step("admin creates a zh-TW recipe and its en translation as drafts");
26
+
27
+ for (const path of ["/zh-TW/recipes/scallion-pancake", "/zh-TW/recipes/scallion-pancake.md", "/en/recipes/scallion-pancake"]) {
28
+ assert.equal((await call("GET", path)).status, 404, path);
29
+ }
30
+ // Visitor pages read as anonymous even when the request carries an admin session.
31
+ assert.equal((await call("GET", "/zh-TW/recipes/scallion-pancake", { cookie: admin })).status, 404);
32
+ assert.ok(!(await call("GET", "/zh-TW/")).body.includes("scallion-pancake"));
33
+ assert.ok(!(await call("GET", "/llms.txt")).body.includes("scallion-pancake"));
34
+ assert.ok(!(await call("GET", "/sitemap.xml")).body.includes("scallion-pancake"));
35
+ step("drafts are invisible to visitors: detail, Markdown, list, llms.txt and sitemap");
36
+
37
+ assert.equal((await publish("recipe_translations", translation)).status, 409);
38
+ const published = await publish("recipes", parent);
39
+ assert.equal(published.status, 200, JSON.stringify(published.body));
40
+ assert.equal((await call("GET", "/en/recipes/scallion-pancake")).status, 404);
41
+ assert.equal((await publish("recipe_translations", translation)).status, 200);
42
+ step("a translation cannot publish before its parent; publishing both via the Admin API");
43
+
44
+ const zh = await call("GET", "/zh-TW/recipes/scallion-pancake");
45
+ assert.equal(zh.status, 200);
46
+ assert.match(zh.headers.get("content-type"), /text\/html/);
47
+ const html = zh.body;
48
+ assert.match(html, /<html lang="zh-TW">/);
49
+ assert.match(html, /<title>蔥油餅 \| 食譜<\/title>/);
50
+ assert.match(html, /<meta name="description" content="外酥內軟的蔥油餅 &lt;\/script&gt;">/);
51
+ assert.ok(html.includes(`<link rel="canonical" href="${origin}/zh-TW/recipes/scallion-pancake">`));
52
+ assert.ok(html.includes(`<link rel="alternate" type="text/markdown" href="${origin}/zh-TW/recipes/scallion-pancake.md">`));
53
+ step("zh-TW detail has title, description and canonical");
54
+
55
+ for (const [lang, path] of [["zh-TW", "/zh-TW"], ["en", "/en"], ["x-default", "/zh-TW"]]) {
56
+ assert.ok(html.includes(`<link rel="alternate" hreflang="${lang}" href="${origin}${path}/recipes/scallion-pancake">`), lang);
57
+ }
58
+ assert.ok(html.includes(`<a href="/en/recipes/scallion-pancake" hreflang="en" lang="en">English</a>`));
59
+ const en = await call("GET", "/en/recipes/scallion-pancake");
60
+ assert.equal(en.status, 200);
61
+ assert.match(en.body, /<html lang="en">/);
62
+ assert.ok(en.body.includes(`<link rel="canonical" href="${origin}/en/recipes/scallion-pancake">`));
63
+ assert.ok(en.body.includes(`<link rel="alternate" hreflang="zh-TW" href="${origin}/zh-TW/recipes/scallion-pancake">`));
64
+ assert.ok(en.body.includes(`<a href="/zh-TW/recipes/scallion-pancake" hreflang="zh-TW" lang="zh-TW">繁體中文</a>`));
65
+ step("hreflang alternates and the locale switcher link zh-TW and en");
66
+
67
+ const zhData = jsonLd(html), enData = jsonLd(en.body);
68
+ assert.equal(zhData["@type"], "Recipe");
69
+ assert.equal(zhData.name, "蔥油餅");
70
+ assert.equal(zhData.description, recipe.summary);
71
+ assert.deepEqual(zhData.recipeIngredient, ["中筋麵粉 200 克", "熱水 120 毫升", "青蔥 3 根"]);
72
+ assert.equal(enData.name, "Scallion Pancake");
73
+ assert.equal(enData.inLanguage, "en");
74
+ assert.equal(enData.totalTime, "PT25M"); // merged from the parent row
75
+ assert.match(zhData.datePublished, /^\d{4}-/); assert.match(zhData.dateModified, /^\d{4}-/);
76
+ assert.ok(en.body.includes(`<meta property="og:url" content="${origin}/en/recipes/scallion-pancake">`));
77
+ assert.ok(en.body.includes(`<meta property="og:type" content="article">`));
78
+ assert.ok(en.body.includes(`<meta property="og:locale" content="en">`) && en.body.includes(`<meta property="og:locale:alternate" content="zh_TW">`));
79
+ assert.match(en.body, /<section id="quick-answer"><h2>How long does it take\?<\/h2><p>About 25 minutes in total; serves 2.<\/p><\/section>/);
80
+ step("JSON-LD Recipe parses with name and dates; Open Graph url/type/locale; the short answer block is present");
81
+
82
+ const md = await call("GET", "/en/recipes/scallion-pancake.md");
83
+ assert.equal(md.status, 200);
84
+ assert.match(md.headers.get("content-type"), /^text\/markdown/);
85
+ assert.ok(md.body.startsWith("# Scallion Pancake\n"));
86
+ assert.ok(md.body.includes("- 200 g all-purpose flour") && md.body.includes("1. Knead and rest the dough"));
87
+ step(".md alternate returns Markdown");
88
+
89
+ const llms = await call("GET", "/llms.txt");
90
+ assert.ok(llms.body.includes(`- [蔥油餅](${origin}/zh-TW/recipes/scallion-pancake.md)`));
91
+ assert.ok(llms.body.includes(`- [Scallion Pancake](${origin}/en/recipes/scallion-pancake.md)`));
92
+ assert.ok((await call("GET", "/en/")).body.includes(`<a href="/en/recipes/scallion-pancake">Scallion Pancake</a>`));
93
+ const home = await fetch(`${origin}/`, { redirect: "manual" });
94
+ assert.equal(home.status, 302);
95
+ assert.equal(home.headers.get("location"), `${origin}/zh-TW/`);
96
+ const sitemap = await call("GET", "/sitemap.xml");
97
+ assert.match(sitemap.headers.get("content-type"), /^application\/xml/);
98
+ for (const path of ["/zh-TW/", "/en/", "/zh-TW/recipes/scallion-pancake", "/en/recipes/scallion-pancake"]) assert.ok(sitemap.body.includes(`<loc>${origin}${path}</loc>`), path);
99
+ const robots = await call("GET", "/robots.txt");
100
+ assert.ok(robots.body.includes("Disallow: /admin/") && robots.body.includes(`Sitemap: ${origin}/sitemap.xml`));
101
+ step("llms.txt, sitemap (both locales), robots and the locale lists show the published recipe; / redirects to the default locale");
102
+ });
@@ -0,0 +1,13 @@
1
+ import { r2MediaStorage, type S3Signer } from "@aotter/mantle/media/r2";
2
+
3
+ /**
4
+ * Cover-image storage on the MEDIA R2 binding (docs/handbook/cloudflare/media-r2.md). Pass the result as Admin's
5
+ * `media` option (with `site` from generated/site.ts); the Admin media library then creates an upload, the browser
6
+ * PUTs the bytes straight to R2 with a presigned URL, and commit copies them under `committed/`.
7
+ * Copy the committed asset's public URL into a recipe's `cover_url`.
8
+ * AGENT: supply a SigV4 signer that returns query-signed PUT URLs, e.g. `new AwsClient({ accessKeyId, secretAccessKey })`
9
+ * from aws4fetch with R2 S3 credentials kept as secrets. Configure bucket CORS for PUT from your origin.
10
+ */
11
+ export function coverStorage(env: Env, signer: S3Signer) {
12
+ return r2MediaStorage({ bucket: env.MEDIA, signer, endpoint: env.MEDIA_S3_ENDPOINT, publicBase: env.MEDIA_PUBLIC_BASE });
13
+ }
@@ -0,0 +1,99 @@
1
+ /** Dependency-free visitor HTML, Markdown and llms.txt for recipes. */
2
+ export const LOCALES = ["zh-TW", "en"] as const;
3
+ export type Locale = (typeof LOCALES)[number];
4
+ export const DEFAULT_LOCALE: Locale = "zh-TW";
5
+
6
+ export interface Recipe {
7
+ slug: string; locale: Locale; title: string; summary: string; ingredients: string; steps: string;
8
+ total_minutes: number; servings: number; cover_url?: string | null; published_at?: string | null; updated_at?: string | null;
9
+ }
10
+ export interface Listed { slug: string; title: string; summary: string }
11
+
12
+ // AGENT: move UI strings to your translation workflow when you add locales.
13
+ const text = {
14
+ "zh-TW": { site: "食譜", description: "家常食譜,中英對照。", how: "需要多久?", answer: (m: number, s: number) => `總共約 ${m} 分鐘,可供 ${s} 人份。`, ingredients: "材料", steps: "步驟", all: "所有食譜", name: "繁體中文" },
15
+ en: { site: "Recipes", description: "Home recipes in Traditional Chinese and English.", how: "How long does it take?", answer: (m: number, s: number) => `About ${m} minutes in total; serves ${s}.`, ingredients: "Ingredients", steps: "Steps", all: "All recipes", name: "English" },
16
+ } satisfies Record<Locale, unknown>;
17
+
18
+ const escape = (value: unknown) => String(value ?? "").replace(/[&<>"']/g, c => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[c]!));
19
+ const lines = (value: string) => value.split("\n").map(line => line.trim()).filter(Boolean);
20
+ const recipePath = (locale: Locale, slug: string) => `/${locale}/recipes/${slug}`;
21
+
22
+ /** hreflang alternates for every locale the page exists in, plus x-default (the default locale). */
23
+ function alternates(origin: string, locales: readonly Locale[], path: (locale: Locale) => string) {
24
+ return locales.map(l => `<link rel="alternate" hreflang="${l}" href="${origin}${path(l)}">`).join("")
25
+ + `<link rel="alternate" hreflang="x-default" href="${origin}${path(DEFAULT_LOCALE)}">`;
26
+ }
27
+
28
+ /** Open Graph: og:locale uses underscores (zh_TW); the other locales the page exists in are alternates. */
29
+ function openGraph(type: "website" | "article", url: string, locale: Locale, locales: readonly Locale[], title: string, description: string) {
30
+ const og = (property: string, content: string) => `<meta property="og:${property}" content="${escape(content)}">`;
31
+ return og("type", type) + og("url", url) + og("site_name", text[locale].site) + og("title", title) + og("description", description)
32
+ + og("locale", locale.replace("-", "_")) + locales.filter(l => l !== locale).map(l => og("locale:alternate", l.replace("-", "_"))).join("");
33
+ }
34
+
35
+ function documentHtml(locale: Locale, head: string, body: string) {
36
+ // AGENT: real layout and styling (stylesheet, header, footer, images sizes); keep the head tags.
37
+ return `<!doctype html><html lang="${locale}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">${head}</head><body>${body}</body></html>`;
38
+ }
39
+
40
+ function switcher(locales: readonly Locale[], path: (locale: Locale) => string, current: Locale) {
41
+ return `<nav aria-label="Language">${locales.map(l => l === current ? `<strong>${text[l].name}</strong>` : `<a href="${path(l)}" hreflang="${l}" lang="${l}">${text[l].name}</a>`).join(" | ")}</nav>`;
42
+ }
43
+
44
+ export function listHtml(origin: string, locale: Locale, rows: readonly Listed[]) {
45
+ const t = text[locale], path = (l: Locale) => `/${l}/`;
46
+ const head = `<title>${escape(t.site)}</title><meta name="description" content="${escape(t.description)}">`
47
+ + `<link rel="canonical" href="${origin}${path(locale)}">${alternates(origin, LOCALES, path)}`
48
+ + openGraph("website", `${origin}${path(locale)}`, locale, LOCALES, t.site, t.description);
49
+ const items = rows.map(r => `<li><a href="${recipePath(locale, r.slug)}">${escape(r.title)}</a><p>${escape(r.summary)}</p></li>`).join("");
50
+ return documentHtml(locale, head, `${switcher(LOCALES, path, locale)}<h1>${escape(t.site)}</h1><ul>${items}</ul>`);
51
+ }
52
+
53
+ export function recipeHtml(origin: string, locale: Locale, r: Recipe, locales: readonly Locale[]) {
54
+ const t = text[locale], path = (l: Locale) => recipePath(l, r.slug), canonical = `${origin}${path(locale)}`;
55
+ const jsonLd = {
56
+ "@context": "https://schema.org", "@type": "Recipe", name: r.title, description: r.summary, inLanguage: locale, url: canonical,
57
+ ...(r.cover_url ? { image: [r.cover_url] } : {}), ...(r.published_at ? { datePublished: r.published_at } : {}), ...(r.updated_at ? { dateModified: r.updated_at } : {}),
58
+ totalTime: `PT${r.total_minutes}M`, recipeYield: String(r.servings),
59
+ recipeIngredient: lines(r.ingredients),
60
+ recipeInstructions: lines(r.steps).map(step => ({ "@type": "HowToStep", text: step })),
61
+ };
62
+ const head = `<title>${escape(r.title)} | ${escape(t.site)}</title><meta name="description" content="${escape(r.summary)}">`
63
+ + `<link rel="canonical" href="${canonical}">${alternates(origin, locales, path)}`
64
+ + `<link rel="alternate" type="text/markdown" href="${canonical}.md">`
65
+ + openGraph("article", canonical, locale, locales, r.title, r.summary)
66
+ + (r.cover_url ? `<meta property="og:image" content="${escape(r.cover_url)}">` : "")
67
+ // `<` is escaped so admin-authored text cannot close the script element.
68
+ + `<script type="application/ld+json">${JSON.stringify(jsonLd).replace(/</g, "\\u003c")}</script>`;
69
+ const body = `${switcher(locales, path, locale)}<p><a href="/${locale}/">${escape(t.all)}</a></p>`
70
+ + `<article><h1>${escape(r.title)}</h1><p>${escape(r.summary)}</p>`
71
+ + (r.cover_url ? `<img src="${escape(r.cover_url)}" alt="${escape(r.title)}">` : "")
72
+ // Short answer block: one question and a one-sentence answer that search and answer engines can quote.
73
+ + `<section id="quick-answer"><h2>${escape(t.how)}</h2><p>${escape(t.answer(r.total_minutes, r.servings))}</p></section>`
74
+ + `<h2>${escape(t.ingredients)}</h2><ul>${lines(r.ingredients).map(i => `<li>${escape(i)}</li>`).join("")}</ul>`
75
+ + `<h2>${escape(t.steps)}</h2><ol>${lines(r.steps).map(s => `<li>${escape(s)}</li>`).join("")}</ol></article>`;
76
+ return documentHtml(locale, head, body);
77
+ }
78
+
79
+ export function recipeMarkdown(r: Recipe) {
80
+ const t = text[r.locale];
81
+ return [`# ${r.title}`, "", r.summary, "", `## ${t.how}`, "", t.answer(r.total_minutes, r.servings), "",
82
+ `## ${t.ingredients}`, "", ...lines(r.ingredients).map(i => `- ${i}`), "",
83
+ `## ${t.steps}`, "", ...lines(r.steps).map((s, i) => `${i + 1}. ${s}`), ""].join("\n");
84
+ }
85
+
86
+ /** llms.txt (https://llmstxt.org): one section per locale, each recipe linking to its Markdown alternate. */
87
+ export function llmsTxt(origin: string, lists: readonly (readonly [Locale, readonly Listed[]])[]) {
88
+ return [`# ${text.en.site}`, "", `> ${text.en.description}`, "",
89
+ ...lists.flatMap(([locale, rows]) => [`## ${text[locale].name} (${locale})`, "",
90
+ ...rows.map(r => `- [${r.title}](${origin}${recipePath(locale, r.slug)}.md): ${r.summary.replace(/\s+/g, " ")}`), ""])].join("\n");
91
+ }
92
+
93
+ /** sitemap.xml: each locale's list page and every published recipe in it (only published rows reach `lists`). */
94
+ export function sitemapXml(origin: string, lists: readonly (readonly [Locale, readonly Listed[]])[]) {
95
+ const paths = lists.flatMap(([locale, rows]) => [`/${locale}/`, ...rows.map(r => recipePath(locale, r.slug))]);
96
+ return `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">${paths.map(path => `<url><loc>${escape(origin + path)}</loc></url>`).join("")}</urlset>\n`;
97
+ }
98
+
99
+ export const robotsTxt = (origin: string) => `User-agent: *\nDisallow: /admin/\nSitemap: ${origin}/sitemap.xml\n`;