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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (495) hide show
  1. package/README.md +166 -298
  2. package/dist/auth/adapter.d.ts +32 -0
  3. package/dist/auth/adapter.js +288 -0
  4. package/dist/auth/admin.d.ts +3 -0
  5. package/dist/auth/admin.js +21 -0
  6. package/dist/auth/caller.d.ts +86 -0
  7. package/dist/auth/caller.js +155 -0
  8. package/dist/auth/hardening.d.ts +24 -0
  9. package/dist/auth/hardening.js +40 -0
  10. package/dist/auth/identity.d.ts +121 -0
  11. package/dist/auth/identity.js +203 -0
  12. package/dist/auth/oauth.d.ts +63 -0
  13. package/dist/auth/oauth.js +217 -0
  14. package/dist/auth/resource-seeds.js +10 -0
  15. package/dist/auth/routes.d.ts +32 -0
  16. package/dist/auth/routes.js +226 -0
  17. package/dist/build/compiler.d.ts +721 -0
  18. package/dist/build/compiler.js +7029 -0
  19. package/dist/build/spec/domain/model/ManifestGrammar.d.ts +439 -0
  20. package/dist/build/spec/domain/model/RuntimePlan.d.ts +168 -0
  21. package/dist/build/spec/domain/model/SqlIr.d.ts +129 -0
  22. package/dist/build/spec/domain/model/UserFields.d.ts +21 -0
  23. package/dist/build/spec/domain/service/PlanFingerprint.d.ts +7 -0
  24. package/dist/build/spec/kernel/diagnostic.d.ts +108 -0
  25. package/dist/bun/assets.d.ts +2 -0
  26. package/dist/bun/assets.js +24 -0
  27. package/dist/domain/adminOrder.js +24 -0
  28. package/dist/domain/columns.js +57 -0
  29. package/dist/domain/describe/chunk-ZVXBGP7Y.js +424 -0
  30. package/dist/domain/describe/index.d.ts +58 -0
  31. package/dist/domain/describe/index.js +115 -0
  32. package/dist/domain/describe/target-hash.d.ts +8 -0
  33. package/dist/domain/describe/target-hash.js +12 -0
  34. package/dist/domain/describe/targets.d.ts +40 -0
  35. package/dist/domain/entry.js +33 -0
  36. package/dist/domain/generated-tools.js +19 -0
  37. package/dist/domain/generation.d.ts +16 -0
  38. package/dist/domain/generation.js +1 -0
  39. package/dist/domain/input.js +17 -0
  40. package/dist/domain/model.js +1 -0
  41. package/dist/domain/paging.js +230 -0
  42. package/dist/domain/recipes.js +24 -0
  43. package/dist/domain/userReferences.js +5 -0
  44. package/dist/domain/wireScalars.js +47 -0
  45. package/dist/infrastructure/adminEntries.js +153 -0
  46. package/dist/infrastructure/adminPaging.js +72 -0
  47. package/dist/infrastructure/artifacts.js +71 -0
  48. package/dist/infrastructure/authSchema.js +83 -0
  49. package/dist/infrastructure/bundle.js +188 -0
  50. package/dist/infrastructure/cli.js +118 -0
  51. package/dist/infrastructure/generate.d.ts +12 -0
  52. package/dist/infrastructure/generate.js +189 -0
  53. package/dist/infrastructure/generateAuth.d.ts +8 -0
  54. package/dist/infrastructure/generateAuth.js +74 -0
  55. package/dist/infrastructure/help.js +60 -0
  56. package/dist/infrastructure/init.js +54 -0
  57. package/dist/infrastructure/inputs.js +41 -0
  58. package/dist/infrastructure/mounts.js +25 -0
  59. package/dist/infrastructure/wrangler.js +109 -0
  60. package/dist/kernel/authorization.d.ts +4 -0
  61. package/dist/kernel/authorization.js +16 -0
  62. package/dist/kernel/binds.d.ts +60 -0
  63. package/dist/kernel/binds.js +49 -0
  64. package/dist/kernel/caller-identity.d.ts +24 -0
  65. package/dist/kernel/caller-identity.js +22 -0
  66. package/dist/kernel/contract-error.d.ts +12 -0
  67. package/dist/kernel/contract-error.js +14 -0
  68. package/dist/kernel/contracts.d.ts +143 -0
  69. package/dist/kernel/contracts.js +97 -0
  70. package/dist/kernel/cron.d.ts +1 -0
  71. package/dist/kernel/cron.js +58 -0
  72. package/dist/kernel/dialect/common.d.ts +38 -0
  73. package/dist/kernel/dialect/common.js +6 -0
  74. package/dist/kernel/dialect/postgres.d.ts +16 -0
  75. package/dist/kernel/dialect/postgres.js +18 -0
  76. package/dist/kernel/dialect/sqlite.d.ts +20 -0
  77. package/dist/kernel/dialect/sqlite.js +34 -0
  78. package/dist/kernel/drain.d.ts +5 -0
  79. package/dist/kernel/drain.js +35 -0
  80. package/dist/kernel/driver/bun-sqlite.d.ts +19 -0
  81. package/dist/kernel/driver/bun-sqlite.js +77 -0
  82. package/dist/kernel/driver/d1.d.ts +24 -0
  83. package/dist/kernel/driver/d1.js +22 -0
  84. package/dist/kernel/driver/postgres.d.ts +25 -0
  85. package/dist/kernel/driver/postgres.js +99 -0
  86. package/dist/kernel/driver/transaction-context.d.ts +10 -0
  87. package/dist/kernel/driver/transaction-context.js +29 -0
  88. package/dist/kernel/email.d.ts +28 -0
  89. package/dist/kernel/email.js +38 -0
  90. package/dist/kernel/host-access.d.ts +10 -0
  91. package/dist/kernel/host-access.js +65 -0
  92. package/dist/kernel/host-caller.d.ts +39 -0
  93. package/dist/kernel/host-caller.js +56 -0
  94. package/dist/kernel/json-body.d.ts +4 -0
  95. package/dist/kernel/json-body.js +44 -0
  96. package/dist/kernel/mcp-apps.d.ts +69 -0
  97. package/dist/kernel/mcp-apps.js +40 -0
  98. package/dist/kernel/mcp-call.d.ts +5 -0
  99. package/dist/kernel/mcp-call.js +25 -0
  100. package/dist/kernel/mcp-handler.js +371 -0
  101. package/dist/kernel/mcp.d.ts +110 -0
  102. package/dist/kernel/mcp.js +50 -0
  103. package/dist/kernel/media.d.ts +27 -0
  104. package/dist/kernel/media.js +216 -0
  105. package/dist/kernel/paging.d.ts +55 -0
  106. package/dist/kernel/paging.js +95 -0
  107. package/dist/kernel/publishing.d.ts +4 -0
  108. package/dist/kernel/publishing.js +16 -0
  109. package/dist/kernel/r2-media.d.ts +42 -0
  110. package/dist/kernel/r2-media.js +78 -0
  111. package/dist/kernel/rest-dispatch.js +38 -0
  112. package/dist/kernel/rest-router.js +64 -0
  113. package/dist/kernel/rest.d.ts +58 -0
  114. package/dist/kernel/rest.js +41 -0
  115. package/dist/kernel/scalars.d.ts +6 -0
  116. package/dist/kernel/scalars.js +44 -0
  117. package/dist/kernel/search.js +5 -0
  118. package/dist/kernel/site-types.d.ts +146 -0
  119. package/dist/kernel/site-types.js +1 -0
  120. package/dist/kernel/site.d.ts +39 -0
  121. package/dist/kernel/site.js +32 -0
  122. package/dist/kernel/store.d.ts +175 -0
  123. package/dist/kernel/store.js +269 -0
  124. package/dist/kernel/surface.d.ts +10 -0
  125. package/dist/kernel/surface.js +19 -0
  126. package/dist/kernel/sweep.d.ts +17 -0
  127. package/dist/kernel/sweep.js +27 -0
  128. package/dist/kernel/translations.d.ts +1 -0
  129. package/dist/kernel/translations.js +13 -0
  130. package/dist/kernel/user-caller.d.ts +31 -0
  131. package/dist/kernel/user-caller.js +67 -0
  132. package/dist/kernel/user-field-name.js +3 -0
  133. package/dist/kernel/user-fields.d.ts +35 -0
  134. package/dist/kernel/user-fields.js +81 -0
  135. package/dist/kernel/validation.d.ts +15 -0
  136. package/dist/kernel/validation.js +54 -0
  137. package/dist/kernel/web-gateway.js +68 -0
  138. package/dist/kernel/web-head.js +15 -0
  139. package/dist/kernel/web-index.js +40 -0
  140. package/dist/kernel/web-response.js +8 -0
  141. package/dist/kernel/web.d.ts +77 -0
  142. package/dist/kernel/web.js +124 -0
  143. package/dist/kernel/where.js +27 -0
  144. package/dist/testing/index.d.ts +1 -0
  145. package/dist/testing/index.js +1 -0
  146. package/dist/usecase/adminBindings.js +143 -0
  147. package/dist/usecase/adminClient.js +87 -0
  148. package/dist/usecase/adminDescriptors.js +107 -0
  149. package/dist/usecase/generate.js +166 -0
  150. package/dist/usecase/handlers.js +21 -0
  151. package/dist/usecase/mcp-media.js +17 -0
  152. package/dist/usecase/mcp.js +100 -0
  153. package/dist/usecase/paging.js +71 -0
  154. package/dist/usecase/publishing.js +63 -0
  155. package/dist/usecase/refusals.js +27 -0
  156. package/dist/usecase/rest.js +54 -0
  157. package/dist/usecase/search.js +7 -0
  158. package/dist/usecase/site.js +39 -0
  159. package/dist/usecase/stores.js +64 -0
  160. package/dist/usecase/tool-version.js +9 -0
  161. package/dist/usecase/translations.js +78 -0
  162. package/dist/usecase/ttl.js +20 -0
  163. package/dist/usecase/validation.js +241 -0
  164. package/dist/usecase/web.js +23 -0
  165. package/docs/examples/README.md +39 -18
  166. package/docs/examples/app-shell/.dev.vars.example +4 -0
  167. package/docs/examples/app-shell/README.md +121 -0
  168. package/docs/examples/app-shell/accept.mjs +91 -0
  169. package/docs/examples/app-shell/gitignore +7 -0
  170. package/docs/examples/app-shell/package.json +43 -0
  171. package/docs/examples/app-shell/pnpm-workspace.yaml +8 -0
  172. package/docs/examples/app-shell/smoke-kit.mjs +168 -0
  173. package/docs/examples/app-shell/src/auth.ts +58 -0
  174. package/docs/examples/app-shell/src/handlers.ts +5 -0
  175. package/docs/examples/app-shell/src/index.ts +4 -0
  176. package/docs/examples/app-shell/src/routes.ts +14 -0
  177. package/docs/examples/app-shell/src/schedule.ts +8 -0
  178. package/docs/examples/app-shell/src/service.ts +70 -0
  179. package/docs/examples/app-shell/src/sign-in.ts +41 -0
  180. package/docs/examples/app-shell/src/signup.ts +8 -0
  181. package/docs/examples/app-shell/tsconfig.json +26 -0
  182. package/docs/examples/app-shell/worker-configuration.d.ts +22 -0
  183. package/docs/examples/app-shell/wrangler.jsonc +62 -0
  184. package/docs/examples/club-events/README.md +65 -0
  185. package/docs/examples/club-events/manifests/club-events.yaml +247 -0
  186. package/docs/examples/club-events/smoke.mjs +127 -0
  187. package/docs/examples/club-events/src/handlers.ts +25 -0
  188. package/docs/examples/club-events/src/signup.ts +9 -0
  189. package/docs/examples/generated-web/README.md +78 -0
  190. package/docs/examples/generated-web/posts.yaml +38 -0
  191. package/docs/examples/generated-web/seed.sql +6 -0
  192. package/docs/examples/generated-web/src/worker.ts +43 -0
  193. package/docs/examples/generated-web/tsconfig.json +8 -0
  194. package/docs/examples/generated-web/wrangler.jsonc +8 -0
  195. package/docs/examples/purchasing/.dev.vars.example +6 -0
  196. package/docs/examples/purchasing/README.md +99 -0
  197. package/docs/examples/purchasing/manifests/purchasing.yaml +374 -0
  198. package/docs/examples/purchasing/seed.sql +4 -0
  199. package/docs/examples/purchasing/smoke.mjs +191 -0
  200. package/docs/examples/purchasing/src/admin.tsx +13 -0
  201. package/docs/examples/purchasing/src/handlers.ts +33 -0
  202. package/docs/examples/purchasing/src/notify.ts +29 -0
  203. package/docs/examples/purchasing/src/requests-page.ts +57 -0
  204. package/docs/examples/purchasing/src/routes.ts +53 -0
  205. package/docs/examples/purchasing/src/schedule.ts +31 -0
  206. package/docs/examples/purchasing/src/signup.ts +5 -0
  207. package/docs/examples/purchasing/src/supplier.ts +32 -0
  208. package/docs/examples/purchasing/wrangler.jsonc +69 -0
  209. package/docs/examples/recipes/README.md +60 -0
  210. package/docs/examples/recipes/manifests/recipes.yaml +75 -0
  211. package/docs/examples/recipes/smoke.mjs +102 -0
  212. package/docs/examples/recipes/src/media.ts +13 -0
  213. package/docs/examples/recipes/src/pages.ts +99 -0
  214. package/docs/examples/recipes/src/routes.ts +71 -0
  215. package/docs/examples/recipes/src/site-config.ts +12 -0
  216. package/docs/examples/recipes/wrangler.jsonc +74 -0
  217. package/docs/handbook/cloudflare/authentication.md +180 -322
  218. package/docs/handbook/cloudflare/chatgpt-sites.md +5 -23
  219. package/docs/handbook/cloudflare/deploy-and-operate.md +55 -77
  220. package/docs/handbook/cloudflare/media-r2.md +61 -205
  221. package/docs/handbook/cloudflare/service-entry.md +239 -0
  222. package/docs/handbook/concepts/authorization.md +10 -107
  223. package/docs/handbook/concepts/four-atoms.md +9 -168
  224. package/docs/handbook/concepts/lifecycle-and-locales.md +9 -132
  225. package/docs/handbook/concepts/mcp-and-agents.md +24 -121
  226. package/docs/handbook/concepts/procedures-and-triggers.md +5 -217
  227. package/docs/handbook/concepts/runtime-and-adapters.md +15 -169
  228. package/docs/handbook/concepts/views.md +10 -185
  229. package/docs/handbook/examples/hub.md +3 -8
  230. package/docs/handbook/guides/adapter-guide.md +11 -0
  231. package/docs/handbook/guides/admin-ui.md +115 -133
  232. package/docs/handbook/guides/agent-setup.md +64 -74
  233. package/docs/handbook/guides/agent-workflow-acceptance.md +232 -0
  234. package/docs/handbook/guides/email.md +86 -0
  235. package/docs/handbook/guides/local-testing.md +40 -0
  236. package/docs/handbook/guides/mcp-apps.md +560 -0
  237. package/docs/handbook/guides/native-admin.md +566 -0
  238. package/docs/handbook/guides/native-execution.md +133 -0
  239. package/docs/handbook/guides/oauth.md +219 -0
  240. package/docs/handbook/guides/postgres-adapters.md +56 -0
  241. package/docs/handbook/guides/public-pages.md +87 -0
  242. package/docs/handbook/guides/size-and-startup.md +9 -0
  243. package/docs/handbook/guides/sql-patterns.md +266 -0
  244. package/docs/handbook/guides/typed-queries.md +211 -107
  245. package/docs/handbook/navigation.json +176 -55
  246. package/docs/handbook/reference/admin-descriptors.md +53 -0
  247. package/docs/handbook/reference/authorization.md +39 -266
  248. package/docs/handbook/reference/describe.md +56 -0
  249. package/docs/handbook/reference/diagnostics.md +19 -186
  250. package/docs/handbook/reference/features.md +45 -55
  251. package/docs/handbook/reference/manifest.md +6 -129
  252. package/docs/handbook/reference/procedure.md +11 -303
  253. package/docs/handbook/reference/schema.md +64 -240
  254. package/docs/handbook/reference/site-config.md +5 -218
  255. package/docs/handbook/reference/sql.md +64 -0
  256. package/docs/handbook/reference/surface.md +392 -201
  257. package/docs/handbook/reference/trigger.md +56 -192
  258. package/docs/handbook/reference/view.md +24 -243
  259. package/docs/handbook/releases/index.md +244 -77
  260. package/docs/handbook/start/agents.md +45 -0
  261. package/docs/handbook/start/overview.md +9 -48
  262. package/docs/handbook/start/project-and-cli.md +51 -104
  263. package/docs/handbook/start/quickstart-worker.md +43 -178
  264. package/docs/skills/develop/SKILL.md +79 -249
  265. package/docs/skills/integrate/SKILL.md +39 -50
  266. package/docs/skills/media-gc/SKILL.md +34 -10
  267. package/docs/skills/provision/SKILL.md +36 -186
  268. package/docs/skills/update/SKILL.md +23 -38
  269. package/docs/source/scripts/postgres-adapter.ts +101 -0
  270. package/docs/upgrade-0.1-to-0.2.md +227 -0
  271. package/package.json +120 -131
  272. package/skills/README.md +48 -108
  273. package/skills/mantle/SKILL.md +76 -0
  274. package/dist/admin-ui.d.ts +0 -2
  275. package/dist/admin-ui.d.ts.map +0 -1
  276. package/dist/admin-ui.js +0 -2
  277. package/dist/admin-ui.js.map +0 -1
  278. package/dist/admin.d.ts +0 -2
  279. package/dist/admin.d.ts.map +0 -1
  280. package/dist/admin.js +0 -2
  281. package/dist/admin.js.map +0 -1
  282. package/dist/auth.d.ts +0 -2
  283. package/dist/auth.d.ts.map +0 -1
  284. package/dist/auth.js +0 -2
  285. package/dist/auth.js.map +0 -1
  286. package/dist/bun.d.ts +0 -2
  287. package/dist/bun.d.ts.map +0 -1
  288. package/dist/bun.js +0 -2
  289. package/dist/bun.js.map +0 -1
  290. package/dist/cli/generate-cloudflare.d.ts +0 -7
  291. package/dist/cli/generate-cloudflare.d.ts.map +0 -1
  292. package/dist/cli/generate-cloudflare.js +0 -123
  293. package/dist/cli/generate-cloudflare.js.map +0 -1
  294. package/dist/cli/generate-project.d.ts +0 -25
  295. package/dist/cli/generate-project.d.ts.map +0 -1
  296. package/dist/cli/generate-project.js +0 -282
  297. package/dist/cli/generate-project.js.map +0 -1
  298. package/dist/cli/generate-sites.d.ts +0 -8
  299. package/dist/cli/generate-sites.d.ts.map +0 -1
  300. package/dist/cli/generate-sites.js +0 -273
  301. package/dist/cli/generate-sites.js.map +0 -1
  302. package/dist/cli/generate.d.ts +0 -20
  303. package/dist/cli/generate.d.ts.map +0 -1
  304. package/dist/cli/generate.js +0 -299
  305. package/dist/cli/generate.js.map +0 -1
  306. package/dist/cli/harness.d.ts +0 -3
  307. package/dist/cli/harness.d.ts.map +0 -1
  308. package/dist/cli/harness.js +0 -214
  309. package/dist/cli/harness.js.map +0 -1
  310. package/dist/cli/main.d.ts +0 -3
  311. package/dist/cli/main.d.ts.map +0 -1
  312. package/dist/cli/main.js +0 -77
  313. package/dist/cli/main.js.map +0 -1
  314. package/dist/cli/skills.d.ts +0 -3
  315. package/dist/cli/skills.d.ts.map +0 -1
  316. package/dist/cli/skills.js +0 -128
  317. package/dist/cli/skills.js.map +0 -1
  318. package/dist/cloudflare.d.ts +0 -2
  319. package/dist/cloudflare.d.ts.map +0 -1
  320. package/dist/cloudflare.js +0 -2
  321. package/dist/cloudflare.js.map +0 -1
  322. package/dist/codegen/emitMantleModule.d.ts +0 -22
  323. package/dist/codegen/emitMantleModule.d.ts.map +0 -1
  324. package/dist/codegen/emitMantleModule.js +0 -291
  325. package/dist/codegen/emitMantleModule.js.map +0 -1
  326. package/dist/codegen.d.ts +0 -2
  327. package/dist/codegen.d.ts.map +0 -1
  328. package/dist/codegen.js +0 -2
  329. package/dist/codegen.js.map +0 -1
  330. package/dist/runtime-testing.d.ts +0 -2
  331. package/dist/runtime-testing.d.ts.map +0 -1
  332. package/dist/runtime-testing.js +0 -2
  333. package/dist/runtime-testing.js.map +0 -1
  334. package/dist/runtime.d.ts +0 -2
  335. package/dist/runtime.d.ts.map +0 -1
  336. package/dist/runtime.js +0 -2
  337. package/dist/runtime.js.map +0 -1
  338. package/dist/spec.d.ts +0 -2
  339. package/dist/spec.d.ts.map +0 -1
  340. package/dist/spec.js +0 -2
  341. package/dist/spec.js.map +0 -1
  342. package/dist/vercel-libsql.d.ts +0 -2
  343. package/dist/vercel-libsql.d.ts.map +0 -1
  344. package/dist/vercel-libsql.js +0 -2
  345. package/dist/vercel-libsql.js.map +0 -1
  346. package/dist/vercel.d.ts +0 -2
  347. package/dist/vercel.d.ts.map +0 -1
  348. package/dist/vercel.js +0 -2
  349. package/dist/vercel.js.map +0 -1
  350. package/dist/web.d.ts +0 -2
  351. package/dist/web.d.ts.map +0 -1
  352. package/dist/web.js +0 -2
  353. package/dist/web.js.map +0 -1
  354. package/docs/adapter-guide.md +0 -222
  355. package/docs/adr/0001-four-atom-manifest-model.md +0 -440
  356. package/docs/adr/0002-closed-enums-for-bindings.md +0 -253
  357. package/docs/adr/0007-ai-as-primary-author.md +0 -473
  358. package/docs/adr/0008-structured-diagnostic-shape.md +0 -338
  359. package/docs/adr/0009-consumer-supplied-manifests.md +0 -149
  360. package/docs/adr/0010-locale-and-translates.md +0 -453
  361. package/docs/adr/0011-adapter-port-spec.md +0 -281
  362. package/docs/adr/0012-views-as-public-rest.md +0 -161
  363. package/docs/adr/0013-agent-provisioned-consumer-projects.md +0 -154
  364. package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +0 -900
  365. package/docs/adr/0016-site-semantic-layer.md +0 -70
  366. package/docs/adr/0017-media-multi-variant-agent-side-optimization.md +0 -181
  367. package/docs/adr/0018-core-starters-repository-boundary.md +0 -179
  368. package/docs/adr/0019-sealed-manifest-runtime-pipeline.md +0 -240
  369. package/docs/adr/0020-builtin-handler-contracts-and-matched-upsert.md +0 -199
  370. package/docs/adr/0021-retire-starter-scaffolding.md +0 -30
  371. package/docs/adr/0022-caller-observed-version-occ.md +0 -212
  372. package/docs/adr/0023-port-failure-contract.md +0 -69
  373. package/docs/adr/0024-manifest-native-schema-tables.md +0 -264
  374. package/docs/adr/0025-reserved-native-columns-and-published-only-public-views.md +0 -105
  375. package/docs/adr/0026-progressive-generate.md +0 -36
  376. package/docs/adr/0027-scheduled-procedure-triggers.md +0 -31
  377. package/docs/adr/0028-schema-ttl-expiry.md +0 -33
  378. package/docs/adr/README.md +0 -81
  379. package/docs/adr/adr-lite-803-request-diagnostics.md +0 -38
  380. package/docs/adr/adr-lite-808-route-readiness.md +0 -47
  381. package/docs/adr/adr-lite-809-bounded-public-content.md +0 -71
  382. package/docs/adr/adr-lite-812-native-parity.md +0 -150
  383. package/docs/adr/adr-lite-823-home-statistics.md +0 -64
  384. package/docs/adr/adr-lite-842-mcp-authoring.md +0 -30
  385. package/docs/adr/adr-lite-845-frontend-client.md +0 -38
  386. package/docs/adr/adr-lite-861-admin-webmcp.md +0 -42
  387. package/docs/adr/adr-lite-909-admin-ui-kit.md +0 -37
  388. package/docs/agent-prompts.md +0 -64
  389. package/docs/api-mcp-authorization.md +0 -5
  390. package/docs/assets/mantle-admin-developer.png +0 -0
  391. package/docs/assets/mantle-admin-operations.png +0 -0
  392. package/docs/assets/mantle-hero.jpg +0 -0
  393. package/docs/auth-hosting-model.md +0 -166
  394. package/docs/cloudflare-low-level-composition.md +0 -5
  395. package/docs/consumer-onboarding-audit.md +0 -65
  396. package/docs/deferred-lifecycle-queues.md +0 -5
  397. package/docs/design-atoms.md +0 -16
  398. package/docs/design-references/editorial-blog-2026-05-05.md +0 -218
  399. package/docs/direct-authoring.md +0 -5
  400. package/docs/examples/builtin-commerce.md +0 -269
  401. package/docs/examples/builtin-intake.md +0 -143
  402. package/docs/examples/builtin-legal-documents.md +0 -189
  403. package/docs/examples/builtin-procurement.md +0 -241
  404. package/docs/examples/builtin-publication.md +0 -241
  405. package/docs/examples/builtin-reservation.md +0 -149
  406. package/docs/examples/cf-primitives-commerce-inventory.md +0 -809
  407. package/docs/examples/cf-primitives-guarded-api.md +0 -429
  408. package/docs/examples/cf-primitives-intake-hooks.md +0 -319
  409. package/docs/examples/host-chatgpt-sites/.openai/hosting.json +0 -1
  410. package/docs/examples/host-chatgpt-sites/README.md +0 -70
  411. package/docs/examples/host-chatgpt-sites/drizzle/0000_sites_users.sql +0 -8
  412. package/docs/examples/host-chatgpt-sites/drizzle/0001_mantle.sql +0 -294
  413. package/docs/examples/host-chatgpt-sites/drizzle/0002_article_cover.sql +0 -5
  414. package/docs/examples/host-chatgpt-sites/drizzle/0003_store_instance_id.sql +0 -1
  415. package/docs/examples/host-chatgpt-sites/drizzle/0004_mantle.sql +0 -10
  416. package/docs/examples/host-chatgpt-sites/drizzle/0005_mantle.sql +0 -24
  417. package/docs/examples/host-chatgpt-sites/drizzle/meta/_journal.json +0 -1
  418. package/docs/examples/host-chatgpt-sites/drizzle/meta/mantle-state.json +0 -60
  419. package/docs/examples/host-chatgpt-sites/manifests/site.yaml +0 -27
  420. package/docs/examples/host-chatgpt-sites/package.json +0 -1
  421. package/docs/examples/host-chatgpt-sites/public/site.css +0 -1
  422. package/docs/examples/host-chatgpt-sites/scripts/build.mjs +0 -13
  423. package/docs/examples/host-chatgpt-sites/scripts/check.mjs +0 -101
  424. package/docs/examples/host-chatgpt-sites/scripts/migration.mjs +0 -43
  425. package/docs/examples/host-chatgpt-sites/src/chatgpt-auth.ts +0 -66
  426. package/docs/examples/host-chatgpt-sites/src/index.ts +0 -46
  427. package/docs/examples/host-chatgpt-sites/src/mcp.ts +0 -49
  428. package/docs/examples/host-chatgpt-sites/src/media.ts +0 -109
  429. package/docs/examples/host-chatgpt-sites/src/r2-lab.ts +0 -38
  430. package/docs/examples/host-chatgpt-sites/src/storage-fingerprint.json +0 -1
  431. package/docs/examples/host-chatgpt-sites/src/web.ts +0 -35
  432. package/docs/examples/host-chatgpt-sites/tsconfig.json +0 -1
  433. package/docs/examples/host-chatgpt-sites/wrangler.jsonc +0 -10
  434. package/docs/examples/host-local-admin-otp/.dev.vars.example +0 -3
  435. package/docs/examples/host-local-admin-otp/README.md +0 -80
  436. package/docs/examples/host-local-admin-otp/ensure-dev-vars.mjs +0 -5
  437. package/docs/examples/host-local-admin-otp/manifests/site.yaml +0 -25
  438. package/docs/examples/host-local-admin-otp/package.json +0 -29
  439. package/docs/examples/host-local-admin-otp/public/.gitkeep +0 -1
  440. package/docs/examples/host-local-admin-otp/smoke.mjs +0 -141
  441. package/docs/examples/host-local-admin-otp/src/index.ts +0 -54
  442. package/docs/examples/host-local-admin-otp/tsconfig.json +0 -17
  443. package/docs/examples/host-local-admin-otp/wrangler.jsonc +0 -23
  444. package/docs/examples/host-minimal-worker/README.md +0 -41
  445. package/docs/examples/host-minimal-worker/manifests/site.yaml +0 -25
  446. package/docs/examples/host-minimal-worker/package.json +0 -26
  447. package/docs/examples/host-minimal-worker/smoke.mjs +0 -23
  448. package/docs/examples/host-minimal-worker/src/index.ts +0 -4
  449. package/docs/examples/host-minimal-worker/tsconfig.json +0 -17
  450. package/docs/examples/host-minimal-worker/wrangler.jsonc +0 -22
  451. package/docs/handbook/chatgpt-sites/equipment-checkout.md +0 -231
  452. package/docs/handbook/chatgpt-sites/generated-app.md +0 -84
  453. package/docs/handbook/chatgpt-sites/host-reference.md +0 -113
  454. package/docs/handbook/chatgpt-sites/index.md +0 -133
  455. package/docs/handbook/cloudflare/bindings.md +0 -291
  456. package/docs/handbook/cloudflare/conventional-worker.md +0 -166
  457. package/docs/handbook/cloudflare/deferred-hooks-queues.md +0 -170
  458. package/docs/handbook/cloudflare/low-level-composition.md +0 -169
  459. package/docs/handbook/cloudflare/public-web.md +0 -144
  460. package/docs/handbook/cloudflare/site-chrome.md +0 -75
  461. package/docs/handbook/examples/commerce-transaction.md +0 -11
  462. package/docs/handbook/examples/commerce.md +0 -11
  463. package/docs/handbook/examples/guarded-api.md +0 -10
  464. package/docs/handbook/examples/intake-form.md +0 -12
  465. package/docs/handbook/examples/intake-hooks.md +0 -11
  466. package/docs/handbook/examples/legal-documents.md +0 -10
  467. package/docs/handbook/examples/procurement-approvals.md +0 -10
  468. package/docs/handbook/examples/publication.md +0 -10
  469. package/docs/handbook/examples/reservation.md +0 -10
  470. package/docs/handbook/start/quickstart-admin.md +0 -52
  471. package/docs/labels.md +0 -71
  472. package/docs/media-uploads.md +0 -5
  473. package/docs/migration-0.1.2.md +0 -13
  474. package/docs/performance-harness.md +0 -228
  475. package/docs/release-process.md +0 -244
  476. package/docs/schema-indexes.md +0 -5
  477. package/docs/sealed-pipeline-ownership.md +0 -101
  478. package/docs/skills/plugin/SKILL.md +0 -132
  479. package/docs/skills/theme/SKILL.md +0 -77
  480. package/docs/spec-only-host-adoption.md +0 -157
  481. package/docs/transaction-patterns.md +0 -5
  482. package/skills/develop/SKILL.md +0 -258
  483. package/skills/install/SKILL.md +0 -112
  484. package/skills/integrate/SKILL.md +0 -60
  485. package/skills/media-gc/SKILL.md +0 -85
  486. package/skills/plugin/SKILL.md +0 -132
  487. package/skills/provision/SKILL.md +0 -194
  488. package/skills/theme/SKILL.md +0 -77
  489. package/skills/update/SKILL.md +0 -51
  490. package/templates/chatgpt-sites/auth.ts +0 -66
  491. package/templates/chatgpt-sites/build.mjs +0 -14
  492. package/templates/chatgpt-sites/env.ts +0 -1
  493. package/templates/chatgpt-sites/mcp.ts +0 -48
  494. package/templates/chatgpt-sites/public-mcp.ts +0 -29
  495. package/templates/chatgpt-sites/smoke-local.mjs +0 -20
