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

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 (946) hide show
  1. package/README.md +157 -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 +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 +225 -0
  17. package/dist/build/compiler.d.ts +712 -0
  18. package/dist/build/compiler.js +6896 -0
  19. package/dist/{spec → build/spec}/domain/model/ManifestGrammar.d.ts +30 -47
  20. package/dist/{spec → build/spec}/domain/model/RuntimePlan.d.ts +77 -3
  21. package/dist/{spec → build/spec}/domain/model/SqlIr.d.ts +37 -6
  22. package/dist/build/spec/domain/model/UserFields.d.ts +20 -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 +55 -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 +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 +174 -0
  50. package/dist/infrastructure/cli.js +97 -0
  51. package/dist/infrastructure/generate.d.ts +12 -0
  52. package/dist/infrastructure/generate.js +174 -0
  53. package/dist/infrastructure/generateAuth.d.ts +8 -0
  54. package/dist/infrastructure/generateAuth.js +23 -0
  55. package/dist/infrastructure/inputs.js +41 -0
  56. package/dist/infrastructure/wrangler.js +94 -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/drain.d.ts +5 -0
  76. package/dist/kernel/drain.js +35 -0
  77. package/dist/kernel/driver/bun-sqlite.d.ts +19 -0
  78. package/dist/kernel/driver/bun-sqlite.js +77 -0
  79. package/dist/kernel/driver/d1.d.ts +24 -0
  80. package/dist/kernel/driver/d1.js +22 -0
  81. package/dist/kernel/driver/postgres.d.ts +25 -0
  82. package/dist/kernel/driver/postgres.js +98 -0
  83. package/dist/kernel/driver/transaction-context.d.ts +10 -0
  84. package/dist/kernel/driver/transaction-context.js +29 -0
  85. package/dist/kernel/email.d.ts +28 -0
  86. package/dist/{auth/emailTemplates.js → kernel/email.js} +13 -2
  87. package/dist/kernel/host-access.d.ts +10 -0
  88. package/dist/kernel/host-access.js +65 -0
  89. package/dist/kernel/host-caller.d.ts +39 -0
  90. package/dist/kernel/host-caller.js +56 -0
  91. package/dist/kernel/json-body.d.ts +4 -0
  92. package/dist/kernel/json-body.js +44 -0
  93. package/dist/kernel/mcp-apps.d.ts +69 -0
  94. package/dist/kernel/mcp-apps.js +40 -0
  95. package/dist/kernel/mcp-call.d.ts +5 -0
  96. package/dist/kernel/mcp-call.js +25 -0
  97. package/dist/kernel/mcp-handler.js +371 -0
  98. package/dist/kernel/mcp.d.ts +106 -0
  99. package/dist/kernel/mcp.js +49 -0
  100. package/dist/kernel/media.d.ts +27 -0
  101. package/dist/kernel/media.js +216 -0
  102. package/dist/kernel/paging.d.ts +55 -0
  103. package/dist/kernel/paging.js +95 -0
  104. package/dist/kernel/publishing.d.ts +4 -0
  105. package/dist/kernel/publishing.js +16 -0
  106. package/dist/{cloudflare/r2Media.d.ts → kernel/r2-media.d.ts} +4 -5
  107. package/dist/kernel/r2-media.js +78 -0
  108. package/dist/kernel/rest-dispatch.js +38 -0
  109. package/dist/kernel/rest-router.js +64 -0
  110. package/dist/kernel/rest.d.ts +58 -0
  111. package/dist/kernel/rest.js +41 -0
  112. package/dist/kernel/scalars.d.ts +6 -0
  113. package/dist/kernel/scalars.js +43 -0
  114. package/dist/kernel/search.js +5 -0
  115. package/dist/{core/site.d.ts → kernel/site-types.d.ts} +38 -9
  116. package/dist/kernel/site-types.js +1 -0
  117. package/dist/kernel/site.d.ts +39 -0
  118. package/dist/kernel/site.js +32 -0
  119. package/dist/kernel/store.d.ts +165 -0
  120. package/dist/kernel/store.js +257 -0
  121. package/dist/kernel/surface.d.ts +10 -0
  122. package/dist/kernel/surface.js +19 -0
  123. package/dist/kernel/sweep.d.ts +17 -0
  124. package/dist/kernel/sweep.js +27 -0
  125. package/dist/kernel/translations.d.ts +1 -0
  126. package/dist/kernel/translations.js +13 -0
  127. package/dist/kernel/user-caller.d.ts +31 -0
  128. package/dist/kernel/user-caller.js +67 -0
  129. package/dist/kernel/user-field-name.js +3 -0
  130. package/dist/kernel/user-fields.d.ts +34 -0
  131. package/dist/kernel/user-fields.js +71 -0
  132. package/dist/kernel/validation.d.ts +15 -0
  133. package/dist/kernel/validation.js +54 -0
  134. package/dist/kernel/web-gateway.js +68 -0
  135. package/dist/kernel/web-head.js +15 -0
  136. package/dist/kernel/web-response.js +8 -0
  137. package/dist/kernel/web.d.ts +77 -0
  138. package/dist/kernel/web.js +158 -0
  139. package/dist/kernel/where.js +27 -0
  140. package/dist/testing/index.d.ts +1 -37
  141. package/dist/testing/index.js +1 -43
  142. package/dist/usecase/adminBindings.js +141 -0
  143. package/dist/usecase/adminClient.js +87 -0
  144. package/dist/usecase/adminDescriptors.js +107 -0
  145. package/dist/usecase/generate.js +165 -0
  146. package/dist/usecase/handlers.js +21 -0
  147. package/dist/usecase/mcp-media.js +17 -0
  148. package/dist/usecase/mcp.js +100 -0
  149. package/dist/usecase/paging.js +71 -0
  150. package/dist/usecase/publishing.js +63 -0
  151. package/dist/usecase/refusals.js +27 -0
  152. package/dist/usecase/rest.js +54 -0
  153. package/dist/usecase/search.js +7 -0
  154. package/dist/usecase/site.js +40 -0
  155. package/dist/usecase/stores.js +62 -0
  156. package/dist/usecase/tool-version.js +9 -0
  157. package/dist/usecase/translations.js +78 -0
  158. package/dist/usecase/ttl.js +20 -0
  159. package/dist/usecase/validation.js +233 -0
  160. package/dist/usecase/web.js +23 -0
  161. package/docs/examples/README.md +31 -16
  162. package/docs/examples/app-shell/README.md +86 -0
  163. package/docs/examples/app-shell/package.json +42 -0
  164. package/docs/examples/app-shell/pnpm-workspace.yaml +8 -0
  165. package/docs/examples/app-shell/smoke-kit.mjs +154 -0
  166. package/docs/examples/app-shell/src/auth.ts +57 -0
  167. package/docs/examples/app-shell/src/handlers.ts +5 -0
  168. package/docs/examples/app-shell/src/index.ts +4 -0
  169. package/docs/examples/app-shell/src/routes.ts +13 -0
  170. package/docs/examples/app-shell/src/schedule.ts +8 -0
  171. package/docs/examples/app-shell/src/service.ts +70 -0
  172. package/docs/examples/app-shell/src/sign-in.ts +41 -0
  173. package/docs/examples/app-shell/src/signup.ts +8 -0
  174. package/docs/examples/{reference-service → app-shell}/tsconfig.json +6 -2
  175. package/docs/examples/app-shell/worker-configuration.d.ts +22 -0
  176. package/docs/examples/app-shell/wrangler.jsonc +62 -0
  177. package/docs/examples/club-events/README.md +60 -0
  178. package/docs/examples/club-events/manifests/club-events.yaml +225 -0
  179. package/docs/examples/club-events/smoke.mjs +121 -0
  180. package/docs/examples/club-events/src/handlers.ts +25 -0
  181. package/docs/examples/club-events/src/signup.ts +9 -0
  182. package/docs/examples/generated-web/README.md +78 -0
  183. package/docs/examples/generated-web/posts.yaml +38 -0
  184. package/docs/examples/generated-web/seed.sql +6 -0
  185. package/docs/examples/generated-web/src/worker.ts +43 -0
  186. package/docs/examples/generated-web/tsconfig.json +8 -0
  187. package/docs/examples/generated-web/wrangler.jsonc +8 -0
  188. package/docs/examples/purchasing/.dev.vars.example +6 -0
  189. package/docs/examples/purchasing/README.md +90 -0
  190. package/docs/examples/purchasing/manifests/purchasing.yaml +341 -0
  191. package/docs/examples/purchasing/seed.sql +4 -0
  192. package/docs/examples/purchasing/smoke.mjs +177 -0
  193. package/docs/examples/purchasing/src/admin.tsx +13 -0
  194. package/docs/examples/purchasing/src/handlers.ts +33 -0
  195. package/docs/examples/purchasing/src/notify.ts +29 -0
  196. package/docs/examples/purchasing/src/requests-page.ts +57 -0
  197. package/docs/examples/purchasing/src/routes.ts +53 -0
  198. package/docs/examples/purchasing/src/schedule.ts +31 -0
  199. package/docs/examples/purchasing/src/signup.ts +5 -0
  200. package/docs/examples/purchasing/src/supplier.ts +32 -0
  201. package/docs/examples/purchasing/wrangler.jsonc +69 -0
  202. package/docs/examples/recipes/README.md +58 -0
  203. package/docs/examples/recipes/manifests/recipes.yaml +75 -0
  204. package/docs/examples/recipes/smoke.mjs +102 -0
  205. package/docs/examples/recipes/src/media.ts +13 -0
  206. package/docs/examples/recipes/src/pages.ts +99 -0
  207. package/docs/examples/recipes/src/routes.ts +71 -0
  208. package/docs/examples/recipes/src/site-config.ts +12 -0
  209. package/docs/examples/recipes/wrangler.jsonc +74 -0
  210. package/docs/handbook/cloudflare/authentication.md +164 -214
  211. package/docs/handbook/cloudflare/chatgpt-sites.md +4 -113
  212. package/docs/handbook/cloudflare/deploy-and-operate.md +38 -103
  213. package/docs/handbook/cloudflare/media-r2.md +62 -133
  214. package/docs/handbook/cloudflare/service-entry.md +210 -119
  215. package/docs/handbook/concepts/authorization.md +10 -123
  216. package/docs/handbook/concepts/four-atoms.md +9 -92
  217. package/docs/handbook/concepts/lifecycle-and-locales.md +9 -80
  218. package/docs/handbook/concepts/mcp-and-agents.md +24 -136
  219. package/docs/handbook/concepts/procedures-and-triggers.md +5 -162
  220. package/docs/handbook/concepts/runtime-and-adapters.md +15 -224
  221. package/docs/handbook/concepts/views.md +10 -109
  222. package/docs/handbook/examples/hub.md +2 -18
  223. package/docs/handbook/guides/adapter-guide.md +11 -0
  224. package/docs/handbook/guides/admin-ui.md +111 -164
  225. package/docs/handbook/guides/agent-setup.md +32 -27
  226. package/docs/handbook/guides/email.md +86 -0
  227. package/docs/handbook/guides/mcp-apps.md +483 -0
  228. package/docs/handbook/guides/native-admin.md +543 -0
  229. package/docs/handbook/guides/native-execution.md +132 -0
  230. package/docs/handbook/guides/oauth.md +161 -0
  231. package/docs/handbook/guides/postgres-adapters.md +56 -0
  232. package/docs/handbook/guides/public-pages.md +53 -0
  233. package/docs/handbook/guides/size-and-startup.md +9 -0
  234. package/docs/handbook/guides/typed-queries.md +39 -102
  235. package/docs/handbook/navigation.json +160 -31
  236. package/docs/handbook/reference/admin-descriptors.md +53 -0
  237. package/docs/handbook/reference/authorization.md +44 -52
  238. package/docs/handbook/reference/describe.md +56 -0
  239. package/docs/handbook/reference/diagnostics.md +15 -79
  240. package/docs/handbook/reference/features.md +45 -53
  241. package/docs/handbook/reference/manifest.md +6 -69
  242. package/docs/handbook/reference/procedure.md +11 -116
  243. package/docs/handbook/reference/schema.md +85 -185
  244. package/docs/handbook/reference/site-config.md +5 -65
  245. package/docs/handbook/reference/surface.md +389 -97
  246. package/docs/handbook/reference/trigger.md +54 -90
  247. package/docs/handbook/reference/view.md +32 -160
  248. package/docs/handbook/releases/index.md +120 -13
  249. package/docs/handbook/start/overview.md +9 -56
  250. package/docs/handbook/start/project-and-cli.md +40 -149
  251. package/docs/handbook/start/quickstart-worker.md +38 -116
  252. package/docs/skills/develop/SKILL.md +35 -123
  253. package/docs/skills/develop/mcp-app.md +37 -77
  254. package/docs/skills/integrate/SKILL.md +42 -84
  255. package/docs/skills/media-gc/SKILL.md +21 -8
  256. package/docs/skills/provision/SKILL.md +28 -91
  257. package/docs/skills/update/SKILL.md +19 -43
  258. package/docs/source/scripts/postgres-adapter.ts +101 -0
  259. package/docs/upgrade-0.1-to-0.2.md +25 -9
  260. package/package.json +97 -112
  261. package/skills/.keep +0 -0
  262. package/skills/README.md +67 -0
  263. package/skills/mantle/SKILL.md +135 -0
  264. package/skills/mantle/references/local-testing.md +15 -0
  265. package/skills/mantle/references/web.md +41 -0
  266. package/skills/mantle/references/website.md +49 -0
  267. package/dist/admin/consent.d.ts +0 -17
  268. package/dist/admin/consent.d.ts.map +0 -1
  269. package/dist/admin/consent.js +0 -17
  270. package/dist/admin/consent.js.map +0 -1
  271. package/dist/admin/createAdminSurface.d.ts +0 -25
  272. package/dist/admin/createAdminSurface.d.ts.map +0 -1
  273. package/dist/admin/createAdminSurface.js +0 -635
  274. package/dist/admin/createAdminSurface.js.map +0 -1
  275. package/dist/admin/design.d.ts +0 -7
  276. package/dist/admin/design.d.ts.map +0 -1
  277. package/dist/admin/design.js +0 -18
  278. package/dist/admin/design.js.map +0 -1
  279. package/dist/admin/developerConsole.d.ts +0 -267
  280. package/dist/admin/developerConsole.d.ts.map +0 -1
  281. package/dist/admin/developerConsole.js +0 -136
  282. package/dist/admin/developerConsole.js.map +0 -1
  283. package/dist/admin/extensions.d.ts +0 -290
  284. package/dist/admin/extensions.d.ts.map +0 -1
  285. package/dist/admin/extensions.js +0 -532
  286. package/dist/admin/extensions.js.map +0 -1
  287. package/dist/admin/identity.d.ts +0 -62
  288. package/dist/admin/identity.d.ts.map +0 -1
  289. package/dist/admin/identity.js +0 -7
  290. package/dist/admin/identity.js.map +0 -1
  291. package/dist/admin/index.d.ts +0 -7
  292. package/dist/admin/index.d.ts.map +0 -1
  293. package/dist/admin/index.js +0 -7
  294. package/dist/admin/index.js.map +0 -1
  295. package/dist/admin/procedureFlow.d.ts +0 -38
  296. package/dist/admin/procedureFlow.d.ts.map +0 -1
  297. package/dist/admin/procedureFlow.js +0 -188
  298. package/dist/admin/procedureFlow.js.map +0 -1
  299. package/dist/auth/ConsoleEmailSender.d.ts +0 -15
  300. package/dist/auth/ConsoleEmailSender.d.ts.map +0 -1
  301. package/dist/auth/ConsoleEmailSender.js +0 -19
  302. package/dist/auth/ConsoleEmailSender.js.map +0 -1
  303. package/dist/auth/appleClientSecret.d.ts +0 -75
  304. package/dist/auth/appleClientSecret.d.ts.map +0 -1
  305. package/dist/auth/appleClientSecret.js +0 -116
  306. package/dist/auth/appleClientSecret.js.map +0 -1
  307. package/dist/auth/buildAuth.d.ts +0 -4568
  308. package/dist/auth/buildAuth.d.ts.map +0 -1
  309. package/dist/auth/buildAuth.js +0 -312
  310. package/dist/auth/buildAuth.js.map +0 -1
  311. package/dist/auth/callerResolver.d.ts +0 -52
  312. package/dist/auth/callerResolver.d.ts.map +0 -1
  313. package/dist/auth/callerResolver.js +0 -52
  314. package/dist/auth/callerResolver.js.map +0 -1
  315. package/dist/auth/createAuthRoutes.d.ts +0 -22
  316. package/dist/auth/createAuthRoutes.d.ts.map +0 -1
  317. package/dist/auth/createAuthRoutes.js +0 -129
  318. package/dist/auth/createAuthRoutes.js.map +0 -1
  319. package/dist/auth/createMantleAuth.d.ts +0 -3
  320. package/dist/auth/createMantleAuth.d.ts.map +0 -1
  321. package/dist/auth/createMantleAuth.js +0 -504
  322. package/dist/auth/createMantleAuth.js.map +0 -1
  323. package/dist/auth/db.d.ts +0 -18
  324. package/dist/auth/db.d.ts.map +0 -1
  325. package/dist/auth/db.js +0 -27
  326. package/dist/auth/db.js.map +0 -1
  327. package/dist/auth/emailTemplates.d.ts +0 -7
  328. package/dist/auth/emailTemplates.d.ts.map +0 -1
  329. package/dist/auth/emailTemplates.js.map +0 -1
  330. package/dist/auth/index.d.ts +0 -9
  331. package/dist/auth/index.d.ts.map +0 -1
  332. package/dist/auth/index.js +0 -7
  333. package/dist/auth/index.js.map +0 -1
  334. package/dist/auth/methods.d.ts +0 -717
  335. package/dist/auth/methods.d.ts.map +0 -1
  336. package/dist/auth/methods.js +0 -321
  337. package/dist/auth/methods.js.map +0 -1
  338. package/dist/auth/oauthTokens.d.ts +0 -24
  339. package/dist/auth/oauthTokens.d.ts.map +0 -1
  340. package/dist/auth/oauthTokens.js +0 -146
  341. package/dist/auth/oauthTokens.js.map +0 -1
  342. package/dist/auth/setupIncomplete.d.ts +0 -16
  343. package/dist/auth/setupIncomplete.d.ts.map +0 -1
  344. package/dist/auth/setupIncomplete.js +0 -50
  345. package/dist/auth/setupIncomplete.js.map +0 -1
  346. package/dist/auth/types.d.ts +0 -440
  347. package/dist/auth/types.d.ts.map +0 -1
  348. package/dist/auth/types.js +0 -10
  349. package/dist/auth/types.js.map +0 -1
  350. package/dist/bun/index.d.ts +0 -21
  351. package/dist/bun/index.d.ts.map +0 -1
  352. package/dist/bun/index.js +0 -46
  353. package/dist/bun/index.js.map +0 -1
  354. package/dist/cli/emitModule.d.ts +0 -7
  355. package/dist/cli/emitModule.d.ts.map +0 -1
  356. package/dist/cli/emitModule.js +0 -127
  357. package/dist/cli/emitModule.js.map +0 -1
  358. package/dist/cli/generate.d.ts +0 -8
  359. package/dist/cli/generate.d.ts.map +0 -1
  360. package/dist/cli/generate.js +0 -391
  361. package/dist/cli/generate.js.map +0 -1
  362. package/dist/cli/main.d.ts +0 -3
  363. package/dist/cli/main.d.ts.map +0 -1
  364. package/dist/cli/main.js +0 -21
  365. package/dist/cli/main.js.map +0 -1
  366. package/dist/cli/preset.d.ts +0 -18
  367. package/dist/cli/preset.d.ts.map +0 -1
  368. package/dist/cli/preset.js +0 -405
  369. package/dist/cli/preset.js.map +0 -1
  370. package/dist/cloudflare/index.d.ts +0 -25
  371. package/dist/cloudflare/index.d.ts.map +0 -1
  372. package/dist/cloudflare/index.js +0 -16
  373. package/dist/cloudflare/index.js.map +0 -1
  374. package/dist/cloudflare/r2Media.d.ts.map +0 -1
  375. package/dist/cloudflare/r2Media.js +0 -69
  376. package/dist/cloudflare/r2Media.js.map +0 -1
  377. package/dist/core/caller.d.ts +0 -35
  378. package/dist/core/caller.d.ts.map +0 -1
  379. package/dist/core/caller.js +0 -3
  380. package/dist/core/caller.js.map +0 -1
  381. package/dist/core/createMantle.d.ts +0 -22
  382. package/dist/core/createMantle.d.ts.map +0 -1
  383. package/dist/core/createMantle.js +0 -117
  384. package/dist/core/createMantle.js.map +0 -1
  385. package/dist/core/dialect.d.ts +0 -53
  386. package/dist/core/dialect.d.ts.map +0 -1
  387. package/dist/core/dialect.js +0 -7
  388. package/dist/core/dialect.js.map +0 -1
  389. package/dist/core/driver.d.ts +0 -24
  390. package/dist/core/driver.d.ts.map +0 -1
  391. package/dist/core/driver.js +0 -2
  392. package/dist/core/driver.js.map +0 -1
  393. package/dist/core/email.d.ts +0 -17
  394. package/dist/core/email.d.ts.map +0 -1
  395. package/dist/core/email.js +0 -2
  396. package/dist/core/email.js.map +0 -1
  397. package/dist/core/index.d.ts +0 -16
  398. package/dist/core/index.d.ts.map +0 -1
  399. package/dist/core/index.js +0 -16
  400. package/dist/core/index.js.map +0 -1
  401. package/dist/core/invocation.d.ts +0 -66
  402. package/dist/core/invocation.d.ts.map +0 -1
  403. package/dist/core/invocation.js +0 -3
  404. package/dist/core/invocation.js.map +0 -1
  405. package/dist/core/observation.d.ts +0 -3
  406. package/dist/core/observation.d.ts.map +0 -1
  407. package/dist/core/observation.js +0 -10
  408. package/dist/core/observation.js.map +0 -1
  409. package/dist/core/runtime/auth.d.ts +0 -9
  410. package/dist/core/runtime/auth.d.ts.map +0 -1
  411. package/dist/core/runtime/auth.js +0 -39
  412. package/dist/core/runtime/auth.js.map +0 -1
  413. package/dist/core/runtime/createRuntime.d.ts +0 -23
  414. package/dist/core/runtime/createRuntime.d.ts.map +0 -1
  415. package/dist/core/runtime/createRuntime.js +0 -214
  416. package/dist/core/runtime/createRuntime.js.map +0 -1
  417. package/dist/core/runtime/verifyPlan.d.ts +0 -34
  418. package/dist/core/runtime/verifyPlan.d.ts.map +0 -1
  419. package/dist/core/runtime/verifyPlan.js +0 -301
  420. package/dist/core/runtime/verifyPlan.js.map +0 -1
  421. package/dist/core/service.d.ts +0 -45
  422. package/dist/core/service.d.ts.map +0 -1
  423. package/dist/core/service.js +0 -2
  424. package/dist/core/service.js.map +0 -1
  425. package/dist/core/sessionUser.d.ts +0 -14
  426. package/dist/core/sessionUser.d.ts.map +0 -1
  427. package/dist/core/sessionUser.js +0 -4
  428. package/dist/core/sessionUser.js.map +0 -1
  429. package/dist/core/site.d.ts.map +0 -1
  430. package/dist/core/site.js +0 -2
  431. package/dist/core/site.js.map +0 -1
  432. package/dist/core/siteConfig.d.ts +0 -5
  433. package/dist/core/siteConfig.d.ts.map +0 -1
  434. package/dist/core/siteConfig.js +0 -24
  435. package/dist/core/siteConfig.js.map +0 -1
  436. package/dist/core/sql/allowlist.d.ts +0 -67
  437. package/dist/core/sql/allowlist.d.ts.map +0 -1
  438. package/dist/core/sql/allowlist.js +0 -737
  439. package/dist/core/sql/allowlist.js.map +0 -1
  440. package/dist/core/sql/ast.d.ts +0 -24
  441. package/dist/core/sql/ast.d.ts.map +0 -1
  442. package/dist/core/sql/ast.js +0 -32
  443. package/dist/core/sql/ast.js.map +0 -1
  444. package/dist/core/sql/compile.d.ts +0 -50
  445. package/dist/core/sql/compile.d.ts.map +0 -1
  446. package/dist/core/sql/compile.js +0 -48
  447. package/dist/core/sql/compile.js.map +0 -1
  448. package/dist/core/sql/policy.d.ts +0 -123
  449. package/dist/core/sql/policy.d.ts.map +0 -1
  450. package/dist/core/sql/policy.js +0 -398
  451. package/dist/core/sql/policy.js.map +0 -1
  452. package/dist/core/sql/positions.d.ts +0 -30
  453. package/dist/core/sql/positions.d.ts.map +0 -1
  454. package/dist/core/sql/positions.js +0 -22
  455. package/dist/core/sql/positions.js.map +0 -1
  456. package/dist/core/sql/run.d.ts +0 -68
  457. package/dist/core/sql/run.d.ts.map +0 -1
  458. package/dist/core/sql/run.js +0 -384
  459. package/dist/core/sql/run.js.map +0 -1
  460. package/dist/core/store/createStore.d.ts +0 -49
  461. package/dist/core/store/createStore.d.ts.map +0 -1
  462. package/dist/core/store/createStore.js +0 -227
  463. package/dist/core/store/createStore.js.map +0 -1
  464. package/dist/core/store/cursor.d.ts +0 -4
  465. package/dist/core/store/cursor.d.ts.map +0 -1
  466. package/dist/core/store/cursor.js +0 -22
  467. package/dist/core/store/cursor.js.map +0 -1
  468. package/dist/core/store/json.d.ts +0 -80
  469. package/dist/core/store/json.d.ts.map +0 -1
  470. package/dist/core/store/json.js +0 -342
  471. package/dist/core/store/json.js.map +0 -1
  472. package/dist/core/store.d.ts +0 -160
  473. package/dist/core/store.d.ts.map +0 -1
  474. package/dist/core/store.js +0 -2
  475. package/dist/core/store.js.map +0 -1
  476. package/dist/core/wire.d.ts +0 -20
  477. package/dist/core/wire.d.ts.map +0 -1
  478. package/dist/core/wire.js +0 -84
  479. package/dist/core/wire.js.map +0 -1
  480. package/dist/core/withCaller.d.ts +0 -24
  481. package/dist/core/withCaller.d.ts.map +0 -1
  482. package/dist/core/withCaller.js +0 -35
  483. package/dist/core/withCaller.js.map +0 -1
  484. package/dist/d1/adapter.d.ts +0 -12
  485. package/dist/d1/adapter.d.ts.map +0 -1
  486. package/dist/d1/adapter.js +0 -23
  487. package/dist/d1/adapter.js.map +0 -1
  488. package/dist/d1/codec.d.ts +0 -14
  489. package/dist/d1/codec.d.ts.map +0 -1
  490. package/dist/d1/codec.js +0 -136
  491. package/dist/d1/codec.js.map +0 -1
  492. package/dist/d1/compile/index.d.ts +0 -9
  493. package/dist/d1/compile/index.d.ts.map +0 -1
  494. package/dist/d1/compile/index.js +0 -9
  495. package/dist/d1/compile/index.js.map +0 -1
  496. package/dist/d1/dialect.d.ts +0 -4
  497. package/dist/d1/dialect.d.ts.map +0 -1
  498. package/dist/d1/dialect.js +0 -13
  499. package/dist/d1/dialect.js.map +0 -1
  500. package/dist/d1/executor.d.ts +0 -15
  501. package/dist/d1/executor.d.ts.map +0 -1
  502. package/dist/d1/executor.js +0 -96
  503. package/dist/d1/executor.js.map +0 -1
  504. package/dist/d1/index.d.ts +0 -6
  505. package/dist/d1/index.d.ts.map +0 -1
  506. package/dist/d1/index.js +0 -6
  507. package/dist/d1/index.js.map +0 -1
  508. package/dist/d1/lower.d.ts +0 -8
  509. package/dist/d1/lower.d.ts.map +0 -1
  510. package/dist/d1/lower.js +0 -167
  511. package/dist/d1/lower.js.map +0 -1
  512. package/dist/d1/media.d.ts +0 -5
  513. package/dist/d1/media.d.ts.map +0 -1
  514. package/dist/d1/media.js +0 -157
  515. package/dist/d1/media.js.map +0 -1
  516. package/dist/d1/migrations.d.ts +0 -12
  517. package/dist/d1/migrations.d.ts.map +0 -1
  518. package/dist/d1/migrations.js +0 -111
  519. package/dist/d1/migrations.js.map +0 -1
  520. package/dist/d1/print.d.ts +0 -28
  521. package/dist/d1/print.d.ts.map +0 -1
  522. package/dist/d1/print.js +0 -54
  523. package/dist/d1/print.js.map +0 -1
  524. package/dist/d1/site.d.ts +0 -17
  525. package/dist/d1/site.d.ts.map +0 -1
  526. package/dist/d1/site.js +0 -63
  527. package/dist/d1/site.js.map +0 -1
  528. package/dist/d1/storage.d.ts +0 -34
  529. package/dist/d1/storage.d.ts.map +0 -1
  530. package/dist/d1/storage.js +0 -288
  531. package/dist/d1/storage.js.map +0 -1
  532. package/dist/d1/tz.d.ts +0 -21
  533. package/dist/d1/tz.d.ts.map +0 -1
  534. package/dist/d1/tz.js +0 -54
  535. package/dist/d1/tz.js.map +0 -1
  536. package/dist/d1/validator.d.ts +0 -10
  537. package/dist/d1/validator.d.ts.map +0 -1
  538. package/dist/d1/validator.js +0 -33
  539. package/dist/d1/validator.js.map +0 -1
  540. package/dist/mcp/apps.d.ts +0 -43
  541. package/dist/mcp/apps.d.ts.map +0 -1
  542. package/dist/mcp/apps.js +0 -57
  543. package/dist/mcp/apps.js.map +0 -1
  544. package/dist/mcp/catalog.d.ts +0 -55
  545. package/dist/mcp/catalog.d.ts.map +0 -1
  546. package/dist/mcp/catalog.js +0 -81
  547. package/dist/mcp/catalog.js.map +0 -1
  548. package/dist/mcp/createMcpSurface.d.ts +0 -60
  549. package/dist/mcp/createMcpSurface.d.ts.map +0 -1
  550. package/dist/mcp/createMcpSurface.js +0 -182
  551. package/dist/mcp/createMcpSurface.js.map +0 -1
  552. package/dist/mcp/index.d.ts +0 -4
  553. package/dist/mcp/index.d.ts.map +0 -1
  554. package/dist/mcp/index.js +0 -4
  555. package/dist/mcp/index.js.map +0 -1
  556. package/dist/mcp/media.d.ts +0 -9
  557. package/dist/mcp/media.d.ts.map +0 -1
  558. package/dist/mcp/media.js +0 -33
  559. package/dist/mcp/media.js.map +0 -1
  560. package/dist/postgres/adapter.d.ts +0 -23
  561. package/dist/postgres/adapter.d.ts.map +0 -1
  562. package/dist/postgres/adapter.js +0 -50
  563. package/dist/postgres/adapter.js.map +0 -1
  564. package/dist/postgres/codec.d.ts +0 -13
  565. package/dist/postgres/codec.d.ts.map +0 -1
  566. package/dist/postgres/codec.js +0 -110
  567. package/dist/postgres/codec.js.map +0 -1
  568. package/dist/postgres/compile/index.d.ts +0 -9
  569. package/dist/postgres/compile/index.d.ts.map +0 -1
  570. package/dist/postgres/compile/index.js +0 -9
  571. package/dist/postgres/compile/index.js.map +0 -1
  572. package/dist/postgres/driver.d.ts +0 -109
  573. package/dist/postgres/driver.d.ts.map +0 -1
  574. package/dist/postgres/driver.js +0 -186
  575. package/dist/postgres/driver.js.map +0 -1
  576. package/dist/postgres/executor.d.ts +0 -28
  577. package/dist/postgres/executor.d.ts.map +0 -1
  578. package/dist/postgres/executor.js +0 -96
  579. package/dist/postgres/executor.js.map +0 -1
  580. package/dist/postgres/index.d.ts +0 -8
  581. package/dist/postgres/index.d.ts.map +0 -1
  582. package/dist/postgres/index.js +0 -8
  583. package/dist/postgres/index.js.map +0 -1
  584. package/dist/postgres/lower.d.ts +0 -6
  585. package/dist/postgres/lower.d.ts.map +0 -1
  586. package/dist/postgres/lower.js +0 -152
  587. package/dist/postgres/lower.js.map +0 -1
  588. package/dist/postgres/print.d.ts +0 -11
  589. package/dist/postgres/print.d.ts.map +0 -1
  590. package/dist/postgres/print.js +0 -108
  591. package/dist/postgres/print.js.map +0 -1
  592. package/dist/postgres/session.d.ts +0 -9
  593. package/dist/postgres/session.d.ts.map +0 -1
  594. package/dist/postgres/session.js +0 -111
  595. package/dist/postgres/session.js.map +0 -1
  596. package/dist/postgres/storage.d.ts +0 -32
  597. package/dist/postgres/storage.d.ts.map +0 -1
  598. package/dist/postgres/storage.js +0 -283
  599. package/dist/postgres/storage.js.map +0 -1
  600. package/dist/postgres/validator.d.ts +0 -13
  601. package/dist/postgres/validator.d.ts.map +0 -1
  602. package/dist/postgres/validator.js +0 -53
  603. package/dist/postgres/validator.js.map +0 -1
  604. package/dist/spec/domain/index.d.ts +0 -3
  605. package/dist/spec/domain/index.d.ts.map +0 -1
  606. package/dist/spec/domain/index.js +0 -3
  607. package/dist/spec/domain/index.js.map +0 -1
  608. package/dist/spec/domain/model/ContentState.d.ts +0 -8
  609. package/dist/spec/domain/model/ContentState.d.ts.map +0 -1
  610. package/dist/spec/domain/model/ContentState.js +0 -7
  611. package/dist/spec/domain/model/ContentState.js.map +0 -1
  612. package/dist/spec/domain/model/Entry.d.ts +0 -29
  613. package/dist/spec/domain/model/Entry.d.ts.map +0 -1
  614. package/dist/spec/domain/model/Entry.js +0 -16
  615. package/dist/spec/domain/model/Entry.js.map +0 -1
  616. package/dist/spec/domain/model/Locale.d.ts +0 -30
  617. package/dist/spec/domain/model/Locale.d.ts.map +0 -1
  618. package/dist/spec/domain/model/Locale.js +0 -33
  619. package/dist/spec/domain/model/Locale.js.map +0 -1
  620. package/dist/spec/domain/model/ManifestGrammar.d.ts.map +0 -1
  621. package/dist/spec/domain/model/ManifestGrammar.js +0 -153
  622. package/dist/spec/domain/model/ManifestGrammar.js.map +0 -1
  623. package/dist/spec/domain/model/MediaMimeAccept.d.ts +0 -55
  624. package/dist/spec/domain/model/MediaMimeAccept.d.ts.map +0 -1
  625. package/dist/spec/domain/model/MediaMimeAccept.js +0 -90
  626. package/dist/spec/domain/model/MediaMimeAccept.js.map +0 -1
  627. package/dist/spec/domain/model/RuntimePlan.d.ts.map +0 -1
  628. package/dist/spec/domain/model/RuntimePlan.js +0 -4
  629. package/dist/spec/domain/model/RuntimePlan.js.map +0 -1
  630. package/dist/spec/domain/model/SiteConfig.d.ts +0 -173
  631. package/dist/spec/domain/model/SiteConfig.d.ts.map +0 -1
  632. package/dist/spec/domain/model/SiteConfig.js +0 -13
  633. package/dist/spec/domain/model/SiteConfig.js.map +0 -1
  634. package/dist/spec/domain/model/SqlIr.d.ts.map +0 -1
  635. package/dist/spec/domain/model/SqlIr.js +0 -95
  636. package/dist/spec/domain/model/SqlIr.js.map +0 -1
  637. package/dist/spec/domain/model/index.d.ts +0 -14
  638. package/dist/spec/domain/model/index.d.ts.map +0 -1
  639. package/dist/spec/domain/model/index.js +0 -14
  640. package/dist/spec/domain/model/index.js.map +0 -1
  641. package/dist/spec/domain/service/CloudflareCron.d.ts.map +0 -1
  642. package/dist/spec/domain/service/CloudflareCron.js.map +0 -1
  643. package/dist/spec/domain/service/CrossSchemaChecker.d.ts +0 -34
  644. package/dist/spec/domain/service/CrossSchemaChecker.d.ts.map +0 -1
  645. package/dist/spec/domain/service/CrossSchemaChecker.js +0 -114
  646. package/dist/spec/domain/service/CrossSchemaChecker.js.map +0 -1
  647. package/dist/spec/domain/service/EntryDataValidator.d.ts +0 -56
  648. package/dist/spec/domain/service/EntryDataValidator.d.ts.map +0 -1
  649. package/dist/spec/domain/service/EntryDataValidator.js +0 -88
  650. package/dist/spec/domain/service/EntryDataValidator.js.map +0 -1
  651. package/dist/spec/domain/service/JsonSchemaToZod.d.ts +0 -29
  652. package/dist/spec/domain/service/JsonSchemaToZod.d.ts.map +0 -1
  653. package/dist/spec/domain/service/JsonSchemaToZod.js +0 -156
  654. package/dist/spec/domain/service/JsonSchemaToZod.js.map +0 -1
  655. package/dist/spec/domain/service/LifecycleStateMachine.d.ts +0 -75
  656. package/dist/spec/domain/service/LifecycleStateMachine.d.ts.map +0 -1
  657. package/dist/spec/domain/service/LifecycleStateMachine.js +0 -76
  658. package/dist/spec/domain/service/LifecycleStateMachine.js.map +0 -1
  659. package/dist/spec/domain/service/LocaleCanonicalizer.d.ts +0 -29
  660. package/dist/spec/domain/service/LocaleCanonicalizer.d.ts.map +0 -1
  661. package/dist/spec/domain/service/LocaleCanonicalizer.js +0 -96
  662. package/dist/spec/domain/service/LocaleCanonicalizer.js.map +0 -1
  663. package/dist/spec/domain/service/ManifestFieldChecks.d.ts +0 -60
  664. package/dist/spec/domain/service/ManifestFieldChecks.d.ts.map +0 -1
  665. package/dist/spec/domain/service/ManifestFieldChecks.js +0 -116
  666. package/dist/spec/domain/service/ManifestFieldChecks.js.map +0 -1
  667. package/dist/spec/domain/service/ManifestGraphValidator.d.ts +0 -15
  668. package/dist/spec/domain/service/ManifestGraphValidator.d.ts.map +0 -1
  669. package/dist/spec/domain/service/ManifestGraphValidator.js +0 -225
  670. package/dist/spec/domain/service/ManifestGraphValidator.js.map +0 -1
  671. package/dist/spec/domain/service/ManifestLinker.d.ts +0 -45
  672. package/dist/spec/domain/service/ManifestLinker.d.ts.map +0 -1
  673. package/dist/spec/domain/service/ManifestLinker.js +0 -92
  674. package/dist/spec/domain/service/ManifestLinker.js.map +0 -1
  675. package/dist/spec/domain/service/ManifestLocaleTrimmer.d.ts +0 -16
  676. package/dist/spec/domain/service/ManifestLocaleTrimmer.d.ts.map +0 -1
  677. package/dist/spec/domain/service/ManifestLocaleTrimmer.js +0 -76
  678. package/dist/spec/domain/service/ManifestLocaleTrimmer.js.map +0 -1
  679. package/dist/spec/domain/service/ManifestParser.d.ts +0 -49
  680. package/dist/spec/domain/service/ManifestParser.d.ts.map +0 -1
  681. package/dist/spec/domain/service/ManifestParser.js +0 -203
  682. package/dist/spec/domain/service/ManifestParser.js.map +0 -1
  683. package/dist/spec/domain/service/ManifestPartition.d.ts +0 -9
  684. package/dist/spec/domain/service/ManifestPartition.d.ts.map +0 -1
  685. package/dist/spec/domain/service/ManifestPartition.js +0 -19
  686. package/dist/spec/domain/service/ManifestPartition.js.map +0 -1
  687. package/dist/spec/domain/service/ManifestPathDiagnoser.d.ts +0 -33
  688. package/dist/spec/domain/service/ManifestPathDiagnoser.d.ts.map +0 -1
  689. package/dist/spec/domain/service/ManifestPathDiagnoser.js +0 -43
  690. package/dist/spec/domain/service/ManifestPathDiagnoser.js.map +0 -1
  691. package/dist/spec/domain/service/ManifestSourceSpans.d.ts +0 -10
  692. package/dist/spec/domain/service/ManifestSourceSpans.d.ts.map +0 -1
  693. package/dist/spec/domain/service/ManifestSourceSpans.js +0 -76
  694. package/dist/spec/domain/service/ManifestSourceSpans.js.map +0 -1
  695. package/dist/spec/domain/service/McpToolNaming.d.ts +0 -25
  696. package/dist/spec/domain/service/McpToolNaming.d.ts.map +0 -1
  697. package/dist/spec/domain/service/McpToolNaming.js +0 -50
  698. package/dist/spec/domain/service/McpToolNaming.js.map +0 -1
  699. package/dist/spec/domain/service/PlanFingerprint.d.ts.map +0 -1
  700. package/dist/spec/domain/service/PlanFingerprint.js +0 -14
  701. package/dist/spec/domain/service/PlanFingerprint.js.map +0 -1
  702. package/dist/spec/domain/service/ProcedureViewSpecChecks.d.ts +0 -4
  703. package/dist/spec/domain/service/ProcedureViewSpecChecks.d.ts.map +0 -1
  704. package/dist/spec/domain/service/ProcedureViewSpecChecks.js +0 -211
  705. package/dist/spec/domain/service/ProcedureViewSpecChecks.js.map +0 -1
  706. package/dist/spec/domain/service/SchemaAdminUiChecker.d.ts +0 -44
  707. package/dist/spec/domain/service/SchemaAdminUiChecker.d.ts.map +0 -1
  708. package/dist/spec/domain/service/SchemaAdminUiChecker.js +0 -333
  709. package/dist/spec/domain/service/SchemaAdminUiChecker.js.map +0 -1
  710. package/dist/spec/domain/service/SchemaIndexChecker.d.ts +0 -33
  711. package/dist/spec/domain/service/SchemaIndexChecker.d.ts.map +0 -1
  712. package/dist/spec/domain/service/SchemaIndexChecker.js +0 -256
  713. package/dist/spec/domain/service/SchemaIndexChecker.js.map +0 -1
  714. package/dist/spec/domain/service/SchemaSearchChecker.d.ts +0 -11
  715. package/dist/spec/domain/service/SchemaSearchChecker.d.ts.map +0 -1
  716. package/dist/spec/domain/service/SchemaSearchChecker.js +0 -78
  717. package/dist/spec/domain/service/SchemaSearchChecker.js.map +0 -1
  718. package/dist/spec/domain/service/SchemaSpecChecks.d.ts +0 -31
  719. package/dist/spec/domain/service/SchemaSpecChecks.d.ts.map +0 -1
  720. package/dist/spec/domain/service/SchemaSpecChecks.js +0 -445
  721. package/dist/spec/domain/service/SchemaSpecChecks.js.map +0 -1
  722. package/dist/spec/domain/service/SiteDefaultsValidator.d.ts +0 -39
  723. package/dist/spec/domain/service/SiteDefaultsValidator.d.ts.map +0 -1
  724. package/dist/spec/domain/service/SiteDefaultsValidator.js +0 -194
  725. package/dist/spec/domain/service/SiteDefaultsValidator.js.map +0 -1
  726. package/dist/spec/domain/service/SqlClassify.d.ts +0 -19
  727. package/dist/spec/domain/service/SqlClassify.d.ts.map +0 -1
  728. package/dist/spec/domain/service/SqlClassify.js +0 -42
  729. package/dist/spec/domain/service/SqlClassify.js.map +0 -1
  730. package/dist/spec/domain/service/SqlRefusal.d.ts +0 -16
  731. package/dist/spec/domain/service/SqlRefusal.d.ts.map +0 -1
  732. package/dist/spec/domain/service/SqlRefusal.js +0 -20
  733. package/dist/spec/domain/service/SqlRefusal.js.map +0 -1
  734. package/dist/spec/domain/service/SqlTypes.d.ts +0 -40
  735. package/dist/spec/domain/service/SqlTypes.d.ts.map +0 -1
  736. package/dist/spec/domain/service/SqlTypes.js +0 -110
  737. package/dist/spec/domain/service/SqlTypes.js.map +0 -1
  738. package/dist/spec/domain/service/StaffRoleHierarchy.d.ts +0 -3
  739. package/dist/spec/domain/service/StaffRoleHierarchy.d.ts.map +0 -1
  740. package/dist/spec/domain/service/StaffRoleHierarchy.js +0 -19
  741. package/dist/spec/domain/service/StaffRoleHierarchy.js.map +0 -1
  742. package/dist/spec/domain/service/TriggerGraphChecks.d.ts +0 -16
  743. package/dist/spec/domain/service/TriggerGraphChecks.d.ts.map +0 -1
  744. package/dist/spec/domain/service/TriggerGraphChecks.js +0 -337
  745. package/dist/spec/domain/service/TriggerGraphChecks.js.map +0 -1
  746. package/dist/spec/domain/service/TriggerSpecChecks.d.ts +0 -4
  747. package/dist/spec/domain/service/TriggerSpecChecks.d.ts.map +0 -1
  748. package/dist/spec/domain/service/TriggerSpecChecks.js +0 -89
  749. package/dist/spec/domain/service/TriggerSpecChecks.js.map +0 -1
  750. package/dist/spec/domain/service/ValidatedAdminUiProjection.d.ts +0 -11
  751. package/dist/spec/domain/service/ValidatedAdminUiProjection.d.ts.map +0 -1
  752. package/dist/spec/domain/service/ValidatedAdminUiProjection.js +0 -26
  753. package/dist/spec/domain/service/ValidatedAdminUiProjection.js.map +0 -1
  754. package/dist/spec/domain/service/ViewAdminUiChecker.d.ts +0 -14
  755. package/dist/spec/domain/service/ViewAdminUiChecker.d.ts.map +0 -1
  756. package/dist/spec/domain/service/ViewAdminUiChecker.js +0 -58
  757. package/dist/spec/domain/service/ViewAdminUiChecker.js.map +0 -1
  758. package/dist/spec/domain/service/index.d.ts +0 -24
  759. package/dist/spec/domain/service/index.d.ts.map +0 -1
  760. package/dist/spec/domain/service/index.js +0 -24
  761. package/dist/spec/domain/service/index.js.map +0 -1
  762. package/dist/spec/index.d.ts +0 -10
  763. package/dist/spec/index.d.ts.map +0 -1
  764. package/dist/spec/index.js +0 -12
  765. package/dist/spec/index.js.map +0 -1
  766. package/dist/spec/infrastructure/cli/parseArgsError.d.ts +0 -16
  767. package/dist/spec/infrastructure/cli/parseArgsError.d.ts.map +0 -1
  768. package/dist/spec/infrastructure/cli/parseArgsError.js +0 -34
  769. package/dist/spec/infrastructure/cli/parseArgsError.js.map +0 -1
  770. package/dist/spec/infrastructure/index.d.ts +0 -2
  771. package/dist/spec/infrastructure/index.d.ts.map +0 -1
  772. package/dist/spec/infrastructure/index.js +0 -2
  773. package/dist/spec/infrastructure/index.js.map +0 -1
  774. package/dist/spec/infrastructure/sql/PgQueryParser.d.ts +0 -18
  775. package/dist/spec/infrastructure/sql/PgQueryParser.d.ts.map +0 -1
  776. package/dist/spec/infrastructure/sql/PgQueryParser.js +0 -17
  777. package/dist/spec/infrastructure/sql/PgQueryParser.js.map +0 -1
  778. package/dist/spec/infrastructure/sql/compilePlan.d.ts +0 -37
  779. package/dist/spec/infrastructure/sql/compilePlan.d.ts.map +0 -1
  780. package/dist/spec/infrastructure/sql/compilePlan.js +0 -287
  781. package/dist/spec/infrastructure/sql/compilePlan.js.map +0 -1
  782. package/dist/spec/infrastructure/sql/compileSql.d.ts +0 -35
  783. package/dist/spec/infrastructure/sql/compileSql.d.ts.map +0 -1
  784. package/dist/spec/infrastructure/sql/compileSql.js +0 -92
  785. package/dist/spec/infrastructure/sql/compileSql.js.map +0 -1
  786. package/dist/spec/infrastructure/sql/frontEnd.d.ts +0 -8
  787. package/dist/spec/infrastructure/sql/frontEnd.d.ts.map +0 -1
  788. package/dist/spec/infrastructure/sql/frontEnd.js +0 -100
  789. package/dist/spec/infrastructure/sql/frontEnd.js.map +0 -1
  790. package/dist/spec/infrastructure/sql/index.d.ts +0 -4
  791. package/dist/spec/infrastructure/sql/index.d.ts.map +0 -1
  792. package/dist/spec/infrastructure/sql/index.js +0 -4
  793. package/dist/spec/infrastructure/sql/index.js.map +0 -1
  794. package/dist/spec/kernel/diagnostic.d.ts.map +0 -1
  795. package/dist/spec/kernel/diagnostic.js +0 -301
  796. package/dist/spec/kernel/diagnostic.js.map +0 -1
  797. package/dist/spec/kernel/index.d.ts +0 -8
  798. package/dist/spec/kernel/index.d.ts.map +0 -1
  799. package/dist/spec/kernel/index.js +0 -8
  800. package/dist/spec/kernel/index.js.map +0 -1
  801. package/dist/spec/usecase/EmitTypes.d.ts +0 -22
  802. package/dist/spec/usecase/EmitTypes.d.ts.map +0 -1
  803. package/dist/spec/usecase/EmitTypes.js +0 -182
  804. package/dist/spec/usecase/EmitTypes.js.map +0 -1
  805. package/dist/spec/usecase/ValidateManifestsUseCase.d.ts +0 -8
  806. package/dist/spec/usecase/ValidateManifestsUseCase.d.ts.map +0 -1
  807. package/dist/spec/usecase/ValidateManifestsUseCase.js +0 -101
  808. package/dist/spec/usecase/ValidateManifestsUseCase.js.map +0 -1
  809. package/dist/spec/usecase/dto/ValidateManifestsRequest.d.ts +0 -27
  810. package/dist/spec/usecase/dto/ValidateManifestsRequest.d.ts.map +0 -1
  811. package/dist/spec/usecase/dto/ValidateManifestsRequest.js +0 -2
  812. package/dist/spec/usecase/dto/ValidateManifestsRequest.js.map +0 -1
  813. package/dist/spec/usecase/dto/ValidateManifestsResponse.d.ts +0 -14
  814. package/dist/spec/usecase/dto/ValidateManifestsResponse.d.ts.map +0 -1
  815. package/dist/spec/usecase/dto/ValidateManifestsResponse.js +0 -2
  816. package/dist/spec/usecase/dto/ValidateManifestsResponse.js.map +0 -1
  817. package/dist/spec/usecase/dto/index.d.ts +0 -3
  818. package/dist/spec/usecase/dto/index.d.ts.map +0 -1
  819. package/dist/spec/usecase/dto/index.js +0 -3
  820. package/dist/spec/usecase/dto/index.js.map +0 -1
  821. package/dist/spec/usecase/index.d.ts +0 -10
  822. package/dist/spec/usecase/index.d.ts.map +0 -1
  823. package/dist/spec/usecase/index.js +0 -10
  824. package/dist/spec/usecase/index.js.map +0 -1
  825. package/dist/testing/cases/before-hook.d.ts +0 -4
  826. package/dist/testing/cases/before-hook.d.ts.map +0 -1
  827. package/dist/testing/cases/before-hook.js +0 -71
  828. package/dist/testing/cases/before-hook.js.map +0 -1
  829. package/dist/testing/cases/corpus.d.ts +0 -14
  830. package/dist/testing/cases/corpus.d.ts.map +0 -1
  831. package/dist/testing/cases/corpus.js +0 -93
  832. package/dist/testing/cases/corpus.js.map +0 -1
  833. package/dist/testing/cases/policy.d.ts +0 -87
  834. package/dist/testing/cases/policy.d.ts.map +0 -1
  835. package/dist/testing/cases/policy.js +0 -143
  836. package/dist/testing/cases/policy.js.map +0 -1
  837. package/dist/testing/cases/printer.d.ts +0 -4
  838. package/dist/testing/cases/printer.d.ts.map +0 -1
  839. package/dist/testing/cases/printer.js +0 -34
  840. package/dist/testing/cases/printer.js.map +0 -1
  841. package/dist/testing/cases/report-view.d.ts +0 -5
  842. package/dist/testing/cases/report-view.d.ts.map +0 -1
  843. package/dist/testing/cases/report-view.js +0 -51
  844. package/dist/testing/cases/report-view.js.map +0 -1
  845. package/dist/testing/cases/requisition.d.ts +0 -4
  846. package/dist/testing/cases/requisition.d.ts.map +0 -1
  847. package/dist/testing/cases/requisition.js +0 -38
  848. package/dist/testing/cases/requisition.js.map +0 -1
  849. package/dist/testing/cases/search-places.d.ts +0 -4
  850. package/dist/testing/cases/search-places.d.ts.map +0 -1
  851. package/dist/testing/cases/search-places.js +0 -60
  852. package/dist/testing/cases/search-places.js.map +0 -1
  853. package/dist/testing/cases/stock.d.ts +0 -4
  854. package/dist/testing/cases/stock.d.ts.map +0 -1
  855. package/dist/testing/cases/stock.js +0 -35
  856. package/dist/testing/cases/stock.js.map +0 -1
  857. package/dist/testing/cases/store.d.ts +0 -4
  858. package/dist/testing/cases/store.d.ts.map +0 -1
  859. package/dist/testing/cases/store.js +0 -100
  860. package/dist/testing/cases/store.js.map +0 -1
  861. package/dist/testing/harness.d.ts +0 -94
  862. package/dist/testing/harness.d.ts.map +0 -1
  863. package/dist/testing/harness.js +0 -168
  864. package/dist/testing/harness.js.map +0 -1
  865. package/dist/testing/index.d.ts.map +0 -1
  866. package/dist/testing/index.js.map +0 -1
  867. package/dist/testing/report.d.ts +0 -15
  868. package/dist/testing/report.d.ts.map +0 -1
  869. package/dist/testing/report.js +0 -22
  870. package/dist/testing/report.js.map +0 -1
  871. package/dist/web/createRestSurface.d.ts +0 -7
  872. package/dist/web/createRestSurface.d.ts.map +0 -1
  873. package/dist/web/createRestSurface.js +0 -41
  874. package/dist/web/createRestSurface.js.map +0 -1
  875. package/dist/web/index.d.ts +0 -2
  876. package/dist/web/index.d.ts.map +0 -1
  877. package/dist/web/index.js +0 -2
  878. package/dist/web/index.js.map +0 -1
  879. package/docs/adr/0001-four-atom-manifest-model.md +0 -440
  880. package/docs/adr/0002-closed-enums-for-bindings.md +0 -253
  881. package/docs/adr/0007-ai-as-primary-author.md +0 -473
  882. package/docs/adr/0008-structured-diagnostic-shape.md +0 -338
  883. package/docs/adr/0009-consumer-supplied-manifests.md +0 -149
  884. package/docs/adr/0010-locale-and-translates.md +0 -453
  885. package/docs/adr/0011-adapter-port-spec.md +0 -281
  886. package/docs/adr/0012-views-as-public-rest.md +0 -161
  887. package/docs/adr/0013-agent-provisioned-consumer-projects.md +0 -154
  888. package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +0 -939
  889. package/docs/adr/0016-site-semantic-layer.md +0 -70
  890. package/docs/adr/0017-media-multi-variant-agent-side-optimization.md +0 -181
  891. package/docs/adr/0018-core-starters-repository-boundary.md +0 -179
  892. package/docs/adr/0019-sealed-manifest-runtime-pipeline.md +0 -244
  893. package/docs/adr/0020-builtin-handler-contracts-and-matched-upsert.md +0 -199
  894. package/docs/adr/0021-retire-starter-scaffolding.md +0 -30
  895. package/docs/adr/0022-caller-observed-version-occ.md +0 -212
  896. package/docs/adr/0023-port-failure-contract.md +0 -69
  897. package/docs/adr/0024-manifest-native-schema-tables.md +0 -264
  898. package/docs/adr/0025-reserved-native-columns-and-published-only-public-views.md +0 -105
  899. package/docs/adr/0026-progressive-generate.md +0 -36
  900. package/docs/adr/0027-scheduled-procedure-triggers.md +0 -31
  901. package/docs/adr/0028-schema-ttl-expiry.md +0 -33
  902. package/docs/adr/0029-mcp-apps-interaction-contracts.md +0 -324
  903. package/docs/adr/0030-store.md +0 -40
  904. package/docs/adr/0031-host-cloud-core-contract.md +0 -102
  905. package/docs/adr/0032-store-centric-core-and-service-contract.md +0 -384
  906. package/docs/adr/0033-storage-converges-to-the-plan.md +0 -92
  907. package/docs/adr/0034-store-is-authored-as-sql.md +0 -192
  908. package/docs/adr/0035-sql-dialects.md +0 -154
  909. package/docs/adr/0036-host-and-dialect.md +0 -65
  910. package/docs/adr/0037-postgresql-is-the-reference-dialect.md +0 -125
  911. package/docs/adr/0038-bun-native-host.md +0 -63
  912. package/docs/adr/0039-postgresql-is-native.md +0 -130
  913. package/docs/adr/README.md +0 -98
  914. package/docs/adr/adr-lite-1376-admin-extensions.md +0 -330
  915. package/docs/adr/adr-lite-803-request-diagnostics.md +0 -38
  916. package/docs/adr/adr-lite-808-route-readiness.md +0 -47
  917. package/docs/adr/adr-lite-809-bounded-public-content.md +0 -71
  918. package/docs/adr/adr-lite-812-native-parity.md +0 -150
  919. package/docs/adr/adr-lite-823-home-statistics.md +0 -64
  920. package/docs/adr/adr-lite-842-mcp-authoring.md +0 -30
  921. package/docs/adr/adr-lite-845-frontend-client.md +0 -38
  922. package/docs/adr/adr-lite-861-admin-webmcp.md +0 -48
  923. package/docs/adr/adr-lite-909-admin-ui-kit.md +0 -37
  924. package/docs/examples/commerce-inventory.md +0 -498
  925. package/docs/examples/commerce.md +0 -254
  926. package/docs/examples/guarded-api.md +0 -238
  927. package/docs/examples/intake-hooks.md +0 -237
  928. package/docs/examples/intake.md +0 -127
  929. package/docs/examples/legal-documents.md +0 -218
  930. package/docs/examples/procurement.md +0 -226
  931. package/docs/examples/publication.md +0 -211
  932. package/docs/examples/reference-service/.mantle/generated/mantle.ts +0 -174
  933. package/docs/examples/reference-service/.mantle/generated/plan.json +0 -1936
  934. package/docs/examples/reference-service/README.md +0 -33
  935. package/docs/examples/reference-service/manifests/catalog.yaml +0 -103
  936. package/docs/examples/reference-service/manifests/orders.yaml +0 -120
  937. package/docs/examples/reference-service/mantle.config.json +0 -1
  938. package/docs/examples/reference-service/package.json +0 -27
  939. package/docs/examples/reference-service/smoke.mjs +0 -160
  940. package/docs/examples/reference-service/src/handlers.ts +0 -36
  941. package/docs/examples/reference-service/src/index.ts +0 -33
  942. package/docs/examples/reference-service/src/service.ts +0 -78
  943. package/docs/examples/reference-service/wrangler.jsonc +0 -26
  944. package/docs/examples/reservation.md +0 -142
  945. package/docs/handbook/guides/admin-extensions.md +0 -191
  946. /package/docs/examples/{reference-service → app-shell}/.dev.vars.example +0 -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,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`;
@@ -0,0 +1,71 @@
1
+ import type { Caller } from "@aotter/mantle/contracts";
2
+ import { store } from "../generated/generated.js";
3
+ import { translationLocales } from "../generated/translations.js";
4
+ import recipePage from "../generated/paged/views/published_recipes.js";
5
+ import translationPage from "../generated/paged/views/published_translations.js";
6
+ import { DEFAULT_LOCALE, LOCALES, type Locale, type Recipe, listHtml, llmsTxt, recipeHtml, recipeMarkdown, robotsTxt, sitemapXml } from "./pages.js";
7
+
8
+ // Visitor pages always read as anonymous, even for a signed-in admin, so drafts never reach public HTML.
9
+ const visitor: Caller = { kind: "anonymous" };
10
+ const page = /^\/(zh-TW|en)\/(?:recipes\/([a-z0-9-]+?)(\.md)?)?$/;
11
+
12
+ /**
13
+ * Visitor pages, tried before Admin, MCP and REST. Only these paths are matched; everything else falls through.
14
+ * / -> redirect to /zh-TW/
15
+ * /{locale}/ list (HTML)
16
+ * /{locale}/recipes/{slug} detail (HTML: SEO meta, hreflang, JSON-LD Recipe, short answer)
17
+ * /{locale}/recipes/{slug}.md Markdown alternate
18
+ * /llms.txt published recipes for AI agents
19
+ * /sitemap.xml, /robots.txt every published URL in every locale; robots keeps crawlers out of /admin/
20
+ */
21
+ export async function appRoutes(request: Request, env: Env, _context: { caller: () => Promise<Caller>; waitUntil: (task: Promise<unknown>) => void }): Promise<Response | undefined> {
22
+ const url = new URL(request.url), match = page.exec(url.pathname);
23
+ if (!["/", "/llms.txt", "/sitemap.xml", "/robots.txt"].includes(url.pathname) && !match) return undefined;
24
+ if (request.method !== "GET" && request.method !== "HEAD") return new Response(null, { status: 405, headers: { allow: "GET, HEAD" } });
25
+ // Absolute links use the configured origin, not the request Host.
26
+ const origin = env.PUBLIC_ORIGIN;
27
+ if (url.pathname === "/") return Response.redirect(`${origin}/${DEFAULT_LOCALE}/`, 302);
28
+
29
+ if (url.pathname === "/robots.txt") return send(request, robotsTxt(origin), "text/plain; charset=utf-8");
30
+ if (url.pathname === "/llms.txt" || url.pathname === "/sitemap.xml") {
31
+ const lists = await Promise.all(LOCALES.map(async locale => [locale, await list(env, locale)] as const));
32
+ return url.pathname === "/llms.txt"
33
+ ? send(request, llmsTxt(origin, lists), "text/plain; charset=utf-8")
34
+ : send(request, sitemapXml(origin, lists), "application/xml; charset=utf-8");
35
+ }
36
+
37
+ const locale = match![1] as Locale, slug = match![2], markdown = !!match![3];
38
+ if (!slug) return send(request, listHtml(origin, locale, await list(env, locale)), "text/html; charset=utf-8");
39
+ const found = await read(env, slug, locale);
40
+ if (!found) return new Response("Not found", { status: 404, headers: { "cache-control": "no-store" } });
41
+ return markdown
42
+ ? send(request, recipeMarkdown(found.recipe), "text/markdown; charset=utf-8", { link: `<${origin}/${locale}/recipes/${slug}>; rel="canonical"` })
43
+ : send(request, recipeHtml(origin, locale, found.recipe, found.locales), "text/html; charset=utf-8");
44
+ }
45
+
46
+ /** First page of published recipes in one locale. AGENT: page with `nextCursor` once a locale has more than 100 recipes. */
47
+ async function list(env: Env, locale: Locale) {
48
+ const result = locale === DEFAULT_LOCALE
49
+ ? await recipePage(env.DB, visitor, {}, { limit: 100 })
50
+ : await translationPage(env.DB, visitor, { locale }, { limit: 100 });
51
+ return result.rows.map(row => ({ slug: String(row.slug), title: String(row.title), summary: String(row.summary) }));
52
+ }
53
+
54
+ /** One published recipe in `locale` plus the locales it is published in (for hreflang). */
55
+ async function read(env: Env, slug: string, locale: Locale): Promise<{ recipe: Recipe; locales: Locale[] } | undefined> {
56
+ const recipes = store(env.DB, visitor);
57
+ const parent = await recipes.collection("recipes").first({ where: { slug } });
58
+ if (!parent) return undefined;
59
+ const translated = (await translationLocales(env.DB, visitor, "recipes", [parent]))[parent.id] ?? [];
60
+ const locales = LOCALES.filter(l => l === DEFAULT_LOCALE || translated.includes(l));
61
+ if (!locales.includes(locale)) return undefined;
62
+ // A translation read merges the parent's fields (total_minutes, servings, cover_url, ...); the child's own fields win.
63
+ const row = locale === DEFAULT_LOCALE ? parent : await recipes.collection("recipe_translations").first({ where: { slug, locale } });
64
+ if (!row) return undefined;
65
+ return { recipe: { ...row, slug, locale } as Recipe, locales };
66
+ }
67
+
68
+ function send(request: Request, body: string, type: string, headers: Record<string, string> = {}) {
69
+ // AGENT: choose a cache policy (and purge on publish) once pages sit behind a CDN.
70
+ return new Response(request.method === "HEAD" ? null : body, { headers: { "content-type": type, "cache-control": "no-store", ...headers } });
71
+ }
@@ -0,0 +1,12 @@
1
+ import type { SiteDefaults } from "@aotter/mantle/site";
2
+
3
+ /**
4
+ * Site defaults for `mantle generate ... --site src/site-config.ts`, which adds the media library tables and
5
+ * `generated/site.ts`. The shell does not pass `--site`; see README "Cover images" for the two lines to add.
6
+ */
7
+ export const siteDefaults = {
8
+ title: "Recipes",
9
+ description: "Home recipes in Traditional Chinese and English.",
10
+ locales: ["zh-TW", "en"],
11
+ media: { purposes: [{ name: "cover", required: ["image/jpeg,image/png,image/webp"], maxBytes: { "image/jpeg": 5_000_000, "image/png": 5_000_000, "image/webp": 5_000_000 } }] },
12
+ } satisfies SiteDefaults;