@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,72 @@
1
+ import { adminEntries } from "./adminEntries.js";
2
+ import { wireScalars } from "../domain/wireScalars.js";
3
+ import { boundaryType } from "../domain/columns.js";
4
+ import { viewOutputs, compileProgram } from "../build/compiler.js";
5
+ import { pagedOf } from "../build/compiler.js";
6
+ import { castCursor } from "../domain/paging.js";
7
+ /** Proved per View: an unproved View is already refused at `views/<name>`; any other failure is refused at `views/<name>/admin`. */
8
+ export function adminPaging(plan, dialect, proofs, project, refuse) {
9
+ const all = Object.create(null);
10
+ for (const [name, v] of Object.entries(plan.views).filter(([, v]) => v.surface !== "internal")) {
11
+ const proof = proofs[name];
12
+ if (!proof || !proof.types.length && !proof.single)
13
+ continue;
14
+ try {
15
+ const list = (v.uiSchema?.list ?? {});
16
+ const modes = v.surface === "public" ? ["public", "public"] : ["caller", "trusted"];
17
+ const pages = modes.map(mode => {
18
+ const [c] = compileProgram(v.stmts, { dialect, schemas: plan.schemas, inputs: v.inputs, kind: "view", mode, restrict: v.restrict });
19
+ if (!c)
20
+ throw Error(`No compiled Admin View: ${name}`);
21
+ const keys = c.ast.SelectStmt.sortClause?.length ?? 0;
22
+ if (keys !== proof.types.length)
23
+ throw Error(`Incomplete Admin cursor proof: ${name}`);
24
+ const first = pagedOf({ schemas: plan.schemas, dialect }, c, undefined, !keys && (list.filterFields?.length || list.searchFields?.length) ? { eq: [{ column: (list.filterFields ?? list.searchFields)[0], value: "" }] } : undefined), next = keys ? pagedOf({ schemas: plan.schemas, dialect }, c, Array(keys).fill(0), undefined) : undefined;
25
+ if (!keys && !first.flat)
26
+ first.ast.SelectStmt.whereClause = undefined;
27
+ const reserved = JSON.stringify([c.ast, v.source, list, Object.values(plan.schemas).map(s => s.fields)]);
28
+ let marker = 32767;
29
+ while (reserved.includes(String(marker)))
30
+ marker++;
31
+ const print = (ast) => dialect.print(ast, plan.schemas);
32
+ const read = (p) => { const typed = castCursor(p.ast, p.sources, proof.types, !!dialect.nativeOrder); return { prefix: print(project({ SelectStmt: { ...typed.SelectStmt, whereClause: undefined, sortClause: undefined, limitCount: undefined, limitOption: "LIMIT_OPTION_DEFAULT" } }, p.flat ? viewOutputs(v, plan.schemas).keys ?? [] : p.names, keys, name)) + (typed.SelectStmt.whereClause ? ` WHERE (${expression(typed.SelectStmt.whereClause)})` : ""), extra: p.sources.filter(x => "cursor" in x).map(x => x.cursor) }; };
33
+ const suffixAst = { SelectStmt: { targetList: [{ ResTarget: { val: { A_Const: { ival: { ival: 1 } } } } }], sortClause: keys ? first.ast.SelectStmt.sortClause : undefined, limitCount: { ParamRef: { number: marker } }, limitOption: "LIMIT_OPTION_COUNT", op: "SETOP_NONE" } };
34
+ const tail = print(suffixAst).replace(/^SELECT 1/, "");
35
+ const expression = (ast) => print({ SelectStmt: { targetList: [{ ResTarget: { val: ast } }], limitOption: "LIMIT_OPTION_DEFAULT", op: "SETOP_NONE" } }).replace(/^SELECT /, "");
36
+ const fragment = (match, type) => {
37
+ const p = pagedOf({ schemas: plan.schemas, dialect }, c, undefined, match);
38
+ const ast = structuredClone(p.ast.SelectStmt.whereClause);
39
+ const renumber = (node) => { if (node.ParamRef) {
40
+ node.ParamRef.number = marker;
41
+ if (dialect.nativeOrder && (type === "date" || type === "timestamptz")) {
42
+ delete node.ParamRef;
43
+ node.Raw = { parts: ["CAST(CAST(", { ParamRef: { number: marker } }, ` AS text) AS ${type})`] };
44
+ return;
45
+ }
46
+ } for (const child of Object.values(node))
47
+ if (child && typeof child === "object")
48
+ Array.isArray(child) ? child.forEach(renumber) : renumber(child); };
49
+ renumber(ast);
50
+ return expression(ast);
51
+ };
52
+ return { marker: dialect.nativeOrder ? `$${marker}` : `?${marker}`, binds: c.binds, keys, first: read(first), ...(next ? { next: read(next) } : {}), tail, eq: Object.fromEntries((list.filterFields ?? []).map(field => { const col = v.columns?.[field] ?? v.columns?.[field.toLowerCase()], type = col && plan.schemas[col.schema]?.fields[col.field]; return [field, fragment({ eq: [{ column: field, value: "" }] }, type)]; })), ...(list.searchFields?.length ? { search: fragment({ search: { columns: list.searchFields, text: "x" } }) } : {}) };
53
+ });
54
+ all[name] = { caller: pages[0], trusted: pages[1] };
55
+ }
56
+ catch (error) {
57
+ refuse(`views/${name}/admin`, error instanceof Error ? error.message : String(error));
58
+ }
59
+ }
60
+ return all;
61
+ }
62
+ /** Only selected build-time Admin work; the ordinary generator remains unchanged. */
63
+ export async function adminBindingsOf(plan, dialect, paging, wire, refusals) {
64
+ const adminPages = adminPaging(plan, dialect, paging, (ast, names, nkeys, name) => {
65
+ const keys = [...names, ...Array.from({ length: nkeys }, (_, i) => `_k${i}`)];
66
+ const projected = wireScalars(ast, keys, [...names.map(key => boundaryType(plan, name, key)), ...(paging[name]?.types ?? [])], wire, true);
67
+ if (typeof projected === "string")
68
+ throw Error(projected);
69
+ return projected;
70
+ }, (path, message) => refusals.push({ code: "GENERATE_UNSUPPORTED", path, message }));
71
+ return { ok: true, adminPages, adminEntries: await adminEntries(plan, dialect, wire, refusals) };
72
+ }
@@ -0,0 +1,71 @@
1
+ import { readFile, readdir, mkdir, rm, writeFile, lstat } from "node:fs/promises";
2
+ import { dirname, join, resolve, relative, sep, isAbsolute } from "node:path";
3
+ import { GENERATED } from "../domain/entry.js";
4
+ /** Only marked generated files (and pre-header operation entries) can be removed. */
5
+ async function ownedFiles(directory, prefix = "") {
6
+ const entries = await readdir(join(directory, prefix), { withFileTypes: true }).catch(error => {
7
+ if (error.code === "ENOENT")
8
+ return [];
9
+ throw error;
10
+ });
11
+ const files = [];
12
+ for (const entry of entries) {
13
+ const name = join(prefix, entry.name);
14
+ if (entry.isDirectory())
15
+ files.push(...await ownedFiles(directory, name));
16
+ else if (entry.isFile() && /\.(ts|sql)$/.test(name)) {
17
+ const source = await readFile(join(directory, name), "utf8");
18
+ if (source.startsWith(`// ${GENERATED}\n`) || source.startsWith(`-- ${GENERATED}\n`) ||
19
+ /^(?:views|procedures)[/\\]/.test(name) && /^import \{ (?:view|procedure)_[A-Za-z0-9$]+ as operation \} from "\.\.\/generated\.js";\nexport default operation;/.test(source))
20
+ files.push(name);
21
+ }
22
+ }
23
+ return files;
24
+ }
25
+ /** Check never writes; normal generation removes obsolete SDK-owned files only. */
26
+ export async function writeArtifacts(directory, files, check = false) {
27
+ const root = resolve(directory);
28
+ const rootStat = await lstat(root).catch(error => { if (error.code === "ENOENT")
29
+ return undefined; throw error; });
30
+ if (rootStat?.isSymbolicLink())
31
+ throw new Error(`Generated output cannot follow an application-owned symlink: ${root}`);
32
+ // Preflight all destinations before writing any: a symlink belongs to the app,
33
+ // never to generated output (including links to somewhere inside the tree).
34
+ for (const name of Object.keys(files)) {
35
+ const child = relative(root, resolve(root, name));
36
+ if (!child || isAbsolute(child) || child === ".." || child.startsWith(`..${sep}`))
37
+ throw new Error(`Generated path escapes output directory: ${name}`);
38
+ let path = root;
39
+ for (const part of child.split(sep)) {
40
+ path = join(path, part);
41
+ const stat = await lstat(path).catch(error => { if (error.code === "ENOENT")
42
+ return undefined; throw error; });
43
+ if (stat?.isSymbolicLink())
44
+ throw new Error(`Generated output cannot follow an application-owned symlink: ${path}`);
45
+ }
46
+ }
47
+ const changed = [];
48
+ for (const [name, source] of Object.entries(files)) {
49
+ const path = join(directory, name);
50
+ const previous = await readFile(path, "utf8").catch(error => {
51
+ if (error.code === "ENOENT")
52
+ return undefined;
53
+ throw error;
54
+ });
55
+ if (previous === source)
56
+ continue;
57
+ changed.push(path);
58
+ if (!check) {
59
+ await mkdir(dirname(path), { recursive: true });
60
+ await writeFile(path, source);
61
+ }
62
+ }
63
+ for (const name of await ownedFiles(directory)) {
64
+ if (Object.hasOwn(files, name.replaceAll("\\", "/")))
65
+ continue;
66
+ changed.push(join(directory, name));
67
+ if (!check)
68
+ await rm(join(directory, name));
69
+ }
70
+ return changed.sort();
71
+ }
@@ -0,0 +1,83 @@
1
+ import { getAuthTables } from "@better-auth/core/db";
2
+ import { getSchema } from "better-auth/db";
3
+ import { ident } from "../kernel/dialect/common.js";
4
+ function columnType(field, dialect) {
5
+ const pg = dialect === "postgres";
6
+ // Anything else, including the arrays the factory stores as JSON text, is text (Bun.SQL would encode that text into jsonb as a JSON string).
7
+ switch (field.type) {
8
+ case "number": return field.bigint ? "bigint" : "integer";
9
+ case "boolean": return pg ? "boolean" : "integer";
10
+ case "date": return pg ? "timestamptz" : "text";
11
+ case "json": return pg ? "jsonb" : "text";
12
+ default: return "text";
13
+ }
14
+ }
15
+ /**
16
+ * Better Auth's name for a field-level index (`<table>_<column>_idx`; past PostgreSQL's 63-byte identifier limit the middle is
17
+ * cut and an FNV-1a hash of the full name is spliced in). Its helper lives in `@better-auth/core/db/internal`, which is not public
18
+ * API, so the same rule is restated here: a database already migrated by Better Auth's CLI keeps matching names, and
19
+ * `IF NOT EXISTS` skips the index instead of duplicating it.
20
+ */
21
+ export function fieldIndexName(table, column) {
22
+ const full = `${table}_${column}_idx`;
23
+ if (new TextEncoder().encode(full).length <= 63)
24
+ return full;
25
+ let hash = 2166136261;
26
+ for (let i = 0; i < full.length; i++)
27
+ hash = Math.imul(hash ^ full.charCodeAt(i), 16777619);
28
+ const suffix = `_${(hash >>> 0).toString(16).padStart(8, "0")}_idx`;
29
+ return full.slice(0, 63 - suffix.length) + suffix;
30
+ }
31
+ /** Better Auth's own `order`, then referenced tables before the tables that reference them. */
32
+ function orderedTables(options) {
33
+ const byOrder = Object.entries(getSchema(options)).filter(([, table]) => !table.disableMigrations).sort((a, b) => a[1].order - b[1].order);
34
+ const ordered = [];
35
+ const place = (entry, visiting) => {
36
+ if (ordered.includes(entry) || visiting.has(entry[0]))
37
+ return;
38
+ visiting.add(entry[0]);
39
+ for (const field of Object.values(entry[1].fields)) {
40
+ const target = byOrder.find(([name]) => name === field.references?.model);
41
+ if (target)
42
+ place(target, visiting);
43
+ }
44
+ ordered.push(entry);
45
+ };
46
+ for (const entry of byOrder)
47
+ place(entry, new Set());
48
+ return ordered;
49
+ }
50
+ export function authSchemaSql(options, dialect) {
51
+ const generateId = options.advanced?.database?.generateId;
52
+ const idType = generateId === "serial" ? "integer" : generateId === "uuid" && dialect === "postgres" ? "uuid" : "text";
53
+ const idColumn = generateId === "serial" ? (dialect === "postgres" ? "integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY" : "integer PRIMARY KEY") : `${idType} PRIMARY KEY NOT NULL${idType === "uuid" ? " DEFAULT gen_random_uuid()" : ""}`;
54
+ // `references.field` is a logical field name; the column it points at may be renamed (`fields: { email: "email_address" }`).
55
+ const tables = getAuthTables(options);
56
+ const logical = Object.values(tables);
57
+ const referenced = (ref) => logical.find(t => t.modelName === ref.model)?.fields[ref.field]?.fieldName ?? ref.field;
58
+ const statements = [];
59
+ const indexes = [];
60
+ for (const [name, table] of orderedTables(options)) {
61
+ const index = (unique, index, columns) => indexes.push(`CREATE ${unique ? "UNIQUE " : ""}INDEX IF NOT EXISTS ${ident(index)} ON ${ident(name)} (${columns.map(ident).join(", ")})`);
62
+ if (name === tables.user?.modelName)
63
+ index(false, fieldIndexName(`_mantle_auth_${name}`, "member_page"), [tables.user.fields.createdAt?.fieldName ?? "createdAt", "id"]);
64
+ const columns = [`${ident("id")} ${idColumn}`];
65
+ for (const [column, field] of Object.entries(table.fields)) {
66
+ if (column === "id")
67
+ continue;
68
+ const type = field.references?.field === "id" ? idType : columnType(field, dialect);
69
+ columns.push([
70
+ ident(column), type,
71
+ field.required !== false && "NOT NULL",
72
+ field.unique && "UNIQUE",
73
+ field.references && `REFERENCES ${ident(field.references.model)}(${ident(referenced(field.references))}) ON DELETE ${(field.references.onDelete ?? "cascade").toUpperCase()}`,
74
+ ].filter(Boolean).join(" "));
75
+ if (field.index && !field.unique)
76
+ index(false, fieldIndexName(name, column), [column]);
77
+ }
78
+ statements.push(`CREATE TABLE IF NOT EXISTS ${ident(name)} (${columns.join(", ")})`);
79
+ for (const declared of table.indexes ?? [])
80
+ index(declared.unique, declared.name, declared.columns);
81
+ }
82
+ return [...statements, ...indexes];
83
+ }
@@ -0,0 +1,188 @@
1
+ import { existsSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import { build } from "esbuild";
6
+ import { INPUTS_FILE, authTableSql, extraRoles, generatedTarget, staleInputs } from "./inputs.js";
7
+ /** Only static imports run at startup; dynamic imports remain separate uploaded modules. */
8
+ export function startupGraph(metafile, entry) {
9
+ const visited = new Set();
10
+ const visit = (name) => {
11
+ if (visited.has(name))
12
+ return;
13
+ visited.add(name);
14
+ const output = metafile.outputs[name];
15
+ for (const imported of output.imports) {
16
+ if (!imported.external && imported.kind !== "dynamic-import")
17
+ visit(imported.path);
18
+ }
19
+ };
20
+ for (const [name, output] of Object.entries(metafile.outputs))
21
+ if (output.entryPoint && resolve(output.entryPoint) === resolve(entry))
22
+ visit(name);
23
+ return [...visited];
24
+ }
25
+ const refused = [
26
+ [/(?:^|\/)node_modules\/(?:better-auth(?:\/|$)|@better-auth\/)/, "Load it through authLoader(() => import(...)) to keep Better Auth out of startup."],
27
+ [/(?:^|\/)node_modules\/@modelcontextprotocol\//, "Load MCP through a request-time import(...)."],
28
+ // the compiler: the package's own generator layers (installed, or workspace dist/source) and its parser, YAML and SQL dependencies
29
+ [/(?:^|\/)node_modules\/(?:@aotter\/mantle\/dist\/(?:build|infrastructure)\/|yaml\/|pgsql-deparser\/|libpg-query\/)|(?:^|\/)mantle\/(?:dist|src)\/(?:build|infrastructure)\//, "Generator code must not run in a Worker; import runtime entry points such as @aotter/mantle/rest."],
30
+ // Admin's UI is built into <assets>/admin by the app's admin.tsx; the Worker serves it, never runs it.
31
+ [/(?:^|\/)node_modules\/@aotter\/mantle-admin\/dist\/admin-app\/|(?:^|\/)mantle-admin\/(?:dist\/admin-app|admin\/src)\//, "Admin's UI (@aotter/mantle-admin/app, /hooks) runs in the browser: import it only from src/admin.tsx, which `mantle bundle` builds into the assets directory."],
32
+ ];
33
+ export function guardStartup(metafile, entry) {
34
+ for (const name of startupGraph(metafile, entry)) {
35
+ const output = metafile.outputs[name];
36
+ for (const input of Object.keys(output.inputs)) {
37
+ // shortcut: vendored auth outside node_modules is not recognized; use package metadata if vendoring is supported.
38
+ const hit = refused.find(([pattern]) => pattern.test(input.replaceAll("\\", "/")));
39
+ if (hit)
40
+ throw new Error(`Startup module ${name} contains ${input}. ${hit[1]}`);
41
+ }
42
+ }
43
+ }
44
+ /** The physical spelling of a path, including a nonexistent tail below its nearest existing ancestor. */
45
+ const physical = (input) => {
46
+ let found = resolve(input);
47
+ const tail = [];
48
+ while (!existsSync(found)) {
49
+ const parent = dirname(found);
50
+ if (parent === found)
51
+ return found;
52
+ tail.unshift(basename(found));
53
+ found = parent;
54
+ }
55
+ return resolve(realpathSync(found), ...tail);
56
+ };
57
+ /** Whether `inner` is `outer` or inside it. */
58
+ const within = (outer, inner) => {
59
+ const path = relative(physical(outer), physical(inner));
60
+ return path === "" || (path !== ".." && !path.startsWith(`..${sep}`) && !isAbsolute(path));
61
+ };
62
+ /** A directory a build empties must not be the working directory or hold the source entry. */
63
+ function assertCleanable(label, dir, entry) {
64
+ if (physical(dir) === physical(process.cwd()))
65
+ throw new Error(`${label} must not be the working directory: it is cleaned before building.`);
66
+ if (within(dir, entry))
67
+ throw new Error(`${label} must not contain the source entry: it is cleaned before building.`);
68
+ }
69
+ /** The app's own modules only (packages external): enough to find its generated output and what it imports. */
70
+ export const ownModules = async (entry) => (await build({ entryPoints: [entry], bundle: true, write: false, metafile: true, packages: "external", format: "esm", platform: "neutral", logLevel: "silent", absWorkingDir: process.cwd() })).metafile;
71
+ /**
72
+ * `mantle bundle <entry> <outdir> [--assets <dir>]`: the Worker into `<outdir>/worker`, and Admin into `<assets>/admin`
73
+ * (default `<outdir>/client`) when the Worker serves it (`@aotter/mantle-admin/server`; `/mcp` and the other subpaths alone do not).
74
+ * A server whose generated output records a `bun-*` target runs as TypeScript, so only its Admin is built.
75
+ */
76
+ export async function bundleApp(entry, outdir, assets = join(outdir, "client")) {
77
+ const source = realpathSync(entry);
78
+ // what is cleaned lies under these two, so neither may be the working directory or hold the source
79
+ assertCleanable("Bundle outdir", outdir, source);
80
+ assertCleanable("The assets directory", assets, source);
81
+ const own = await ownModules(entry);
82
+ if (generatedDirs(own).some(dir => generatedTarget(join(dir, "generated.ts"))?.startsWith("bun-"))) {
83
+ // Packages are external here, so only the app's own imports of the Admin server are seen; one reached through another package is not.
84
+ if (Object.values(own.inputs).some(input => input.imports.some(imported => /^@aotter\/mantle-admin\/server(?:\/|$)/.test(imported.path))))
85
+ await bundleAdmin(source, outdir, join(assets, "admin"));
86
+ else
87
+ console.warn("note: no Admin built: the entry's own modules do not import @aotter/mantle-admin/server (an import through another package is not seen).");
88
+ return;
89
+ }
90
+ const worker = join(outdir, "worker");
91
+ // Wrangler uploads every module under the Worker directory and every file under the assets directory.
92
+ if (within(worker, assets) || within(assets, worker))
93
+ throw new Error(`The assets directory ${assets} must not overlap the Worker directory ${worker}.`);
94
+ const metafile = await bundle(entry, worker);
95
+ if (Object.keys(metafile.inputs).some(input => /(?:^|\/)mantle-admin\/(?:dist|src)\/server\/index\.[jt]s$/.test(input.replaceAll("\\", "/")))) {
96
+ await bundleAdmin(source, outdir, join(assets, "admin"));
97
+ }
98
+ }
99
+ /**
100
+ * Admin from `<dirname(entry)>/admin.tsx`, or the built-in entry; an optional `admin.mcp.tsx` selects the host-safe graph.
101
+ * Uses `@aotter/mantle-admin/build` as the app installed
102
+ * it. The built-in entry is skipped when the installed Admin and its direct dependencies are unchanged since the last
103
+ * build; an application entry always builds, because what it imports is only known by building it.
104
+ */
105
+ async function bundleAdmin(entry, outdir, out) {
106
+ const source = join(dirname(entry), "admin.tsx"), custom = existsSync(source);
107
+ const mcpSource = join(dirname(entry), "admin.mcp.tsx"), customMcp = existsSync(mcpSource);
108
+ assertCleanable("The Admin output directory", out, entry);
109
+ const require = createRequire(entry);
110
+ let builder;
111
+ try {
112
+ builder = require.resolve("@aotter/mantle-admin/build");
113
+ }
114
+ catch {
115
+ throw new Error(`The Worker serves Admin, but @aotter/mantle-admin/build does not resolve from ${relative(process.cwd(), entry)}. Install @aotter/mantle-admin at the version of @aotter/mantle-admin/server.`);
116
+ }
117
+ const stamp = join(outdir, ".admin-inputs");
118
+ // the installed path too: a patched or linked Admin keeps its version but not its path
119
+ const manifest = realpathSync(lookup(require, "@aotter/mantle-admin") ?? require.resolve("@aotter/mantle-admin/package.json"));
120
+ const key = custom || customMcp ? undefined : JSON.stringify({ out: resolve(out), path: dirname(manifest), packages: installed(manifest) });
121
+ if (key && existsSync(join(out, "index.html")) && existsSync(join(out, "mcp-app.html")) && existsSync(stamp) && readFileSync(stamp, "utf8") === key) {
122
+ console.log(`Admin inputs unchanged; kept ${relative(process.cwd(), out)}.`);
123
+ return;
124
+ }
125
+ rmSync(stamp, { force: true });
126
+ const { buildAdmin } = await import(pathToFileURL(builder).href);
127
+ await buildAdmin({ entry: custom ? source : undefined, ...(customMcp ? { mcpEntry: mcpSource } : {}), outDir: out });
128
+ if (key)
129
+ writeFileSync(stamp, key);
130
+ }
131
+ /** The package.json a bare import of `name` finds, read from disk (`exports` may hide it, and require caches realpaths). */
132
+ const lookup = (require, name) => require.resolve.paths(name)?.map(dir => join(dir, name, "package.json")).find(file => existsSync(file));
133
+ /** The package's version and the installed version of each of its dependencies and peers (React comes from the application). */
134
+ function installed(manifest) {
135
+ const { name, version, dependencies = {}, peerDependencies = {} } = JSON.parse(readFileSync(manifest, "utf8"));
136
+ const require = createRequire(manifest);
137
+ const versions = { [name]: version };
138
+ for (const dependency of Object.keys({ ...peerDependencies, ...dependencies }).sort()) {
139
+ const found = lookup(require, dependency);
140
+ versions[dependency] = found ? JSON.parse(readFileSync(found, "utf8")).version : null;
141
+ }
142
+ return versions;
143
+ }
144
+ /** The generated directories among a build's inputs, refusing stale ones. */
145
+ function generatedDirs(metafile) {
146
+ const dirs = [...new Set(Object.keys(metafile.inputs).map(input => resolve(input, "..")).filter(dir => existsSync(resolve(dir, INPUTS_FILE))))];
147
+ for (const dir of dirs) {
148
+ const input = resolve(dir, "generated.ts"), stale = staleInputs(input);
149
+ if (stale.length)
150
+ throw new Error(`Generated output in ${relative(process.cwd(), resolve(input, ".."))} is stale (${stale.join("; ")}). Run your generate script (mantle generate ...) and rebuild.`);
151
+ }
152
+ return dirs;
153
+ }
154
+ export async function bundle(entry, outdir) {
155
+ entry = realpathSync(entry);
156
+ assertCleanable("Bundle outdir", outdir, entry);
157
+ rmSync(outdir, { recursive: true, force: true });
158
+ const result = await build({
159
+ entryPoints: [entry], outdir, bundle: true, splitting: true, format: "esm", platform: "neutral",
160
+ conditions: ["workerd", "worker", "browser"], mainFields: ["module", "main"],
161
+ external: ["cloudflare:*", "node:*"], minify: true, metafile: true, chunkNames: "chunks/[name]-[hash]",
162
+ plugins: [{ name: "optional-peers", setup(builder) {
163
+ builder.onResolve({ filter: /^(?:better-auth|@better-auth\/(?:core|oauth-provider)|@modelcontextprotocol\/server)(?:\/|$)/ }, async (args) => {
164
+ if (args.pluginData?.optionalPeer)
165
+ return;
166
+ const resolved = await builder.resolve(args.path, { importer: args.importer, resolveDir: args.resolveDir, kind: args.kind, pluginData: { optionalPeer: true } });
167
+ // Unselected request paths may reference absent peers; installed peers still bundle normally.
168
+ return resolved.errors.length ? { path: args.path, external: true } : resolved;
169
+ });
170
+ } }],
171
+ });
172
+ if (Object.keys(result.metafile.inputs).some(input => /(?:^|\/)mantle\/(?:src|dist)\/testing\//.test(resolve(input).replaceAll("\\", "/"))))
173
+ throw new Error("@aotter/mantle/testing is only for tests and cannot be bundled for production.");
174
+ // A build is what `wrangler dev` and deploys run: generated code must match the manifests it came from.
175
+ // Any bundled module from a generated directory finds its fingerprint (type-only imports leave generated.ts out).
176
+ generatedDirs(result.metafile);
177
+ const appSources = Object.keys(result.metafile.inputs).filter(input => !/node_modules|(?:^|\/)generated\/|^\.\.\//.test(input) && /\.[cm]?[jt]s$/.test(input)).map(input => [input, readFileSync(input, "utf8")]);
178
+ for (const [input, source] of appSources) {
179
+ const tables = authTableSql(source);
180
+ if (tables.length)
181
+ console.warn(`note: ${input} queries Better Auth's ${tables.join(", ")} table with SQL. Read users through userOf(caller) or readIdentity(auth) (@aotter/mantle/auth, @aotter/mantle/auth/identity), which follow each instance's table mapping and ban state.`);
182
+ }
183
+ const roles = appSources.flatMap(([, source]) => extraRoles(source));
184
+ if (roles.length)
185
+ console.warn(`note: Better Auth roles beyond admin and user: ${[...new Set(roles)].join(", ")}. Mantle grants by the admin layer and app code; keep extra roles only if the app needs them.`);
186
+ guardStartup(result.metafile, entry);
187
+ return result.metafile;
188
+ }
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync } from "node:fs";
3
+ import { readFile } from "node:fs/promises";
4
+ import { join, resolve, relative } from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+ import { parseArgs } from "node:util";
7
+ import { configureWrangler } from "./wrangler.js";
8
+ import { writeArtifacts } from "./artifacts.js";
9
+ import { INPUTS_FILE, inputsFingerprint } from "./inputs.js";
10
+ /** Option modules are often .ts, which older Node rejects with a bare "Unknown file extension". */
11
+ async function load(path) {
12
+ try {
13
+ return await import(pathToFileURL(resolve(path)).href);
14
+ }
15
+ catch (error) {
16
+ if (error.code === "ERR_UNKNOWN_FILE_EXTENSION")
17
+ throw new Error(`Cannot load ${path}: Node ${process.versions.node} does not run TypeScript. Use Node 22.22 or later, as the package engines say.`);
18
+ throw error;
19
+ }
20
+ }
21
+ const targets = ["bun-sqlite", "bun-postgres", "cloudflare-d1", "cloudflare-postgres"];
22
+ const usage = "Usage: mantle help [topic] (start here: the installed docs, one topic at a time)\n mantle init [directory] [--dry-run]\n mantle generate <target> <output-directory> <manifest>... [--check] [--admin] [--site <module>] [--auth <module>]... (one per Better Auth instance) [--wrangler <path>] [--entry <worker entry>] [--outdir <bundle directory>] [--assets <static assets directory>]\n mantle bundle <entry> <outdir> [--assets <static assets directory>]\n mantle describe <target> <manifest>... [--diff <old.json>]";
23
+ try {
24
+ const { values, positionals } = parseArgs({ allowPositionals: true, options: {
25
+ diff: { type: "string" }, check: { type: "boolean" }, admin: { type: "boolean" }, site: { type: "string" }, auth: { type: "string", multiple: true }, wrangler: { type: "string" }, entry: { type: "string", default: "src/worker.ts" }, outdir: { type: "string", default: "dist" }, assets: { type: "string" }, "dry-run": { type: "boolean" }, help: { type: "boolean", short: "h" },
26
+ } });
27
+ const named = ["bundle", "generate", "describe", "help", "init"].includes(positionals[0]) ? positionals.shift() : undefined;
28
+ // `-h` prints usage and never runs a command; bare `mantle` starts at help, but options with no positionals stay a usage error.
29
+ const command = values.help ? "usage" : named ?? (process.argv.length > 2 ? "generate" : "help");
30
+ if (command === "usage")
31
+ console.log(`${usage}\n\nStart with mantle help: the installed docs, one topic at a time.`);
32
+ else if (command === "help") {
33
+ const { help, topicList } = await import("./help.js");
34
+ if (positionals.length > 1)
35
+ throw new Error(usage);
36
+ console.log(positionals[0] === "topics" ? topicList() : help(positionals[0]));
37
+ }
38
+ else if (command === "init") {
39
+ if (positionals.length > 1)
40
+ throw new Error(usage);
41
+ console.log((await import("./init.js")).init(positionals[0] ?? ".", values["dry-run"]));
42
+ }
43
+ else if (command === "describe") {
44
+ const target = positionals.shift();
45
+ if (!target || !targets.includes(target) || !positionals.length)
46
+ throw new Error(usage);
47
+ const { compilePlan, sqliteCompiler, postgresCompiler } = await import("../build/compiler.js");
48
+ const { describeApp, diffApp } = await import("../domain/describe/index.js");
49
+ const sources = await Promise.all(positionals.map(async (path) => ({ sourceId: path, text: await readFile(path, "utf8") })));
50
+ const compiled = await compilePlan({ sources }, target.endsWith("postgres") ? postgresCompiler : sqliteCompiler, "operational");
51
+ if (!compiled.ok)
52
+ throw new Error(JSON.stringify(compiled.diagnostics, null, 2));
53
+ const report = await describeApp(compiled.plan);
54
+ if (values.diff) {
55
+ const changes = diffApp(JSON.parse(await readFile(values.diff, "utf8")), report);
56
+ console.log(JSON.stringify(changes, null, 2));
57
+ }
58
+ else
59
+ console.log(JSON.stringify(report, null, 2));
60
+ }
61
+ else if (command === "bundle") {
62
+ const [entry, outdir] = positionals;
63
+ if (!entry || !outdir || positionals.length !== 2 || values.admin || values.site || values.auth || values.wrangler || values.check)
64
+ throw new Error(usage);
65
+ await (await import("./bundle.js")).bundleApp(entry, outdir, values.assets);
66
+ }
67
+ else {
68
+ const [target, destination, ...paths] = positionals;
69
+ if (!target || !targets.includes(target) || !destination || !paths.length)
70
+ throw new Error(usage);
71
+ // A shell leaves an unmatched glob as is; say what is missing instead of ENOENT.
72
+ for (const path of paths)
73
+ if (/[*?]/.test(path) && !existsSync(path))
74
+ throw new Error(`No manifest matches ${path}. Add one (mantle help four-atoms, mantle help manifest), then run generate again.`);
75
+ const sources = await Promise.all(paths.map(async (path) => ({ sourceId: path, text: await readFile(path, "utf8") })));
76
+ // Read plain options without constructing Better Auth, which can touch the database.
77
+ // One --auth per Better Auth instance; their tables are merged into one migration.
78
+ const auth = await Promise.all((values.auth ?? []).map(async (path) => {
79
+ const loaded = await load(path);
80
+ const options = loaded.authOptions?.(new Proxy({}, { get: () => undefined })) ?? loaded.auth?.options;
81
+ if (!options)
82
+ throw new Error(`${path} must export \`authOptions(env)\` (or \`auth\`, a betterAuth() instance).`);
83
+ return options;
84
+ }));
85
+ const site = values.site ? (await load(values.site)).siteDefaults : undefined;
86
+ if (values.site && !site)
87
+ throw new Error(`${values.site} must export siteDefaults.`);
88
+ const { generateFromSources } = await (auth.length ? import("./generateAuth.js") : import("./generate.js"));
89
+ const result = await generateFromSources({ target: target, sources: { sources }, admin: values.admin, auth, site });
90
+ if (!result.ok)
91
+ throw new Error(JSON.stringify(result.diagnostics, null, 2));
92
+ for (const warning of result.warnings ?? [])
93
+ console.warn(`warning: ${warning}`);
94
+ const read = [...paths, ...(values.auth ?? []), ...(values.site ? [values.site] : [])];
95
+ const changed = await writeArtifacts(destination, { ...result.files, [INPUTS_FILE]: inputsFingerprint(destination, read, target) }, values.check);
96
+ // After writing: the entry's imports of generated modules only resolve once they exist.
97
+ for (const warning of await (await import("./mounts.js")).unmountedWarnings(values.entry, destination, result.files))
98
+ console.warn(`warning: ${warning}`);
99
+ if (target.startsWith("cloudflare-")) {
100
+ const wrangler = values.wrangler ?? ["wrangler.jsonc", "wrangler.json", "wrangler.toml"].find(existsSync) ?? "wrangler.jsonc";
101
+ const assets = values.admin ? values.assets ?? join(values.outdir, "client") : undefined;
102
+ const publicFetchRequired = auth.some(options => options.plugins?.some((plugin) => plugin.id === "oauth-provider" || plugin.id === "cimd"));
103
+ if (configureWrangler(wrangler, values.entry, values.outdir, destination, values.check, assets, publicFetchRequired))
104
+ changed.push(wrangler);
105
+ }
106
+ if (values.check && changed.length)
107
+ throw new Error(`Generated output is stale. Run mantle generate with the same options:\n${changed.join("\n")}`);
108
+ if (!values.check)
109
+ console.log(`Commit generated output and Wrangler config edits; keep dist/ untracked. Add to .gitattributes: ${JSON.stringify(relative(process.cwd(), resolve(destination)).replaceAll("\\", "/") + "/**")} linguist-generated=true`);
110
+ // A contract, not a to-do: the CLI does not read the app's handlers; tsc checks them against Handlers.
111
+ if (!values.check && result.refProcedures.length)
112
+ console.log(`Ref Procedures need these Handlers keys (tsc --noEmit checks them; pass them at store(db, caller, { handlers })): ${result.refProcedures.map(name => JSON.stringify(name)).join(", ")}`);
113
+ }
114
+ }
115
+ catch (error) {
116
+ console.error(error instanceof Error ? error.message : String(error));
117
+ process.exitCode = 1;
118
+ }
@@ -0,0 +1,12 @@
1
+ /** Build-time assembly only. None of these imports enter generated applications. */
2
+ import { d1Dialect, type ManifestSourceSet } from "../build/compiler.js";
3
+ import type { GenerateResult, Target } from "../domain/generation.js";
4
+ /** Core's own new-entry-id lowering for a dialect, printed once as an SQL expression: the Store's inserts carry the same one its compiled INSERTs do. */
5
+ export declare const coreNewId: (dialect: Pick<typeof d1Dialect, "print" | "lowering">, schemas: Parameters<NonNullable<typeof d1Dialect.print>>[1]) => string;
6
+ export interface GenerateRequest {
7
+ readonly target: Target;
8
+ readonly admin?: boolean;
9
+ readonly sources: ManifestSourceSet; /** Select the optional generated site/media capability and deployment tables. */
10
+ readonly site?: import("../build/compiler.js").SiteDefaults;
11
+ }
12
+ export declare function generateFromSources(request: GenerateRequest, schema?: () => readonly string[]): Promise<GenerateResult>;