@@ -0,0 +1,266 @@
1
+ ---
2
+ description: Seven SQL patterns most apps need, each a complete manifest that compiles on every target, with the PostgreSQL spelling and what each refuses.
3
+ ---
4
+ # SQL patterns
5
+
6
+ Each section is a business need, the manifest that meets it, and what the database refuses. Every block here is
7
+ generated by the docs check, and `packages/mantle/tests/sql-patterns.test.ts` runs the same manifest on bun:sqlite,
8
+ local D1 and PostgreSQL through the generated REST boundary. The SQL is the SQLite and D1 spelling; where PostgreSQL
9
+ differs, the section says so. What each dialect accepts is listed in [the SQL reference](../reference/sql.md).
10
+
11
+ For an order with any number of lines, see [several rows that commit together](typed-queries.md#several-rows-that-commit-together).
12
+
13
+ The sections share two Schemas:
14
+
15
+ ```yaml
16
+ apiVersion: cms.mantle.aotter.net/v2
17
+ kind: Schema
18
+ metadata: { name: products }
19
+ spec:
20
+ title: Products
21
+ lifecycle: operational
22
+ uniqueIndexes: [[sku]]
23
+ checks: ["reserved <= stock"]
24
+ schema:
25
+ type: object
26
+ required: [sku, name, price, stock, reserved, active]
27
+ properties:
28
+ sku: { type: string }
29
+ name: { type: string, minLength: 1 }
30
+ note: { type: string }
31
+ price: { type: integer, minimum: 0 }
32
+ stock: { type: integer, minimum: 0 }
33
+ reserved: { type: integer, minimum: 0 }
34
+ active: { type: boolean }
35
+ ---
36
+ apiVersion: cms.mantle.aotter.net/v2
37
+ kind: Schema
38
+ metadata: { name: orders }
39
+ spec:
40
+ title: Orders
41
+ lifecycle: operational
42
+ indexes: [[productId, createdAt, id]]
43
+ schema:
44
+ type: object
45
+ required: [productId, qty, total, orderStatus]
46
+ properties:
47
+ productId: { type: string, x-mantle-ref: products }
48
+ qty: { type: integer, minimum: 1 }
49
+ total: { type: integer, minimum: 0 }
50
+ orderStatus: { type: string, enum: [reserved, pending_approval] }
51
+ ```
52
+
53
+ ## Choose a status while writing: CASE
54
+
55
+ An order over 5000 waits for a manager; a smaller one reserves stock at once. `CASE` picks the status inside the
56
+ INSERT, and the reservation runs only for a small order, in the same program, so the two never disagree. The total
57
+ comes from the catalogue's `price`, never from the caller: a price in the input would let a caller choose the total,
58
+ and with it whether the order needs approval.
59
+
60
+ ```yaml
61
+ apiVersion: cms.mantle.aotter.net/v2
62
+ kind: Procedure
63
+ metadata: { name: place-order }
64
+ spec:
65
+ description: Order a product. An order over 5000 waits for approval and reserves nothing yet.
66
+ requires: { auth: { all: [ctx.user] } }
67
+ input:
68
+ type: object
69
+ additionalProperties: false
70
+ required: [productId, qty]
71
+ properties:
72
+ productId: { type: string }
73
+ qty: { type: integer, minimum: 1 }
74
+ output: { type: object }
75
+ handler:
76
+ sql: |
77
+ INSERT INTO orders (productId, qty, total, orderStatus)
78
+ SELECT p.id, input.qty, input.qty * p.price,
79
+ CASE WHEN input.qty * p.price > 5000 THEN 'pending_approval' ELSE 'reserved' END
80
+ FROM products p WHERE p.id = input.productId AND p.active
81
+ RETURNING id, orderStatus AS "orderStatus";
82
+ UPDATE products SET reserved = reserved + input.qty
83
+ WHERE id IN (SELECT p.id FROM products p WHERE p.id = input.productId AND p.active AND input.qty * p.price <= 5000)
84
+ ```
85
+
86
+ The second statement is written as `WHERE id IN (SELECT …)` on purpose. `WHERE id = input.productId AND …` is a
87
+ row-classified UPDATE, which must change exactly one row: for an order that waits, it would change none and refuse the
88
+ whole call with `CONFLICT`. A write that should do nothing when its condition is false needs the set form. The first
89
+ statement's join on `products` with `p.active` means an unknown or discontinued product writes no order.
90
+
91
+ ## Approve before reserving
92
+
93
+ Approval changes the status and reserves the stock in one program. The first statement is the guard: a stale
94
+ `expectedVersion` or an order that is no longer `pending_approval` matches no row, which is `CONFLICT`. The second
95
+ statement fails `reserved <= stock` when the stock is gone by now, and that rolls back the status change too, so the
96
+ order stays pending. [Purchasing](../../examples/purchasing/README.md) does the same with a department budget
97
+ (`approve-request-write`), and approves small requests at submission with `CASE` (`submit-request`).
98
+
99
+ ```yaml
100
+ apiVersion: cms.mantle.aotter.net/v2
101
+ kind: Procedure
102
+ metadata: { name: approve-order }
103
+ spec:
104
+ description: Approve a pending order at the version you reviewed; reserves its stock.
105
+ requires: { auth: { all: [ctx.admin] } }
106
+ input:
107
+ type: object
108
+ additionalProperties: false
109
+ required: [id, expectedVersion]
110
+ properties:
111
+ id: { type: string }
112
+ expectedVersion: { type: integer }
113
+ output: { type: object }
114
+ handler:
115
+ sql: |
116
+ UPDATE orders SET orderStatus = 'reserved'
117
+ WHERE id = input.id AND version = input.expectedVersion AND orderStatus = 'pending_approval'
118
+ RETURNING id, version;
119
+ UPDATE products SET reserved = reserved + (SELECT o.qty FROM orders o WHERE o.id = input.id)
120
+ WHERE id = (SELECT o.productId FROM orders o WHERE o.id = input.id)
121
+ ```
122
+
123
+ ## Change only the fields given: COALESCE
124
+
125
+ A partial update keeps every field the caller left out: `COALESCE(input.name, name)` is the new name when one is given
126
+ and the current one otherwise. `NULLIF(input.note, '')` also treats an empty string as left out. Because of that, this
127
+ Procedure cannot clear a note; give clearing its own input (for example `clearNote: true` with
128
+ `CASE WHEN input.clearNote THEN NULL ELSE … END`) if the app needs it. The version guard makes a stale edit a 409.
129
+
130
+ ```yaml
131
+ apiVersion: cms.mantle.aotter.net/v2
132
+ kind: Procedure
133
+ metadata: { name: update-product }
134
+ spec:
135
+ description: Change a product's name or note; a field you leave out keeps its value.
136
+ requires: { auth: { all: [ctx.admin] } }
137
+ input:
138
+ type: object
139
+ additionalProperties: false
140
+ required: [id, expectedVersion]
141
+ properties:
142
+ id: { type: string }
143
+ expectedVersion: { type: integer }
144
+ name: { type: string, minLength: 1 }
145
+ note: { type: string }
146
+ output: { type: object }
147
+ handler:
148
+ sql: |
149
+ UPDATE products SET name = COALESCE(input.name, name), note = COALESCE(NULLIF(input.note, ''), note)
150
+ WHERE id = input.id AND version = input.expectedVersion
151
+ RETURNING id, version, name, note
152
+ ```
153
+
154
+ ## Create or add: ON CONFLICT DO UPDATE
155
+
156
+ Restocking a SKU creates the product the first time and adds to its stock afterwards, in one statement. The conflict
157
+ target must be a declared unique index (`uniqueIndexes: [[sku]]`). `excluded` is the row the INSERT proposed;
158
+ qualify the existing row with the table name (`products.stock`).
159
+
160
+ ```yaml
161
+ apiVersion: cms.mantle.aotter.net/v2
162
+ kind: Procedure
163
+ metadata: { name: restock }
164
+ spec:
165
+ description: Add stock by SKU, creating the product the first time.
166
+ requires: { auth: { all: [ctx.admin] } }
167
+ input:
168
+ type: object
169
+ additionalProperties: false
170
+ required: [sku, name, price, qty]
171
+ properties:
172
+ sku: { type: string }
173
+ name: { type: string, minLength: 1 }
174
+ price: { type: integer, minimum: 0 }
175
+ qty: { type: integer, minimum: 1 }
176
+ output: { type: object }
177
+ handler:
178
+ sql: |
179
+ INSERT INTO products (sku, name, price, stock, reserved, active) VALUES (input.sku, input.name, input.price, input.qty, 0, true)
180
+ ON CONFLICT (sku) DO UPDATE SET stock = products.stock + excluded.stock
181
+ RETURNING id, stock
182
+ ```
183
+
184
+ ## Refuse while something refers to it: NOT EXISTS
185
+
186
+ A product cannot be discontinued while an order for it waits for approval, since approving that order would reserve
187
+ stock of a product no longer sold. `NOT EXISTS (…)` is part of the guard, so while such an order exists the
188
+ row-classified UPDATE changes nothing and the call is `CONFLICT`; the product stays on sale. Use `EXISTS` the same way
189
+ for "only if a related row exists". [Purchasing](../../examples/purchasing/README.md) uses `NOT EXISTS` to write the
190
+ ledger row only for the request its program just approved (`submit-request`).
191
+
192
+ ```yaml
193
+ apiVersion: cms.mantle.aotter.net/v2
194
+ kind: Procedure
195
+ metadata: { name: discontinue-product }
196
+ spec:
197
+ description: Stop selling a product; refused while an order for it waits for approval.
198
+ requires: { auth: { all: [ctx.admin] } }
199
+ input:
200
+ type: object
201
+ additionalProperties: false
202
+ required: [id]
203
+ properties:
204
+ id: { type: string }
205
+ output: { type: object }
206
+ handler:
207
+ sql: |
208
+ UPDATE products SET active = false
209
+ WHERE id = input.id
210
+ AND NOT EXISTS (SELECT 1 FROM orders o WHERE o.productId = products.id AND o.orderStatus = 'pending_approval')
211
+ RETURNING id
212
+ ```
213
+
214
+ ## A row with its children in one read
215
+
216
+ An admin, or an AI client, reads each product with its orders in one call instead of one call per product. A
217
+ correlated subquery builds the array, so a product without orders has `[]`; `LEFT JOIN … GROUP BY` would give it one
218
+ empty object instead.
219
+
220
+ ```yaml
221
+ apiVersion: cms.mantle.aotter.net/v2
222
+ kind: View
223
+ metadata: { name: products-with-orders }
224
+ spec:
225
+ description: Each product with its orders as one JSON array.
226
+ surface: admin
227
+ requires: { auth: { all: [ctx.admin] } }
228
+ sql: |
229
+ SELECT p.id, p.name,
230
+ (SELECT json_group_array(json_set('{}', '$.id', o.id, '$.qty', o.qty)) FROM orders o WHERE o.productId = p.id) AS orders
231
+ FROM products p ORDER BY p.id
232
+ ```
233
+
234
+ SQLite and D1 build each object with `json_set('{}', '$.id', o.id, …)` (`json_object` is not on the allowlist).
235
+ PostgreSQL writes it as `COALESCE(jsonb_agg(jsonb_build_object('id', o.id, 'qty', o.qty)), CAST('[]' AS jsonb))`;
236
+ `jsonb_agg` of no rows is `NULL`, hence the `COALESCE`. PostgreSQL can also order inside the aggregate
237
+ (`jsonb_agg(… ORDER BY o.id)`); SQLite cannot, so sort the array.
238
+
239
+ Generation types the column as JSON because its expression can only produce a JSON document: a `json_group_array`,
240
+ `json_group_object`, `json_set`, `json_insert`, `json_remove`, `jsonb_agg`, `jsonb_object_agg`, `jsonb_build_object`,
241
+ `jsonb_build_array`, `jsonb_strip_nulls`, `to_jsonb` or `CAST(… AS jsonb)` call, a scalar subquery of one, or a
242
+ `COALESCE` of those with JSON literal fallbacks such as `'[]'`. REST, MCP (`structuredContent`) and Admin therefore
243
+ return an array on every target, and `WireRow` types the column `unknown`. The bare View and the Store keep the
244
+ driver's value: on SQLite and D1 that is JSON text, so code calling the bare View parses it. `json_extract`, `->>`
245
+ and `CASE` can return a scalar, so their output stays native.
246
+
247
+ ## The latest row of each group: row_number()
248
+
249
+ The most recent order of each product. `row_number() OVER (PARTITION BY … ORDER BY …)` numbers each product's orders
250
+ newest first, and the outer query keeps number 1. End the window's ORDER BY with `id` so two rows created in the same
251
+ instant still have one order. `rank()` instead gives tied rows the same number.
252
+
253
+ ```yaml
254
+ apiVersion: cms.mantle.aotter.net/v2
255
+ kind: View
256
+ metadata: { name: latest-order-per-product }
257
+ spec:
258
+ description: The most recent order of each product.
259
+ surface: admin
260
+ requires: { auth: { all: [ctx.admin] } }
261
+ sql: |
262
+ SELECT x.productId AS "productId", x.id, x.qty FROM (
263
+ SELECT o.productId, o.id, o.qty, row_number() OVER (PARTITION BY o.productId ORDER BY o.created_at DESC, o.id DESC) AS n
264
+ FROM orders o
265
+ ) x WHERE x.n = 1 ORDER BY x.productId
266
+ ```
@@ -1,26 +1,28 @@
1
1
  ---
2
- description: Generate and call typed Views, including internal-only queries, and choose indexed entry readers without confusing them with authorized public reads.
2
+ description: Choose Store for writes and simple lookups; declare internal Views for richer reads.
3
3
  ---
4
4
  # Query from TypeScript
5
5
 
6
- Use a **View** when a read needs declared params, projection, pagination or
7
- `requires` authorization. Use an **entry reader** for trusted host code that
8
- needs stored entries by a data field. Both are exposed by generated bindings;
9
- only the View executes the declared View authorization contract.
6
+ | Work | Data path |
7
+ | --- | --- |
8
+ | Writes | Store `insert` / `update` / `delete`, atomic `batch`, supported `transaction`, or declared Procedures |
9
+ | Id lookup or simple AND filter | Store `findById` / `find` / `first`, inside handlers or an existing application |
10
+ | Joins, aggregates, OR, reusable reads | Internal View: manifest SQL that is reviewable and typed |
10
11
 
11
- ## Declare an internal query
12
+ Never write raw SQL to Mantle collections: it bypasses scope, TTL, publishing and system-column rules. The Store applies row rules but does not check a View's or Procedure's `requires`; authorize your handler before using it.
12
13
 
13
- Save this complete source as `manifests/tickets.yaml`. The operational Schema
14
- uses a business status field distinct from Mantle's native `status`.
14
+ ## Declare an internal View
15
+
16
+ Save this as `manifests/tickets.yaml`:
15
17
 
16
18
  ```yaml
17
- apiVersion: cms.mantle.aotter.net/v1
19
+ apiVersion: cms.mantle.aotter.net/v2
18
20
  kind: Schema
19
- metadata:
20
- name: tickets
21
+ metadata: { name: tickets }
21
22
  spec:
22
23
  title: Tickets
23
24
  lifecycle: operational
25
+ indexes: [[ticketState]]
24
26
  schema:
25
27
  type: object
26
28
  additionalProperties: false
@@ -28,134 +30,236 @@ spec:
28
30
  properties:
29
31
  subject: { type: string }
30
32
  ticketState: { type: string, enum: [open, closed] }
31
- indexes: [[ticketState]]
32
33
  ---
33
- apiVersion: cms.mantle.aotter.net/v1
34
+ apiVersion: cms.mantle.aotter.net/v2
34
35
  kind: View
35
- metadata:
36
- name: tickets-by-state
36
+ metadata: { name: tickets-by-state }
37
37
  spec:
38
38
  surface: internal
39
- from: tickets
40
- fields: [id, subject, ticketState]
41
- filter:
42
- eq: { field: ticketState, value: { $param: ticketState } }
43
- params:
39
+ input:
44
40
  type: object
45
41
  additionalProperties: false
46
42
  required: [ticketState]
47
43
  properties:
48
44
  ticketState: { type: string, enum: [open, closed] }
49
- limit: 50
45
+ sql: |
46
+ SELECT id, subject, ticketState AS "ticketState" FROM tickets
47
+ WHERE ticketState = input.ticketState
48
+ ORDER BY id ASC
50
49
  ```
51
50
 
52
- ```sh
53
- pnpm exec mantle validate --no-source
54
- pnpm exec mantle generate
55
- pnpm exec mantle generate --check
51
+ Generate with `mantle generate cloudflare-d1 generated manifests/tickets.yaml`.
52
+ An internal View has no public REST route or MCP tool. Its generated function still applies declared authorization and compiled row policy.
53
+
54
+ ```ts
55
+ import { store } from './generated/generated.js';
56
+ import ticketsByState from './generated/views/tickets-by-state.js';
57
+ import type { Caller } from '@aotter/mantle/contracts';
58
+
59
+ export async function readTickets(db: Parameters<typeof store>[0], caller: Caller) {
60
+ const tickets = store(db, caller).collection('tickets');
61
+ const first = await tickets.first({ where: { ticketstate: 'open' } });
62
+ const rows = await ticketsByState(db, caller, { ticketState: 'open' });
63
+ return { first, rows };
64
+ }
56
65
  ```
57
66
 
58
- `internal` keeps this query out of REST routes, OpenAPI, MCP/WebMCP catalogs and
59
- Admin reports. It remains in the plan and generated binding. It is not a
60
- security bypass: adding `requires` evaluates the same authorization and guards
61
- against the host-supplied `ctx` on every call. No `uiSchema` or shared HTTP
62
- cache is allowed for an internal View.
67
+ The app supplies a verified Caller and its existing native handle. External input needs boundary validation. Store rows, filters and writes use storage names (`ticketstate`, `created_at`); View aliases use the SQL output names.
63
68
 
64
- ## Bind and call
69
+ ## Store reads and writes
65
70
 
66
- When the host already owns a prepared Runtime, bind it once where you need the
67
- typed API. This function can live in `src/queries.ts`:
71
+ `find` returns `{ rows, cursor }` (null cursor on the last page), with limits 1–500 and opaque keyset cursors. `where` combines fields with AND; `orderBy` selects a generated indexed column with `id` ties. Most order columns must be NOT NULL; publishing `published_at` supports nulls. For a proved pageable internal View, import `paged/views/<encoded-name>.ts` and call `(db, caller, input, { limit?, cursor? })`. Other internal Views retain their bare row-array function.
68
72
 
69
- ```ts
70
- import type { MantleRuntime } from "@aotter/mantle/runtime";
71
- import { bindMantle } from "../.mantle/generated/mantle.js";
72
-
73
- export async function openTickets(runtime: MantleRuntime) {
74
- const mantle = bindMantle(runtime);
75
- const result = await mantle.views.ticketsByState({
76
- params: { ticketState: "open" },
77
- page: 1,
78
- show: 20,
79
- });
80
- if (!result.ok) throw new Error(result.diagnostic.message);
81
- return result.result.rows;
82
- }
83
- ```
73
+ Store keeps `find({ search: 'text' })`, `first({ search: 'text' })` and `count(where?)`. Search matches exact id or declared searchable fields under the same row policy.
84
74
 
85
- The runtime response uses `result` on success (`result.result.rows` above);
86
- REST wraps those rows under `data` instead.
75
+ `count` scans every matching row. For large tables or frequently shown totals, use an internal View with indexed predicates or avoid exact totals; a View's aggregate still scans the matching rows.
87
76
 
88
- The wire name `tickets-by-state` becomes `ticketsByState`. Required params
89
- make the request and `params` mandatory; invalid enum values are TypeScript
90
- errors, and Runtime also validates actual inputs. `show` remains capped by
91
- `limit`. For an authorized View, pass the verified caller context as `ctx`;
92
- do not fabricate staff/user identities from request input.
77
+ Store search, Admin list search and Views using `mantle.search` share the same dialect search. SQLite/D1 use FTS5 trigram for queries of 3 or more characters, and LIKE for shorter queries. PostgreSQL uses ILIKE over the declared fields, which scans by default. A View does not make PostgreSQL search faster. For large PostgreSQL tables, the application's agent adds trigram indexes in a migration; ILIKE then uses them with no code change:
93
78
 
94
- A host without a Runtime can use generated `createMantle({ storage, handlers,
95
- ports })`, which delegates one eager boot attempt and returns the typed
96
- binding. Host code still owns connection lifetime and retries. See
97
- [Runtime and adapters](../concepts/runtime-and-adapters.md).
79
+ ```sql
80
+ CREATE EXTENSION IF NOT EXISTS pg_trgm;
81
+ CREATE INDEX articles_title_trgm ON articles USING gin (title gin_trgm_ops);
82
+ ```
98
83
 
99
- ## What is typed
84
+ Index each searched field as needed and inspect EXPLAIN for the workload. Case folding follows the engine.
100
85
 
101
- | Query form | Generated shape | Limit |
102
- |---|---|---|
103
- | Declarative View | `Mantle.ViewParams_<name>` and `Mantle.ViewRow_<name>` | Projection follows `fields`; native columns have their native types. Data properties remain optional in the row type. |
104
- | SQL View | Typed params; row type `unknown` | The generator does not infer SQL expressions or aliases. Narrow/validate rows in host code. |
105
- | Entry field reader | `MantleEntry<Mantle.Entry_<schema>>` | The field must be a declared data property and the value a compatible string, number or boolean. Types do not prove an index exists. |
106
- | Dynamic Runtime call | `runtime.executeView({ view, ctx, options })` | Useful without codegen; supplying a generic row type is the caller's assertion, not SQL validation. |
86
+ Writes are lazy and execute once when awaited. Insert/update return native rows; delete returns void. `update(id, set, { lock })` and `delete(id, { lock })` require the version the caller reviewed. A stale lock, excluded row or unique clash is `CONFLICT`. `batch` atomically executes unused Writes from one Store in input order; a step cannot use a prior result.
107
87
 
108
- Without `fields`, a declarative View includes native entry columns and Schema
109
- properties. Use explicit projections on exposed reads. Public declarative
110
- Views over publishing Schemas inject `status = published`; internal/staff
111
- Views and SQL statements do not. See [View reference](../reference/view.md).
88
+ Interactive `transaction(async tx => …)` exists on bun:sqlite and PostgreSQL, with collections, Views and Procedures bound to the transaction. D1 supports batches only. PostgreSQL defaults to serializable, with an explicit isolation override; SQLite uses BEGIN IMMEDIATE. Use `tx` inside the callback, not the outer Store or handle. Nested transactions and `tx.batch` are refused; there is no automatic retry.
112
89
 
113
- ## Indexed entry reads
90
+ Ref handlers receive `{ caller, db, store }` bound to the same Caller and transaction. Implement the generated `Handlers` interface and pass it to `store(db, caller, { handlers })`. Direct Procedure modules expose SQL Procedures; ref Procedures are reached through the bound Store or selected surfaces.
114
91
 
115
- For the Schema above:
92
+ See [native execution](native-execution.md) for values and write outcomes and [migrations](../concepts/runtime-and-adapters.md#schema-installation-and-migrations) before changing a Schema.
116
93
 
117
- ```ts
118
- const rows = await mantle.entries.tickets.findManyByDataField({
119
- field: "ticketState",
120
- value: "open",
121
- limit: 20,
122
- });
123
- // rows[n].data is the generated tickets data shape.
124
- ```
94
+ ## Prefer Store-backed ref Procedures for simple writes
125
95
 
126
- | Method | Returns | Options worth knowing |
127
- |---|---|---|
128
- | `readBySlug({ slug, locale?, status? })` | One entry or `null` | Use on a Schema with a slug field and an appropriate index. |
129
- | `readByDataField({ field, value, locale?, status? })` | One entry or `null` | Equality on one data property. |
130
- | `readByDataFieldIn({ field, values, latestPerValue?, locale?, status? })` | Entry array | Batch equality lookups; `latestPerValue` selects the newest match per value. |
131
- | `findManyByDataField({ field, value, limit })` | Entry array | Bounded equality lookup across statuses; no `ctx`, `status` or `locale` option. |
96
+ For a single-entity create or partial update, prefer a declared `ref` Procedure
97
+ whose handler awaits its caller-bound `ctx.store.collection(...).insert(...)`
98
+ or `.update(id, allowedPatch, { lock: expectedVersion })`. Keep the Procedure's
99
+ `requires` and generated validation at its invocation boundary: Store enforces
100
+ row policy, but does not substitute for operation authorization. Do not invoke
101
+ a handler directly from a route and accidentally bypass those checks.
132
102
 
133
- These readers do not evaluate View `requires`, inject public visibility or
134
- fire mutation hooks. In particular, `findManyByDataField` can return drafts.
135
- Use a public View for untrusted callers; do not expose a raw reader as a public
136
- route and assume the generated type authorizes it. Declare a measured index
137
- whose leftmost field matches the lookup; do not scan an entire collection in
138
- TypeScript to replace a field query.
103
+ Insert/update returns the actual written native row. Inside a transaction,
104
+ that result is provisional until the outer transaction commits; later failure
105
+ can still roll it back. Report committed success or emit external effects only
106
+ after that transaction succeeds. Return an explicit
107
+ allowlist of useful business fields, its backend `id` and returned `version`;
108
+ do not spread the entire row, echo the submitted version, calculate a new
109
+ version instead of reading the result, or add a generic receipt adapter. These
110
+ ref results are app-owned: declare their output schema and make any required
111
+ native-to-wire conversion explicit at the boundary.
139
112
 
140
- ## Generate from an existing plan
113
+ Patch only permitted fields. Omitted fields remain unchanged; define whether
114
+ explicit null clears a nullable field or is invalid. Do not expand a partial
115
+ request into a full replacement with defaults for unseen fields. A stale lock
116
+ is a conflict; preserve intent and reconcile/review rather than silently
117
+ refreshing the lock and retrying.
141
118
 
142
- A build tool that already compiled a sealed plan can use the pure emitter:
119
+ Use SQL Procedures when conditions or related effects must commit together:
120
+ for example, creating an order while decrementing stock under a CHECK, a state
121
+ transition guarded by an atomic WHERE, or an additive restock upsert. A
122
+ pre-read in TypeScript followed by an unguarded write is not equivalent.
123
+ Store `batch` is atomic for known writes; a D1 batch step cannot consume a prior
124
+ step's returned ID. D1 has no interactive transaction callback, so retain the
125
+ supported SQL program or another existing native atomic boundary for that case.
126
+ The [MCP design audit](agent-workflow-acceptance.md) applies these choices before
127
+ implementation and separates them from independent end-user acceptance.
143
128
 
144
- ```ts
145
- import { emitMantleModule } from "@aotter/mantle/codegen";
129
+ ## Several rows that commit together
130
+
131
+ An order with any number of lines, a cart checkout or a bulk import must commit
132
+ every row or none. Pass the rows as one array input and write them in one SQL
133
+ Procedure that reads the array as a row source: `json_each(input.items)` on
134
+ SQLite and D1, `jsonb_array_elements(input.items) AS j(value)` on PostgreSQL.
135
+ The Procedure's statements commit together on every target, so a Schema
136
+ `checks` failure in any statement refuses the whole call with `CONFLICT` and
137
+ leaves nothing behind. Bound the array with `maxItems`.
138
+
139
+ A statement cannot read an earlier statement's `RETURNING`, so rows that must
140
+ refer to each other share a key from the input: here the caller (or a `ref`
141
+ handler that then calls this Procedure) supplies `orderRef`. A later statement
142
+ does see the rows an earlier one wrote, so it can also store the parent's id
143
+ (`(SELECT o.id FROM orders o WHERE o.orderRef = input.orderRef)`) in a field with
144
+ `x-mantle-ref: orders`, which lets Admin fold the lines under their order.
146
145
 
147
- const emitted = emitMantleModule({ plan });
148
- if (!emitted.ok) throw new Error(emitted.diagnostics.map(d => d.message).join("\n"));
149
- // Write emitted.source to your generated module in the build step.
146
+ ```yaml
147
+ apiVersion: cms.mantle.aotter.net/v2
148
+ kind: Schema
149
+ metadata: { name: products }
150
+ spec:
151
+ title: Products
152
+ lifecycle: operational
153
+ checks: ["reserved <= stock"]
154
+ schema:
155
+ type: object
156
+ required: [name, stock, reserved]
157
+ properties:
158
+ name: { type: string }
159
+ stock: { type: integer, minimum: 0 }
160
+ reserved: { type: integer, minimum: 0 }
161
+ ---
162
+ apiVersion: cms.mantle.aotter.net/v2
163
+ kind: Schema
164
+ metadata: { name: orders }
165
+ spec:
166
+ title: Orders
167
+ lifecycle: operational
168
+ uniqueIndexes: [[orderRef]]
169
+ checks: ["matchedLines = lineCount"]
170
+ schema:
171
+ type: object
172
+ required: [orderRef, lineCount, matchedLines]
173
+ properties:
174
+ orderRef: { type: string }
175
+ lineCount: { type: integer, minimum: 1 }
176
+ matchedLines: { type: integer, minimum: 0 }
177
+ ---
178
+ apiVersion: cms.mantle.aotter.net/v2
179
+ kind: Schema
180
+ metadata: { name: order_lines }
181
+ spec:
182
+ title: Order lines
183
+ lifecycle: operational
184
+ schema:
185
+ type: object
186
+ required: [orderRef, productId, qty]
187
+ properties:
188
+ orderRef: { type: string }
189
+ productId: { type: string, x-mantle-ref: products }
190
+ qty: { type: integer, minimum: 1 }
191
+ ---
192
+ apiVersion: cms.mantle.aotter.net/v2
193
+ kind: Procedure
194
+ metadata: { name: place-order }
195
+ spec:
196
+ description: Place an order of one or more lines; an unknown product or a line that would oversell refuses the whole order.
197
+ requires: { auth: { all: [ctx.user] } }
198
+ input:
199
+ type: object
200
+ additionalProperties: false
201
+ required: [orderRef, items]
202
+ properties:
203
+ orderRef: { type: string }
204
+ items:
205
+ type: array
206
+ minItems: 1
207
+ maxItems: 50
208
+ items:
209
+ type: object
210
+ additionalProperties: false
211
+ required: [productId, qty]
212
+ properties:
213
+ productId: { type: string }
214
+ qty: { type: integer, minimum: 1 }
215
+ output: { type: object }
216
+ handler:
217
+ sql: |
218
+ INSERT INTO orders (orderRef, lineCount, matchedLines) VALUES (input.orderRef,
219
+ (SELECT COUNT(*) FROM json_each(input.items) j),
220
+ (SELECT COUNT(*) FROM json_each(input.items) j JOIN products p ON p.id = j.value ->> 'productId'));
221
+ UPDATE products SET reserved = reserved + (SELECT SUM(j.value ->> 'qty') FROM json_each(input.items) j WHERE j.value ->> 'productId' = products.id)
222
+ WHERE id IN (SELECT j.value ->> 'productId' FROM json_each(input.items) j);
223
+ INSERT INTO order_lines (orderRef, productId, qty)
224
+ SELECT input.orderRef, p.id, j.value ->> 'qty' FROM json_each(input.items) j JOIN products p ON p.id = j.value ->> 'productId'
225
+ RETURNING id, productId AS "productId";
150
226
  ```
151
227
 
152
- Pass either `{ plan }` or `{ linked }`, never both. The plan form avoids
153
- reparsing YAML and preserves the same generated types and entry/View/Procedure
154
- bindings. The emitter does no I/O, asset copying, storage preparation or caching.
228
+ Each refusal is a check or a unique index, so it rolls back all three statements:
229
+
230
+ - A line that would oversell fails `reserved <= stock`. Repeated lines for one
231
+ product add up in the `SUM`, so they are checked against the same stock.
232
+ - A line naming an unknown product does not match in the join, so
233
+ `matchedLines` falls short of `lineCount` and the `orders` check fails. Set
234
+ operations alone would skip that line and still succeed.
235
+ - A repeated `orderRef` clashes with the unique index, so replaying a request
236
+ cannot reserve twice.
155
237
 
156
- ## Source
238
+ On PostgreSQL `->>` returns text: write `CAST(j.value ->> 'qty' AS int8)`
239
+ there. SQLite and D1 refuse that integer cast (`SQL_TYPE`, because SQLite
240
+ truncates where PostgreSQL rounds), and their `->>` already returns the JSON
241
+ number.
157
242
 
158
- - [Binding generator](../../../packages/mantle/src/codegen/emitMantleModule.ts)
159
- - [Type generator](../../../packages/mantle-spec/src/usecase/EmitTypesUseCase.ts)
160
- - [Entry reader contract](../../../packages/mantle-runtime/src/domain/port/EntryReader.ts)
161
- - [View execution](../../../packages/mantle-runtime/src/usecase/view/ExecuteViewUseCase.ts)
243
+ [SQL patterns](sql-patterns.md) covers approving before reserving, choosing a status with `CASE`, partial updates,
244
+ upserts, `EXISTS` guards and nested reads on the same `products`.
245
+
246
+ Do not split this into a Store write, then a SQL Procedure, then a compensating
247
+ delete: the rows are visible between the steps and a failure leaves cleanup to
248
+ the handler. A `ref` handler that needs the rows written together calls this
249
+ Procedure through `ctx.store.procedures`, passing the array itself: the
250
+ generated input type is the authored one, and the generated query serializes
251
+ the array for the driver (a string is bound as it is). Like a direct call, the
252
+ Store does not validate input against the Procedure's schema (REST and MCP do),
253
+ so `maxItems` and the line shape hold only for rows the handler has checked or
254
+ built.
255
+
256
+ ```ts
257
+ import { store } from './generated/generated.js';
258
+ import type { Caller } from '@aotter/mantle/contracts';
259
+
260
+ // `ctx.store` in a `ref` handler is this Store: `ctx.store.procedures['place-order']({ orderRef, items })`.
261
+ export async function placeOrder(db: Parameters<typeof store>[0], caller: Caller, orderRef: string) {
262
+ const items = [{ productId: 'pen', qty: 2 }, { productId: 'stapler', qty: 1 }];
263
+ return store(db, caller).procedures['place-order']({ orderRef, items });
264
+ }
265
+ ```