@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,63 @@
1
+ /** Opt-in lifecycle: one atomic, version-guarded statement per transition, with Core's row rules. */
2
+ import { rowPolicy } from "../build/compiler.js";
3
+ import { dialect as sqlite } from "../kernel/dialect/sqlite.js";
4
+ import { dialect as postgres } from "../kernel/dialect/postgres.js";
5
+ import { renderTerm } from "../kernel/store.js";
6
+ import { publishedParent } from "./translations.js";
7
+ import { entry, exportName, q } from "../domain/entry.js";
8
+ import { publishingAnnotations, publishingInput, publishingTransitions } from "../domain/generated-tools.js";
9
+ export { publishingAnnotations, publishingInput, publishingTransitions };
10
+ export function emitPublishing(plan, isSqlite, declarations, procedures, entries, values, refuse) {
11
+ const d = isSqlite ? sqlite : postgres;
12
+ const translations = Object.values(plan.schemas).some(s => s.translates);
13
+ if (translations)
14
+ declarations.push('import {translationWrite} from "@aotter/mantle/driver/transaction-context";');
15
+ if (Object.values(plan.schemas).some(s => s.publishing))
16
+ declarations.push('import { transition } from "@aotter/mantle/publishing";');
17
+ for (const s of Object.values(plan.schemas).filter(s => s.publishing).sort((a, b) => a.name.localeCompare(b.name))) {
18
+ if (Object.hasOwn(s.fields, "published_at")) {
19
+ refuse(`schemas/${s.name}`, "published_at is a publishing system column; remove the declared field.");
20
+ continue;
21
+ }
22
+ if (s.ttl)
23
+ values.add("cutoff");
24
+ for (const [verb, from, to] of publishingTransitions) {
25
+ const name = `${s.name}.${verb}`, fn = exportName("procedure", name), path = `procedures/${name}`;
26
+ const failure = `${verb}: row is missing or excluded, expectedVersion differs, status is not ${from.join(" or ")}${verb === "publish" ? `, or required data is missing${s.translates ? ", or no visible published parent shares the join key" : ""}` : ""}.`;
27
+ if (plan.procedures[name]) {
28
+ refuse(path, "This name is reserved for the generated publishing transition.");
29
+ continue;
30
+ }
31
+ const statement = (trusted) => {
32
+ const schema = plan.schemas[s.name];
33
+ const recipes = [{ k: "now" }, { k: "input", name: "id" }, { k: "input", name: "expectedVersion" }];
34
+ const conditions = [`id = ${d.param(2)}`, `version = ${d.param(3)}`, `status IN (${from.map(v => `'${v}'`).join(", ")})`];
35
+ // Same visibility as Store and Admin writes, translation parent included (a child under an out-of-set parent is missing);
36
+ // the status term is replaced by the transition's own `status IN (...)`.
37
+ for (const term of rowPolicy(schema, trusted ? "trusted" : "caller", plan.schemas))
38
+ if (term[1] !== "admin")
39
+ conditions.push(renderTerm(d, term, term[1] === "parent" ? d.ident(s.name) + "." : "", recipe => (recipes.push(recipe), d.param(recipes.length))));
40
+ if (verb === "publish")
41
+ for (const field of s.schema.required ?? []) {
42
+ const column = Object.keys(s.fields).find(c => (s.names?.[c] ?? c) === field);
43
+ if (column)
44
+ conditions.push(`${d.ident(column)} IS NOT NULL`);
45
+ }
46
+ if (verb === "publish" && s.translates)
47
+ conditions.push(publishedParent(plan, schema, d, recipes, trusted));
48
+ const clock = d.param(1);
49
+ return { sql: `UPDATE ${d.ident(s.name)} SET status = '${to}', published_at = ${verb === "publish" ? clock : "NULL"}, version = version + 1, updated_at = ${clock} WHERE ${conditions.join(" AND ")} RETURNING id, version, status, ${d.wireTimestamp(d.ident("published_at")).join("")} AS published_at`, binds: recipes, row: true };
50
+ };
51
+ values.add("now");
52
+ const caller = q([statement(false)]), trusted = q([statement(true)]);
53
+ declarations.push(`export type ${fn}Input = { readonly id: string; readonly expectedVersion: number };`, `const ${fn}Caller = ${caller} as const;`, ...(trusted === caller ? [] : [`const ${fn}Trusted = ${trusted} as const;`]), `async function ${fn}(db: Database, caller: Caller, raw: ${fn}Input): Promise<Output> { ${plan.access ? 'USER.modeOf(caller); ' : ''}return transition(db, caller, raw, ${q(path)}, ${plan.access ? "USER.scopedAll(" : ""}${trusted === caller ? "" : `${plan.access ? 'USER.modeOf(caller) === "trusted"' : 'caller.kind === "system"'} ? ${fn}Trusted : `}${fn}Caller${plan.access ? ", caller)" : ""}, ${q(failure)}, D, DRIVER.procedure${translations ? `, () => translationWrite(db, ${q(s.name)}, ${verb === "publish" && s.translates ? q(s.translates.parent) : "undefined"})` : ""}); }`, `export { ${fn} };`);
54
+ procedures.push(`[${q(name)}]: ${fn}`);
55
+ entries[`procedures/${encodeURIComponent(name)}.ts`] = entry("procedure", name, `${fn}Input as Input`, 'export type { Output } from "@aotter/mantle/contracts";\n');
56
+ }
57
+ }
58
+ }
59
+ /** Publishing lists get one index, prefixed by scope when present. */
60
+ export function publishingSql(schemas, isSqlite) {
61
+ const d = isSqlite ? sqlite : postgres;
62
+ return Object.values(schemas).filter(s => s.publishing).map(s => `CREATE INDEX ${d.ident(`_mantle_ix_${s.name}_published`)} ON ${d.ident(s.name)} (${[...(s.scope ? [s.scope] : []), ...(s.userScopes ?? []).map(u => u.column), "published_at", "id"].map(d.ident).join(", ")})`);
63
+ }
@@ -0,0 +1,27 @@
1
+ import { viewOutputs } from "../build/compiler.js";
2
+ export function refuseDeclarations({ plan }, diagnostics) {
3
+ const refuse = (path, message) => diagnostics.push({ code: "GENERATE_UNSUPPORTED", path, message });
4
+ for (const [name, trigger] of Object.entries(plan.triggers)) {
5
+ const kind = trigger.source.kind;
6
+ if (kind === "http" || kind === "mcp")
7
+ continue;
8
+ refuse(`triggers/${name}`, kind === "schedule" ? "Schedule Triggers are unsupported. Wire scheduled() yourself." : "Before/after lifecycle hooks are unsupported. Write a custom handler.");
9
+ }
10
+ for (const [name, schema] of Object.entries(plan.schemas)) {
11
+ const parent = schema.translates && plan.schemas[schema.translates.parent.toLowerCase()];
12
+ if (parent?.scope && schema.scope !== parent.scope)
13
+ refuse(`schemas/${name}/translates`, `Scoped translation parent '${schema.translates.parent}' requires the same child scope column '${parent.scope}'. Different or missing child scope columns cannot be correlated safely.`);
14
+ }
15
+ for (const [kind, declarations] of [["views", plan.views], ["procedures", plan.procedures]]) {
16
+ for (const [name, declaration] of Object.entries(declarations)) {
17
+ if (declaration.requires?.guard)
18
+ refuse(`${kind}/${name}/requires/guard`, "Dynamic authorization guards are unsupported. Write a custom handler that checks authorization before executing queries.");
19
+ }
20
+ }
21
+ for (const [name, view] of Object.entries(plan.views)) {
22
+ for (const [key, column] of Object.entries(viewOutputs(view, plan.schemas, new Map(), true).columns))
23
+ if (plan.schemas[column.schema]?.fields[column.field] === "geo") {
24
+ refuse(`views/${name}/columns/${key}`, "Native geo projection is unsupported. Select the latitude and longitude columns explicitly.");
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,54 @@
1
+ import { databaseImport, q, viewImports } from "../domain/entry.js";
2
+ import { authLines, conversions } from "../domain/input.js";
3
+ import { boundaryPage, outputConversions } from "../domain/columns.js";
4
+ export function emitRest(plan, lowered, target, description) {
5
+ const guard = (requires, path, binds = [], trustedOnly = false) => {
6
+ const lines = authLines(requires, path, binds, !target.endsWith("postgres"), trustedOnly);
7
+ return lines.length ? `, check: (caller) => {${lines.join("")}}` : "";
8
+ };
9
+ const userImport = plan.access ? ['import { userBinds } from "@aotter/mantle/user-fields";'] : [];
10
+ const refs = Object.values(plan.procedures).some(p => "ref" in p.handler);
11
+ const imports = [...userImport, 'import { rest as serve, type Options } from "@aotter/mantle/rest";', databaseImport(target), ...(refs ? ['import { store, type Handlers } from "./generated.js";'] : [])];
12
+ if (Object.values(plan.procedures).some(p => p.trustedOnly) || [...Object.values(plan.views), ...Object.values(plan.procedures)].some(p => p.requires?.auth?.all.length))
13
+ imports.push('import { deny, isAdmin, isUser } from "@aotter/mantle/contracts";');
14
+ const views = [], procedures = [];
15
+ let hasWire = false;
16
+ const convert = (code) => code ? `, convert: ${code}` : "";
17
+ // Each segment matches in its encoded form; `{name}` segments capture.
18
+ const route = (method, path) => {
19
+ const keys = [];
20
+ const source = path.split("/").map(segment => segment.startsWith("{") && segment.endsWith("}") ? (keys.push(segment.slice(1, -1)), "([^/]+)") : encodeURIComponent(segment).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("/");
21
+ return `method: ${q(method)}, pattern: new RegExp(${q("^" + source + "/?$")})${keys.length ? `, keys: ${q(keys)}` : ""}`;
22
+ };
23
+ for (const [name, view] of Object.entries(plan.views)) {
24
+ if (view.surface !== "public")
25
+ continue;
26
+ const id = views.length, encoded = encodeURIComponent(name);
27
+ const wire = outputConversions(plan, name, target);
28
+ imports.push(...viewImports(name, id).map(line => wire.some(([, type]) => type === "int") ? line.replaceAll("./paged/views/", "./paged/wire/views/") : line));
29
+ hasWire ||= wire.length > 0;
30
+ views.push(`{ ${route("GET", `/api/views/${name}`)}, target: ${q({ kind: "view", name })}${guard(view.requires, `views/${name}`, (lowered.views[name]?.public ?? lowered.views[name]?.caller)?.binds)}, query: query${id}, page: ${boundaryPage(`page${id}`, wire)}${convert(conversions(view.input?.properties, (lowered.views[name]?.public ?? lowered.views[name]?.caller)?.binds ?? [], target))} }`);
31
+ }
32
+ const triggers = Object.values(plan.triggers).filter(t => t.source.kind === "http").sort((a, b) => a.source.kind === "http" && b.source.kind === "http" ? (a.source.path.match(/\{/g)?.length ?? 0) - (b.source.path.match(/\{/g)?.length ?? 0) : 0);
33
+ for (const trigger of triggers) {
34
+ if (trigger.source.kind !== "http")
35
+ continue;
36
+ const { method, path } = trigger.source, name = trigger.procedure, procedure = plan.procedures[name], id = procedures.length, ref = "ref" in procedure.handler;
37
+ if (!ref)
38
+ imports.push(`import procedure${id} from "./procedures/${encodeURIComponent(name)}.js";`);
39
+ imports.push(`import { validate as validate${id} } from "./validation/procedures/${encodeURIComponent(name)}.js";`);
40
+ const params = Object.fromEntries([...path.matchAll(/\{([^}]+)\}/g)].flatMap(([, key]) => {
41
+ const type = [procedure.input?.properties?.[key]?.type].flat().find(t => t && t !== "null");
42
+ return type === "integer" || type === "number" ? [[key, "number"]] : type === "boolean" ? [[key, "boolean"]] : [];
43
+ }));
44
+ procedures.push(`{ ${route(method, path)}, id: ${q(`rest:${method} ${path.replace(/\{([^}]+)\}/g, ":$1")}`)}, target: ${q({ kind: "procedure", name })}${description.targets.find(t => t._meta.id === `procedure:${name}`)?._meta.readOnly ? ", readOnly: true" : ""}${guard(procedure.requires, `procedures/${name}`, lowered.procedures[name]?.caller?.flatMap(s => s.binds), procedure.trustedOnly)}${Object.keys(params).length ? `, params: ${q(params)}` : ""}, validate: validate${id}${convert(conversions(procedure.input?.properties, lowered.procedures[name]?.caller?.flatMap(s => s.binds) ?? [], target))}, run: ${ref ? `(db, caller, input, handlers) => store(db, caller, { handlers }).procedures[${q(name)}](input)` : `procedure${id}`} }`);
45
+ }
46
+ if (hasWire)
47
+ imports.push('import { wireRows } from "@aotter/mantle/contracts";');
48
+ const list = (items) => items.length ? `[\n${items.map(item => " " + item + ",\n").join("")} ]` : "[]";
49
+ return { "rest.ts": `${imports.join("\n")}
50
+ export function rest<Env>(options: Options<Env, Database>${refs ? " & { handlers: Handlers }" : ""}): (request: Request, env: Env, ctx?: import("@aotter/mantle/contracts").RequestContext) => Promise<Response | undefined> {
51
+ return serve<Env, Database${refs ? ", Handlers" : ""}>(${list([...views, ...procedures])}, ${plan.access ? `{...options, fields: ${q(plan.access.userFields)}, accessTargets: () => import("./access-targets.js").then(m => m.targets)}` : '{...options, accessTargets: () => import("./access-targets.js").then(m => m.targets)}'});
52
+ }
53
+ ` };
54
+ }
@@ -0,0 +1,7 @@
1
+ export function searchDiagnostics(warnings) {
2
+ return [...new Set(warnings.filter(message => /mantle\.search|search fields/.test(message)).map(message => message.replace(/^(plan#\/[^ ]+) \([^)]*\): /, "$1: ")))].map(message => ({
3
+ code: "GENERATE_UNSUPPORTED",
4
+ path: /^plan#\/([^: ]+)/.exec(message)?.[1] ?? "views",
5
+ message: `${message}. Declare searchableFields on the source Schema; search_rank needs mantle.search on the same alias in the same query.`,
6
+ }));
7
+ }
@@ -0,0 +1,39 @@
1
+ /** Generate the optional site/media module and deployment DDL, never runtime preparation. */
2
+ import { assertSiteDefaultsCanonical, canonicalizeLocaleList, expandPolicyRequired } from "../build/compiler.js";
3
+ import { driverFor, isSqlite, q } from "../domain/entry.js";
4
+ export function emitSite(target, defaults, refuse, schemaNames) {
5
+ if (!defaults)
6
+ return {};
7
+ try {
8
+ assertSiteDefaultsCanonical(defaults);
9
+ }
10
+ catch (error) {
11
+ refuse("site", String(error.message));
12
+ return {};
13
+ }
14
+ for (const name of schemaNames)
15
+ if (["site_config", "media_assets", "pending_media_uploads"].includes(name.toLowerCase()))
16
+ refuse(`schemas/${name}`, "This table name is reserved by the selected site/media module.");
17
+ if (new Set(defaults.media?.purposes?.map(p => p.name)).size !== (defaults.media?.purposes?.length ?? 0))
18
+ refuse("site/media/purposes", "Media purpose names must be unique.");
19
+ defaults = { ...defaults, ...(defaults.locales ? { locales: canonicalizeLocaleList(defaults.locales).valid } : {}) };
20
+ const pg = !isSqlite(target), driver = driverFor(target);
21
+ const policies = defaults.media?.purposes?.map(p => ({ ...p, required: expandPolicyRequired(p.required).map(slot => slot.join(",")) })) ?? [];
22
+ const seed = ["brand", "title", "description"].filter(key => defaults[key]).map(key => `INSERT INTO site_config (key,value) VALUES ('${key}','${defaults[key].replaceAll("'", "''")}') ON CONFLICT(key) DO NOTHING;`).join("\n");
23
+ const root = "@aotter/mantle";
24
+ return { "site.ts": `import { createSite } from "${root}/site";
25
+ import type { Caller } from "${root}/contracts";
26
+ import type { ${pg ? "Postgres" : "Handle"} as Database } from "${root}/${pg ? "contracts" : `driver/${driver}`}";
27
+ import * as DRIVER from "${root}/driver/${driver}";
28
+ import { param, escapeLike, likeEscape, like } from "${root}/dialect/${pg ? "postgres" : "sqlite"}";
29
+ export const siteDefaults = ${q(defaults)} as const;
30
+ const policies = ${q(policies)} as const;
31
+ export function site(db: Database, caller: Caller) { return createSite({read:(sql,values)=>DRIVER.read(db,sql,values),write:steps=>DRIVER.procedure(db,steps,"site"),definitive:DRIVER.definitive},{param,escapeLike,likeEscape,like},caller,siteDefaults,policies); }
32
+ `, "site-seed.sql": seed + "\n", "site-schema.sql": `CREATE TABLE site_config (key TEXT PRIMARY KEY, value TEXT NOT NULL);
33
+ CREATE TABLE media_assets (id TEXT PRIMARY KEY, created_at ${pg ? "BIGINT" : "INTEGER"} NOT NULL, owner_id TEXT, alt TEXT, caption TEXT, variants TEXT NOT NULL, metadata TEXT);
34
+ CREATE INDEX media_assets_by_created_id ON media_assets (created_at DESC, id DESC);
35
+ CREATE TABLE pending_media_uploads (id TEXT PRIMARY KEY, record TEXT NOT NULL, expires_at ${pg ? "BIGINT" : "INTEGER"} NOT NULL);
36
+ CREATE INDEX pending_media_uploads_expires_at ON pending_media_uploads (expires_at);
37
+ ${seed}
38
+ ` };
39
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * One typed collection per Schema, over the generic `createStore`. Core's row rules (`rowPolicy`, `writePolicy`) are evaluated
3
+ * here, at build time, ONCE for a placeholder scope and TTL into the shared `Rules`; a Schema adds only a `Fact` (scope, TTL,
4
+ * publishing, its date-time and date columns). Generation is refused if a Schema's expanded rules differ from Core's own for it
5
+ * (caller and trusted rows, insert fills, update sets and lock, delete lock).
6
+ * Column names, value types, writable columns and `orderBy` are TypeScript types only: nothing at runtime knows them.
7
+ */
8
+ import { NATIVE_NOT_NULL, notNullColumns, rowPolicy, writePolicy } from "../build/compiler.js";
9
+ import { declaredType, inputType, nativeType } from "../domain/columns.js";
10
+ import { q } from "../domain/entry.js";
11
+ import { expand, SCOPE, TTL } from "../kernel/store.js";
12
+ import { refChecks, userScopeTerm } from '../build/compiler.js';
13
+ // Types the typed Store compares and writes as plain bind values. Fixed numerics are scaled integers on SQLite; geo is two columns.
14
+ const SCALAR = new Set(["text", "real", "float8", "integer", "int4", "int8", "bool", "timestamptz", "date"]);
15
+ /** Core's own rules for a Schema, in the shape of `Rules` without the id expression (a delete's lock is compared separately). */
16
+ const core = (s, schemas) => {
17
+ const update = writePolicy(s, "update", { lock: true });
18
+ return { caller: rowPolicy(s, "caller", schemas), trusted: rowPolicy(s, "trusted", schemas), fills: writePolicy(s, "insert").fills, sets: update.sets, lock: update.lock };
19
+ };
20
+ const probe = { scope: SCOPE, ttl: TTL, ttlSeconds: 0, publishing: true };
21
+ /** @internal */
22
+ export function emitStores(schemas, isSqlite, newId, refuse, required = false) {
23
+ const all = Object.values(schemas).sort((a, b) => a.name < b.name ? -1 : 1);
24
+ if (!all.length && !required)
25
+ return undefined;
26
+ const rules = { ...core(probe), newId };
27
+ const tables = [], facts = [];
28
+ for (const s of all) {
29
+ const type = (column) => declaredType(s.fields, column) ?? (column === "version" ? "integer" : column === "published_at" ? "timestamptz" : "text");
30
+ const declared = Object.keys(s.fields).filter(f => f !== s.scope && s.fields[f] !== "geo");
31
+ const geo = Object.keys(s.fields).filter(f => s.fields[f] === "geo").flatMap(f => [`${f}_lat`, `${f}_lng`]); // `*` returns a geo field's two columns
32
+ const names = [...["id", "version", "created_at", "updated_at", "author_id", ...(s.publishing ? ["status", "published_at"] : [])], ...declared];
33
+ const required = new Set(s.schema.required ?? []), notNull = notNullColumns(s);
34
+ const nonNull = (column) => NATIVE_NOT_NULL.has(column) || notNull.has(column);
35
+ const ts = [...(s.publishing ? ["published_at"] : []), ...declared.filter(c => s.fields[c] === "timestamptz")], dates = declared.filter(c => s.fields[c] === "date");
36
+ const parent = s.translates && schemas[s.translates.parent.toLowerCase()];
37
+ const inherited = parent ? Object.keys(parent.fields).filter(c => c !== parent.scope && !Object.hasOwn(s.fields, c)).flatMap(c => parent.fields[c] === "geo" ? [`${c}_lat`, `${c}_lng`] : [c]) : [];
38
+ const inheritedType = (c) => parent?.fields[c] ?? "real";
39
+ const ref = refChecks(s, schemas);
40
+ const fact = {
41
+ ...(s.userScopes?.length ? { u: s.userScopes.map(u => userScopeTerm(u, true)) } : {}),
42
+ ...(s.scope ? { s: s.scope } : {}), ...(s.ttl ? { ttl: [s.ttl, s.ttlSeconds ?? 0] } : {}), ...(s.publishing ? { pub: 1 } : {}),
43
+ ...(ts.length ? { ts } : {}), ...(dates.length ? { d: dates } : {}),
44
+ ...(s.search?.length ? { search: s.search } : {}),
45
+ ...(ref.length ? { ref } : {}),
46
+ ...(parent ? { parent: { on: s.translates.on.toLowerCase(), ...(parent.scope ? { scope: [parent.scope, s.scope] } : {}), table: s.translates.parent.toLowerCase(), visible: rowPolicy(s, "caller", schemas).find(t => t[1] === "parent"), caller: rowPolicy(parent, "caller", schemas), trusted: rowPolicy(parent, "trusted", schemas), fields: inherited, ts: inherited.filter(c => parent.fields[c] === "timestamptz"), d: inherited.filter(c => parent.fields[c] === "date") } } : {}),
47
+ };
48
+ // the shared rules, with this Schema's scope and TTL put in, are Core's rules for it: or the runtime would enforce something else
49
+ const expected = q([core(s, schemas), writePolicy(s, "delete", { lock: true }).lock]), got = q([expand(rules, fact), rules.lock]);
50
+ if (expected !== got)
51
+ refuse(`schemas/${s.name}`, `Core's row rules for this Schema differ from the shared rules (${expected} vs ${got}); the generic Store cannot serve it.`);
52
+ // ORDER BY is allowed where the caller's scoped read can walk an index (its first column, or the one after the scope column) and the column is NOT NULL.
53
+ const indexed = new Set([...(s.publishing ? [[...(s.scope ? [s.scope] : []), "published_at", "id"]] : []), ...(s.indexes ?? []), ...(s.unique ?? [])].map(i => i[0] === s.scope ? i[1] : i[0]).filter((c) => !!c));
54
+ const key = (c) => /^[A-Za-z_]\w*$/.test(c) ? c : q(c);
55
+ const of = (columns, value) => `{ ${columns.map(c => `${key(c)}: ${value(c)};`).join(" ")} }`;
56
+ const union = (columns) => columns.map(c => q(c)).join(" | ") || "never";
57
+ const scalar = (c) => SCALAR.has(type(c));
58
+ const writable = declared.filter(c => !(s.userScopes ?? []).some(u => u.column === c) && (scalar(c) || type(c) === "json"));
59
+ const input = (c) => `${inputType(type(c))}${nonNull(c) ? "" : " | null"}`;
60
+ tables.push(`${key(s.name)}: { row: ${of([...names, ...geo], c => c === "id" ? "string" : nativeType(geo.includes(c) ? "real" : type(c), isSqlite, nonNull(c)))}${inherited.length ? ` & { ${inherited.map(c => `${key(c)}?: ${nativeType(inheritedType(c), isSqlite)};`).join(" ")} }` : ""}; fields: ${of(names.filter(scalar), c => inputType(type(c)))}; set: ${of(writable, input)}${s.userScopes?.length ? `; insert: ${of([...writable, ...s.userScopes.map(u => u.column)], input)}` : ""}; required: ${union(s.publishing ? [] : writable.filter(c => required.has(s.names?.[c] ?? c)))}; order: ${union(["id", ...names.filter(c => c !== "id" && scalar(c) && indexed.has(c) && (nonNull(c) || s.publishing && c === "published_at"))])} }`);
61
+ facts.push(`${q(s.name)}: ${q(fact)}`);
62
+ }
63
+ return { tables: `{ ${tables.join("; ")} }`, rules: q(rules), facts: `{ ${facts.join(", ")} }` };
64
+ }
@@ -0,0 +1,9 @@
1
+ /** Generate-time tool versions: a stale host copy shows the model an older version than results carry. */
2
+ import { canonical, fnv } from "../build/compiler.js";
3
+ /** A hash of what a catalogue lists, so an unchanged definition keeps its version across deploys. */
4
+ export const versionOf = (value) => fnv(canonical(value));
5
+ /** A listed tool: its version, and its description (each locale's) ending with that version. */
6
+ export function versioned(tool) {
7
+ const v = versionOf(tool), mark = (text) => `${text} (tool version ${v})`;
8
+ return { ...tool, description: typeof tool.description === "string" ? mark(tool.description) : Object.fromEntries(Object.entries(tool.description).map(([locale, text]) => [locale, mark(text)])), version: v };
9
+ }
@@ -0,0 +1,78 @@
1
+ import { userReferences } from "../domain/userReferences.js";
2
+ /** Translation helpers use generated SQL policy, never a request-time manifest. */
3
+ import { rowPolicy } from "../build/compiler.js";
4
+ import { renderTerm } from "../kernel/store.js";
5
+ import { dialect as SQLITE } from "../kernel/dialect/sqlite.js";
6
+ import { dialect as POSTGRES } from "../kernel/dialect/postgres.js";
7
+ import { declaredType } from "../domain/columns.js";
8
+ import { driverFor, isSqlite, q } from "../domain/entry.js";
9
+ /** The parent is checked by the same atomic UPDATE that publishes the child. */
10
+ export function publishedParent(plan, child, d, recipes, trusted) {
11
+ const tr = child.translates, parent = plan.schemas[tr.parent.toLowerCase()], on = tr.on.toLowerCase();
12
+ const conditions = [`p.${d.ident(on)} = ${d.ident(child.name)}.${d.ident(on)}`];
13
+ if (parent.scope)
14
+ conditions.push(`p.${d.ident(parent.scope)} = ${d.ident(child.name)}.${d.ident(child.scope ?? parent.scope)}`);
15
+ if (parent.publishing)
16
+ conditions.push("p.status = 'published'");
17
+ for (const term of rowPolicy(parent, trusted ? "trusted" : "caller", plan.schemas))
18
+ conditions.push(renderTerm(d, term, "p.", recipe => (recipes.push(recipe), d.param(recipes.length))));
19
+ return `EXISTS (SELECT 1 FROM ${d.ident(parent.name)} p WHERE ${conditions.join(" AND ")})`;
20
+ }
21
+ export function emitTranslations(plan, target) {
22
+ const d = isSqlite(target) ? SQLITE : POSTGRES;
23
+ const user = userReferences(plan, isSqlite(target));
24
+ const children = Object.values(plan.schemas).filter(s => s.translates);
25
+ if (!Object.values(plan.schemas).some(s => s.localized))
26
+ return {};
27
+ if (!children.length)
28
+ return { "translations.ts": 'export {toCanonicalLocale} from "@aotter/mantle/translations";\n' };
29
+ const parents = [...new Set(children.map(s => s.translates.parent.toLowerCase()))], declarations = [], descriptors = [];
30
+ const driver = driverFor(target);
31
+ for (const parent of parents) {
32
+ const schema = plan.schemas[parent], types = children.filter(s => s.translates.parent.toLowerCase() === parent);
33
+ const entries = types.map(child => {
34
+ const on = child.translates.on.toLowerCase(), type = declaredType(child.fields, on);
35
+ const key = "t." + d.ident(on), projection = type === "timestamptz" ? d.wireTimestamp(key).join("") : type === "date" ? d.wireDate(key).join("") : key;
36
+ const lower = (mode) => {
37
+ const terms = rowPolicy(plan.schemas[child.name], mode, plan.schemas), recipes = [];
38
+ const where = terms.map(term => renderTerm(d, term, "t.", recipe => (recipes.push(recipe), d.param(recipes.length)))).join(" AND ");
39
+ return `{where:${q(where)},binds:${q(recipes)}}`;
40
+ };
41
+ return `{on:${q(on)},type:${q(type ?? "text")},sql:${q(`SELECT t.id AS _id, ${projection} AS _key, t.locale FROM ${d.ident(child.name)} t`)},caller:${lower('caller')},trusted:${lower('trusted')}}`;
42
+ });
43
+ const columns = [...new Set(types.map(s => s.translates.on.toLowerCase()))];
44
+ declarations.push(`${q(schema.name)}: Pick<Tables[${q(schema.name)}]["row"],${["id", ...columns.filter(c => c !== schema.scope)].map(value => q(value)).join("|")}>${schema.scope && columns.includes(schema.scope) ? ` & {readonly ${q(schema.scope)}?:string|null}` : ""};`);
45
+ descriptors.push(`${q(schema.name)}:[${entries.join(",")}]`);
46
+ }
47
+ return { "translations.ts": `${user.code ? user.code + "\n" : ""}import {ContractError,type Caller${driver === "postgres" ? ",type Unsafe as Database" : ""}} from "@aotter/mantle/contracts";
48
+ ${driver !== "postgres" ? `import type {Handle as Database} from "@aotter/mantle/driver/${driver}";` : ""}
49
+ import {read} from "@aotter/mantle/driver/${driver}";
50
+ import {binds,encode} from "@aotter/mantle/binds";
51
+ import {param,ident${driver === "postgres" ? ",cast" : ""},timestamp,date} from "@aotter/mantle/dialect/${driver === "postgres" ? "postgres" : "sqlite"}";
52
+ import type {Tables} from "./generated.js";
53
+ export {toCanonicalLocale} from "@aotter/mantle/translations";
54
+ export interface TranslationParents {${declarations.join("\n")}}
55
+ const descriptors={${descriptors.join(",")}} as const;
56
+ /** Only pass the current parent page (up to 500); reads are bounded to its keys. */
57
+ export async function translationLocales<P extends keyof TranslationParents>(db:Database,caller:Caller,parent:P,parents:readonly TranslationParents[P][]):Promise<Record<string,string[]>> {
58
+ ${user.used ? ' USER.modeOf(caller);\n' : ''} if(parents.length>500)throw new ContractError("INPUT_VALIDATION_FAILED","Pass one parent page, at most 500 rows.","translations");
59
+ const out:Record<string,string[]>=Object.fromEntries(parents.map(row=>[row.id,[]]));
60
+ for(const child of descriptors[parent]) {
61
+ const rows=parents as readonly Record<string,unknown>[],keys=[...new Set(rows.map(row=>row[child.on]).filter(value=>value!==null&&value!==undefined&&value!==""))];
62
+ const byKey=new Map<unknown,Set<string>>(),mode=${user.used ? 'USER.modeOf(caller)==="trusted"' : 'caller.kind==="system"'}?child.trusted:child.caller;
63
+ for(let i=0;i<keys.length;i+=50) {
64
+ const base=binds(${user.used ? "USER_VALUES" : "{}"},mode.binds,caller),batch=keys.slice(i,i+50);let after:string|undefined;
65
+ do {
66
+ const values=[...base,...batch.map(value=>encode({timestamp,date},child.type,value))],where=[...(mode.where?[${user.used ? "USER.scoped(mode.where,caller)" : "mode.where"}]:[]),"t."+ident(child.on)+" IN ("+batch.map((_,index)=>${driver === "postgres" ? "cast(param(base.length+index+1),child.type)" : "param(base.length+index+1)"}).join(",")+")"];
67
+ if(after!==undefined){values.push(after);where.push("t.id > "+param(values.length));}
68
+ const translations:Record<string,unknown>[]=await read(db,child.sql+" WHERE "+where.join(" AND ")+" ORDER BY t.id LIMIT 500",values);
69
+ for(const row of translations)if(typeof row.locale==="string")byKey.set(row._key,(byKey.get(row._key)??new Set()).add(row.locale));
70
+ after=translations.length===500?String(translations.at(-1)!._id):undefined;
71
+ }while(after!==undefined);
72
+ }
73
+ for(const row of rows)for(const locale of byKey.get(row[child.on])??[])if(!out[String(row.id)]!.includes(locale))out[String(row.id)]!.push(locale);
74
+ }
75
+ return out;
76
+ }
77
+ ` };
78
+ }
@@ -0,0 +1,20 @@
1
+ import { dialect as sqlite } from "../kernel/dialect/sqlite.js";
2
+ import { dialect as postgres } from "../kernel/dialect/postgres.js";
3
+ import { entry, exportName, q } from "../domain/entry.js";
4
+ export function emitTtl(plan, isSqlite, declarations, procedures, entries, refuse) {
5
+ const d = isSqlite ? sqlite : postgres;
6
+ const schemas = Object.values(plan.schemas).filter(s => s.ttl).sort((a, b) => a.name.localeCompare(b.name));
7
+ if (schemas.length)
8
+ declarations.push(`import { sweep as sweepTtl } from "@aotter/mantle/sweep";`, `import { cutoff as ttlCutoff } from "@aotter/mantle/dialect/${isSqlite ? "sqlite" : "postgres"}";`);
9
+ for (const s of schemas) {
10
+ const name = `${s.name}.sweepExpired`, fn = exportName("procedure", name), path = `procedures/${name}`;
11
+ if (plan.procedures[name]) {
12
+ refuse(path, "This name is reserved for generated TTL maintenance.");
13
+ continue;
14
+ }
15
+ const pick = `SELECT _rid FROM ${d.ident(s.name)} WHERE ${d.ident(s.ttl)} IS NOT NULL AND ${d.ident(s.ttl)} <= ${d.param(1)} AND _rid > ${d.param(2)} ORDER BY _rid LIMIT ${d.param(3)}`;
16
+ declarations.push(`export type ${fn}Input = import("@aotter/mantle/sweep").SweepInput;`, `export type ${fn}Result = import("@aotter/mantle/sweep").SweepResult;`, `async function ${fn}(db: Database, caller: Caller, input: ${fn}Input = {}): Promise<${fn}Result> { return sweepTtl(db, caller, input, ${q(path)}, ${s.ttlSeconds}, ${q([pick, `DELETE FROM ${d.ident(s.name)} WHERE _rid IN (${pick}) RETURNING _rid`])}, {cutoff: ttlCutoff}, DRIVER.procedure); }`, `export { ${fn} };`);
17
+ procedures.push(`[${q(name)}]: ${fn}`);
18
+ entries[`procedures/${encodeURIComponent(name)}.ts`] = entry("procedure", name, `${fn}Input as Input, ${fn}Result as Output`);
19
+ }
20
+ }