@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.
- package/README.md +166 -298
- package/dist/auth/adapter.d.ts +32 -0
- package/dist/auth/adapter.js +288 -0
- package/dist/auth/admin.d.ts +3 -0
- package/dist/auth/admin.js +21 -0
- package/dist/auth/caller.d.ts +86 -0
- package/dist/auth/caller.js +155 -0
- package/dist/auth/hardening.d.ts +24 -0
- package/dist/auth/hardening.js +40 -0
- package/dist/auth/identity.d.ts +121 -0
- package/dist/auth/identity.js +203 -0
- package/dist/auth/oauth.d.ts +63 -0
- package/dist/auth/oauth.js +217 -0
- package/dist/auth/resource-seeds.js +10 -0
- package/dist/auth/routes.d.ts +32 -0
- package/dist/auth/routes.js +226 -0
- package/dist/build/compiler.d.ts +721 -0
- package/dist/build/compiler.js +7029 -0
- package/dist/build/spec/domain/model/ManifestGrammar.d.ts +439 -0
- package/dist/build/spec/domain/model/RuntimePlan.d.ts +168 -0
- package/dist/build/spec/domain/model/SqlIr.d.ts +129 -0
- package/dist/build/spec/domain/model/UserFields.d.ts +21 -0
- package/dist/build/spec/domain/service/PlanFingerprint.d.ts +7 -0
- package/dist/build/spec/kernel/diagnostic.d.ts +108 -0
- package/dist/bun/assets.d.ts +2 -0
- package/dist/bun/assets.js +24 -0
- package/dist/domain/adminOrder.js +24 -0
- package/dist/domain/columns.js +57 -0
- package/dist/domain/describe/chunk-ZVXBGP7Y.js +424 -0
- package/dist/domain/describe/index.d.ts +58 -0
- package/dist/domain/describe/index.js +115 -0
- package/dist/domain/describe/target-hash.d.ts +8 -0
- package/dist/domain/describe/target-hash.js +12 -0
- package/dist/domain/describe/targets.d.ts +40 -0
- package/dist/domain/entry.js +33 -0
- package/dist/domain/generated-tools.js +19 -0
- package/dist/domain/generation.d.ts +16 -0
- package/dist/domain/generation.js +1 -0
- package/dist/domain/input.js +17 -0
- package/dist/domain/model.js +1 -0
- package/dist/domain/paging.js +230 -0
- package/dist/domain/recipes.js +24 -0
- package/dist/domain/userReferences.js +5 -0
- package/dist/domain/wireScalars.js +47 -0
- package/dist/infrastructure/adminEntries.js +153 -0
- package/dist/infrastructure/adminPaging.js +72 -0
- package/dist/infrastructure/artifacts.js +71 -0
- package/dist/infrastructure/authSchema.js +83 -0
- package/dist/infrastructure/bundle.js +188 -0
- package/dist/infrastructure/cli.js +118 -0
- package/dist/infrastructure/generate.d.ts +12 -0
- package/dist/infrastructure/generate.js +189 -0
- package/dist/infrastructure/generateAuth.d.ts +8 -0
- package/dist/infrastructure/generateAuth.js +74 -0
- package/dist/infrastructure/help.js +60 -0
- package/dist/infrastructure/init.js +54 -0
- package/dist/infrastructure/inputs.js +41 -0
- package/dist/infrastructure/mounts.js +25 -0
- package/dist/infrastructure/wrangler.js +109 -0
- package/dist/kernel/authorization.d.ts +4 -0
- package/dist/kernel/authorization.js +16 -0
- package/dist/kernel/binds.d.ts +60 -0
- package/dist/kernel/binds.js +49 -0
- package/dist/kernel/caller-identity.d.ts +24 -0
- package/dist/kernel/caller-identity.js +22 -0
- package/dist/kernel/contract-error.d.ts +12 -0
- package/dist/kernel/contract-error.js +14 -0
- package/dist/kernel/contracts.d.ts +143 -0
- package/dist/kernel/contracts.js +97 -0
- package/dist/kernel/cron.d.ts +1 -0
- package/dist/kernel/cron.js +58 -0
- package/dist/kernel/dialect/common.d.ts +38 -0
- package/dist/kernel/dialect/common.js +6 -0
- package/dist/kernel/dialect/postgres.d.ts +16 -0
- package/dist/kernel/dialect/postgres.js +18 -0
- package/dist/kernel/dialect/sqlite.d.ts +20 -0
- package/dist/kernel/dialect/sqlite.js +34 -0
- package/dist/kernel/drain.d.ts +5 -0
- package/dist/kernel/drain.js +35 -0
- package/dist/kernel/driver/bun-sqlite.d.ts +19 -0
- package/dist/kernel/driver/bun-sqlite.js +77 -0
- package/dist/kernel/driver/d1.d.ts +24 -0
- package/dist/kernel/driver/d1.js +22 -0
- package/dist/kernel/driver/postgres.d.ts +25 -0
- package/dist/kernel/driver/postgres.js +99 -0
- package/dist/kernel/driver/transaction-context.d.ts +10 -0
- package/dist/kernel/driver/transaction-context.js +29 -0
- package/dist/kernel/email.d.ts +28 -0
- package/dist/kernel/email.js +38 -0
- package/dist/kernel/host-access.d.ts +10 -0
- package/dist/kernel/host-access.js +65 -0
- package/dist/kernel/host-caller.d.ts +39 -0
- package/dist/kernel/host-caller.js +56 -0
- package/dist/kernel/json-body.d.ts +4 -0
- package/dist/kernel/json-body.js +44 -0
- package/dist/kernel/mcp-apps.d.ts +69 -0
- package/dist/kernel/mcp-apps.js +40 -0
- package/dist/kernel/mcp-call.d.ts +5 -0
- package/dist/kernel/mcp-call.js +25 -0
- package/dist/kernel/mcp-handler.js +371 -0
- package/dist/kernel/mcp.d.ts +110 -0
- package/dist/kernel/mcp.js +50 -0
- package/dist/kernel/media.d.ts +27 -0
- package/dist/kernel/media.js +216 -0
- package/dist/kernel/paging.d.ts +55 -0
- package/dist/kernel/paging.js +95 -0
- package/dist/kernel/publishing.d.ts +4 -0
- package/dist/kernel/publishing.js +16 -0
- package/dist/kernel/r2-media.d.ts +42 -0
- package/dist/kernel/r2-media.js +78 -0
- package/dist/kernel/rest-dispatch.js +38 -0
- package/dist/kernel/rest-router.js +64 -0
- package/dist/kernel/rest.d.ts +58 -0
- package/dist/kernel/rest.js +41 -0
- package/dist/kernel/scalars.d.ts +6 -0
- package/dist/kernel/scalars.js +44 -0
- package/dist/kernel/search.js +5 -0
- package/dist/kernel/site-types.d.ts +146 -0
- package/dist/kernel/site-types.js +1 -0
- package/dist/kernel/site.d.ts +39 -0
- package/dist/kernel/site.js +32 -0
- package/dist/kernel/store.d.ts +175 -0
- package/dist/kernel/store.js +269 -0
- package/dist/kernel/surface.d.ts +10 -0
- package/dist/kernel/surface.js +19 -0
- package/dist/kernel/sweep.d.ts +17 -0
- package/dist/kernel/sweep.js +27 -0
- package/dist/kernel/translations.d.ts +1 -0
- package/dist/kernel/translations.js +13 -0
- package/dist/kernel/user-caller.d.ts +31 -0
- package/dist/kernel/user-caller.js +67 -0
- package/dist/kernel/user-field-name.js +3 -0
- package/dist/kernel/user-fields.d.ts +35 -0
- package/dist/kernel/user-fields.js +81 -0
- package/dist/kernel/validation.d.ts +15 -0
- package/dist/kernel/validation.js +54 -0
- package/dist/kernel/web-gateway.js +68 -0
- package/dist/kernel/web-head.js +15 -0
- package/dist/kernel/web-index.js +40 -0
- package/dist/kernel/web-response.js +8 -0
- package/dist/kernel/web.d.ts +77 -0
- package/dist/kernel/web.js +124 -0
- package/dist/kernel/where.js +27 -0
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.js +1 -0
- package/dist/usecase/adminBindings.js +143 -0
- package/dist/usecase/adminClient.js +87 -0
- package/dist/usecase/adminDescriptors.js +107 -0
- package/dist/usecase/generate.js +166 -0
- package/dist/usecase/handlers.js +21 -0
- package/dist/usecase/mcp-media.js +17 -0
- package/dist/usecase/mcp.js +100 -0
- package/dist/usecase/paging.js +71 -0
- package/dist/usecase/publishing.js +63 -0
- package/dist/usecase/refusals.js +27 -0
- package/dist/usecase/rest.js +54 -0
- package/dist/usecase/search.js +7 -0
- package/dist/usecase/site.js +39 -0
- package/dist/usecase/stores.js +64 -0
- package/dist/usecase/tool-version.js +9 -0
- package/dist/usecase/translations.js +78 -0
- package/dist/usecase/ttl.js +20 -0
- package/dist/usecase/validation.js +241 -0
- package/dist/usecase/web.js +23 -0
- package/docs/examples/README.md +39 -18
- package/docs/examples/app-shell/.dev.vars.example +4 -0
- package/docs/examples/app-shell/README.md +121 -0
- package/docs/examples/app-shell/accept.mjs +91 -0
- package/docs/examples/app-shell/gitignore +7 -0
- package/docs/examples/app-shell/package.json +43 -0
- package/docs/examples/app-shell/pnpm-workspace.yaml +8 -0
- package/docs/examples/app-shell/smoke-kit.mjs +168 -0
- package/docs/examples/app-shell/src/auth.ts +58 -0
- package/docs/examples/app-shell/src/handlers.ts +5 -0
- package/docs/examples/app-shell/src/index.ts +4 -0
- package/docs/examples/app-shell/src/routes.ts +14 -0
- package/docs/examples/app-shell/src/schedule.ts +8 -0
- package/docs/examples/app-shell/src/service.ts +70 -0
- package/docs/examples/app-shell/src/sign-in.ts +41 -0
- package/docs/examples/app-shell/src/signup.ts +8 -0
- package/docs/examples/app-shell/tsconfig.json +26 -0
- package/docs/examples/app-shell/worker-configuration.d.ts +22 -0
- package/docs/examples/app-shell/wrangler.jsonc +62 -0
- package/docs/examples/club-events/README.md +65 -0
- package/docs/examples/club-events/manifests/club-events.yaml +247 -0
- package/docs/examples/club-events/smoke.mjs +127 -0
- package/docs/examples/club-events/src/handlers.ts +25 -0
- package/docs/examples/club-events/src/signup.ts +9 -0
- package/docs/examples/generated-web/README.md +78 -0
- package/docs/examples/generated-web/posts.yaml +38 -0
- package/docs/examples/generated-web/seed.sql +6 -0
- package/docs/examples/generated-web/src/worker.ts +43 -0
- package/docs/examples/generated-web/tsconfig.json +8 -0
- package/docs/examples/generated-web/wrangler.jsonc +8 -0
- package/docs/examples/purchasing/.dev.vars.example +6 -0
- package/docs/examples/purchasing/README.md +99 -0
- package/docs/examples/purchasing/manifests/purchasing.yaml +374 -0
- package/docs/examples/purchasing/seed.sql +4 -0
- package/docs/examples/purchasing/smoke.mjs +191 -0
- package/docs/examples/purchasing/src/admin.tsx +13 -0
- package/docs/examples/purchasing/src/handlers.ts +33 -0
- package/docs/examples/purchasing/src/notify.ts +29 -0
- package/docs/examples/purchasing/src/requests-page.ts +57 -0
- package/docs/examples/purchasing/src/routes.ts +53 -0
- package/docs/examples/purchasing/src/schedule.ts +31 -0
- package/docs/examples/purchasing/src/signup.ts +5 -0
- package/docs/examples/purchasing/src/supplier.ts +32 -0
- package/docs/examples/purchasing/wrangler.jsonc +69 -0
- package/docs/examples/recipes/README.md +60 -0
- package/docs/examples/recipes/manifests/recipes.yaml +75 -0
- package/docs/examples/recipes/smoke.mjs +102 -0
- package/docs/examples/recipes/src/media.ts +13 -0
- package/docs/examples/recipes/src/pages.ts +99 -0
- package/docs/examples/recipes/src/routes.ts +71 -0
- package/docs/examples/recipes/src/site-config.ts +12 -0
- package/docs/examples/recipes/wrangler.jsonc +74 -0
- package/docs/handbook/cloudflare/authentication.md +180 -322
- package/docs/handbook/cloudflare/chatgpt-sites.md +5 -23
- package/docs/handbook/cloudflare/deploy-and-operate.md +55 -77
- package/docs/handbook/cloudflare/media-r2.md +61 -205
- package/docs/handbook/cloudflare/service-entry.md +239 -0
- package/docs/handbook/concepts/authorization.md +10 -107
- package/docs/handbook/concepts/four-atoms.md +9 -168
- package/docs/handbook/concepts/lifecycle-and-locales.md +9 -132
- package/docs/handbook/concepts/mcp-and-agents.md +24 -121
- package/docs/handbook/concepts/procedures-and-triggers.md +5 -217
- package/docs/handbook/concepts/runtime-and-adapters.md +15 -169
- package/docs/handbook/concepts/views.md +10 -185
- package/docs/handbook/examples/hub.md +3 -8
- package/docs/handbook/guides/adapter-guide.md +11 -0
- package/docs/handbook/guides/admin-ui.md +115 -133
- package/docs/handbook/guides/agent-setup.md +64 -74
- package/docs/handbook/guides/agent-workflow-acceptance.md +232 -0
- package/docs/handbook/guides/email.md +86 -0
- package/docs/handbook/guides/local-testing.md +40 -0
- package/docs/handbook/guides/mcp-apps.md +560 -0
- package/docs/handbook/guides/native-admin.md +566 -0
- package/docs/handbook/guides/native-execution.md +133 -0
- package/docs/handbook/guides/oauth.md +219 -0
- package/docs/handbook/guides/postgres-adapters.md +56 -0
- package/docs/handbook/guides/public-pages.md +87 -0
- package/docs/handbook/guides/size-and-startup.md +9 -0
- package/docs/handbook/guides/sql-patterns.md +266 -0
- package/docs/handbook/guides/typed-queries.md +211 -107
- package/docs/handbook/navigation.json +176 -55
- package/docs/handbook/reference/admin-descriptors.md +53 -0
- package/docs/handbook/reference/authorization.md +39 -266
- package/docs/handbook/reference/describe.md +56 -0
- package/docs/handbook/reference/diagnostics.md +19 -186
- package/docs/handbook/reference/features.md +45 -55
- package/docs/handbook/reference/manifest.md +6 -129
- package/docs/handbook/reference/procedure.md +11 -303
- package/docs/handbook/reference/schema.md +64 -240
- package/docs/handbook/reference/site-config.md +5 -218
- package/docs/handbook/reference/sql.md +64 -0
- package/docs/handbook/reference/surface.md +392 -201
- package/docs/handbook/reference/trigger.md +56 -192
- package/docs/handbook/reference/view.md +24 -243
- package/docs/handbook/releases/index.md +244 -77
- package/docs/handbook/start/agents.md +45 -0
- package/docs/handbook/start/overview.md +9 -48
- package/docs/handbook/start/project-and-cli.md +51 -104
- package/docs/handbook/start/quickstart-worker.md +43 -178
- package/docs/skills/develop/SKILL.md +79 -249
- package/docs/skills/integrate/SKILL.md +39 -50
- package/docs/skills/media-gc/SKILL.md +34 -10
- package/docs/skills/provision/SKILL.md +36 -186
- package/docs/skills/update/SKILL.md +23 -38
- package/docs/source/scripts/postgres-adapter.ts +101 -0
- package/docs/upgrade-0.1-to-0.2.md +227 -0
- package/package.json +120 -131
- package/skills/README.md +48 -108
- package/skills/mantle/SKILL.md +76 -0
- package/dist/admin-ui.d.ts +0 -2
- package/dist/admin-ui.d.ts.map +0 -1
- package/dist/admin-ui.js +0 -2
- package/dist/admin-ui.js.map +0 -1
- package/dist/admin.d.ts +0 -2
- package/dist/admin.d.ts.map +0 -1
- package/dist/admin.js +0 -2
- package/dist/admin.js.map +0 -1
- package/dist/auth.d.ts +0 -2
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js +0 -2
- package/dist/auth.js.map +0 -1
- package/dist/bun.d.ts +0 -2
- package/dist/bun.d.ts.map +0 -1
- package/dist/bun.js +0 -2
- package/dist/bun.js.map +0 -1
- package/dist/cli/generate-cloudflare.d.ts +0 -7
- package/dist/cli/generate-cloudflare.d.ts.map +0 -1
- package/dist/cli/generate-cloudflare.js +0 -123
- package/dist/cli/generate-cloudflare.js.map +0 -1
- package/dist/cli/generate-project.d.ts +0 -25
- package/dist/cli/generate-project.d.ts.map +0 -1
- package/dist/cli/generate-project.js +0 -282
- package/dist/cli/generate-project.js.map +0 -1
- package/dist/cli/generate-sites.d.ts +0 -8
- package/dist/cli/generate-sites.d.ts.map +0 -1
- package/dist/cli/generate-sites.js +0 -273
- package/dist/cli/generate-sites.js.map +0 -1
- package/dist/cli/generate.d.ts +0 -20
- package/dist/cli/generate.d.ts.map +0 -1
- package/dist/cli/generate.js +0 -299
- package/dist/cli/generate.js.map +0 -1
- package/dist/cli/harness.d.ts +0 -3
- package/dist/cli/harness.d.ts.map +0 -1
- package/dist/cli/harness.js +0 -214
- package/dist/cli/harness.js.map +0 -1
- package/dist/cli/main.d.ts +0 -3
- package/dist/cli/main.d.ts.map +0 -1
- package/dist/cli/main.js +0 -77
- package/dist/cli/main.js.map +0 -1
- package/dist/cli/skills.d.ts +0 -3
- package/dist/cli/skills.d.ts.map +0 -1
- package/dist/cli/skills.js +0 -128
- package/dist/cli/skills.js.map +0 -1
- package/dist/cloudflare.d.ts +0 -2
- package/dist/cloudflare.d.ts.map +0 -1
- package/dist/cloudflare.js +0 -2
- package/dist/cloudflare.js.map +0 -1
- package/dist/codegen/emitMantleModule.d.ts +0 -22
- package/dist/codegen/emitMantleModule.d.ts.map +0 -1
- package/dist/codegen/emitMantleModule.js +0 -291
- package/dist/codegen/emitMantleModule.js.map +0 -1
- package/dist/codegen.d.ts +0 -2
- package/dist/codegen.d.ts.map +0 -1
- package/dist/codegen.js +0 -2
- package/dist/codegen.js.map +0 -1
- package/dist/runtime-testing.d.ts +0 -2
- package/dist/runtime-testing.d.ts.map +0 -1
- package/dist/runtime-testing.js +0 -2
- package/dist/runtime-testing.js.map +0 -1
- package/dist/runtime.d.ts +0 -2
- package/dist/runtime.d.ts.map +0 -1
- package/dist/runtime.js +0 -2
- package/dist/runtime.js.map +0 -1
- package/dist/spec.d.ts +0 -2
- package/dist/spec.d.ts.map +0 -1
- package/dist/spec.js +0 -2
- package/dist/spec.js.map +0 -1
- package/dist/vercel-libsql.d.ts +0 -2
- package/dist/vercel-libsql.d.ts.map +0 -1
- package/dist/vercel-libsql.js +0 -2
- package/dist/vercel-libsql.js.map +0 -1
- package/dist/vercel.d.ts +0 -2
- package/dist/vercel.d.ts.map +0 -1
- package/dist/vercel.js +0 -2
- package/dist/vercel.js.map +0 -1
- package/dist/web.d.ts +0 -2
- package/dist/web.d.ts.map +0 -1
- package/dist/web.js +0 -2
- package/dist/web.js.map +0 -1
- package/docs/adapter-guide.md +0 -222
- package/docs/adr/0001-four-atom-manifest-model.md +0 -440
- package/docs/adr/0002-closed-enums-for-bindings.md +0 -253
- package/docs/adr/0007-ai-as-primary-author.md +0 -473
- package/docs/adr/0008-structured-diagnostic-shape.md +0 -338
- package/docs/adr/0009-consumer-supplied-manifests.md +0 -149
- package/docs/adr/0010-locale-and-translates.md +0 -453
- package/docs/adr/0011-adapter-port-spec.md +0 -281
- package/docs/adr/0012-views-as-public-rest.md +0 -161
- package/docs/adr/0013-agent-provisioned-consumer-projects.md +0 -154
- package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +0 -900
- package/docs/adr/0016-site-semantic-layer.md +0 -70
- package/docs/adr/0017-media-multi-variant-agent-side-optimization.md +0 -181
- package/docs/adr/0018-core-starters-repository-boundary.md +0 -179
- package/docs/adr/0019-sealed-manifest-runtime-pipeline.md +0 -240
- package/docs/adr/0020-builtin-handler-contracts-and-matched-upsert.md +0 -199
- package/docs/adr/0021-retire-starter-scaffolding.md +0 -30
- package/docs/adr/0022-caller-observed-version-occ.md +0 -212
- package/docs/adr/0023-port-failure-contract.md +0 -69
- package/docs/adr/0024-manifest-native-schema-tables.md +0 -264
- package/docs/adr/0025-reserved-native-columns-and-published-only-public-views.md +0 -105
- package/docs/adr/0026-progressive-generate.md +0 -36
- package/docs/adr/0027-scheduled-procedure-triggers.md +0 -31
- package/docs/adr/0028-schema-ttl-expiry.md +0 -33
- package/docs/adr/README.md +0 -81
- package/docs/adr/adr-lite-803-request-diagnostics.md +0 -38
- package/docs/adr/adr-lite-808-route-readiness.md +0 -47
- package/docs/adr/adr-lite-809-bounded-public-content.md +0 -71
- package/docs/adr/adr-lite-812-native-parity.md +0 -150
- package/docs/adr/adr-lite-823-home-statistics.md +0 -64
- package/docs/adr/adr-lite-842-mcp-authoring.md +0 -30
- package/docs/adr/adr-lite-845-frontend-client.md +0 -38
- package/docs/adr/adr-lite-861-admin-webmcp.md +0 -42
- package/docs/adr/adr-lite-909-admin-ui-kit.md +0 -37
- package/docs/agent-prompts.md +0 -64
- package/docs/api-mcp-authorization.md +0 -5
- package/docs/assets/mantle-admin-developer.png +0 -0
- package/docs/assets/mantle-admin-operations.png +0 -0
- package/docs/assets/mantle-hero.jpg +0 -0
- package/docs/auth-hosting-model.md +0 -166
- package/docs/cloudflare-low-level-composition.md +0 -5
- package/docs/consumer-onboarding-audit.md +0 -65
- package/docs/deferred-lifecycle-queues.md +0 -5
- package/docs/design-atoms.md +0 -16
- package/docs/design-references/editorial-blog-2026-05-05.md +0 -218
- package/docs/direct-authoring.md +0 -5
- package/docs/examples/builtin-commerce.md +0 -269
- package/docs/examples/builtin-intake.md +0 -143
- package/docs/examples/builtin-legal-documents.md +0 -189
- package/docs/examples/builtin-procurement.md +0 -241
- package/docs/examples/builtin-publication.md +0 -241
- package/docs/examples/builtin-reservation.md +0 -149
- package/docs/examples/cf-primitives-commerce-inventory.md +0 -809
- package/docs/examples/cf-primitives-guarded-api.md +0 -429
- package/docs/examples/cf-primitives-intake-hooks.md +0 -319
- package/docs/examples/host-chatgpt-sites/.openai/hosting.json +0 -1
- package/docs/examples/host-chatgpt-sites/README.md +0 -70
- package/docs/examples/host-chatgpt-sites/drizzle/0000_sites_users.sql +0 -8
- package/docs/examples/host-chatgpt-sites/drizzle/0001_mantle.sql +0 -294
- package/docs/examples/host-chatgpt-sites/drizzle/0002_article_cover.sql +0 -5
- package/docs/examples/host-chatgpt-sites/drizzle/0003_store_instance_id.sql +0 -1
- package/docs/examples/host-chatgpt-sites/drizzle/0004_mantle.sql +0 -10
- package/docs/examples/host-chatgpt-sites/drizzle/0005_mantle.sql +0 -24
- package/docs/examples/host-chatgpt-sites/drizzle/meta/_journal.json +0 -1
- package/docs/examples/host-chatgpt-sites/drizzle/meta/mantle-state.json +0 -60
- package/docs/examples/host-chatgpt-sites/manifests/site.yaml +0 -27
- package/docs/examples/host-chatgpt-sites/package.json +0 -1
- package/docs/examples/host-chatgpt-sites/public/site.css +0 -1
- package/docs/examples/host-chatgpt-sites/scripts/build.mjs +0 -13
- package/docs/examples/host-chatgpt-sites/scripts/check.mjs +0 -101
- package/docs/examples/host-chatgpt-sites/scripts/migration.mjs +0 -43
- package/docs/examples/host-chatgpt-sites/src/chatgpt-auth.ts +0 -66
- package/docs/examples/host-chatgpt-sites/src/index.ts +0 -46
- package/docs/examples/host-chatgpt-sites/src/mcp.ts +0 -49
- package/docs/examples/host-chatgpt-sites/src/media.ts +0 -109
- package/docs/examples/host-chatgpt-sites/src/r2-lab.ts +0 -38
- package/docs/examples/host-chatgpt-sites/src/storage-fingerprint.json +0 -1
- package/docs/examples/host-chatgpt-sites/src/web.ts +0 -35
- package/docs/examples/host-chatgpt-sites/tsconfig.json +0 -1
- package/docs/examples/host-chatgpt-sites/wrangler.jsonc +0 -10
- package/docs/examples/host-local-admin-otp/.dev.vars.example +0 -3
- package/docs/examples/host-local-admin-otp/README.md +0 -80
- package/docs/examples/host-local-admin-otp/ensure-dev-vars.mjs +0 -5
- package/docs/examples/host-local-admin-otp/manifests/site.yaml +0 -25
- package/docs/examples/host-local-admin-otp/package.json +0 -29
- package/docs/examples/host-local-admin-otp/public/.gitkeep +0 -1
- package/docs/examples/host-local-admin-otp/smoke.mjs +0 -141
- package/docs/examples/host-local-admin-otp/src/index.ts +0 -54
- package/docs/examples/host-local-admin-otp/tsconfig.json +0 -17
- package/docs/examples/host-local-admin-otp/wrangler.jsonc +0 -23
- package/docs/examples/host-minimal-worker/README.md +0 -41
- package/docs/examples/host-minimal-worker/manifests/site.yaml +0 -25
- package/docs/examples/host-minimal-worker/package.json +0 -26
- package/docs/examples/host-minimal-worker/smoke.mjs +0 -23
- package/docs/examples/host-minimal-worker/src/index.ts +0 -4
- package/docs/examples/host-minimal-worker/tsconfig.json +0 -17
- package/docs/examples/host-minimal-worker/wrangler.jsonc +0 -22
- package/docs/handbook/chatgpt-sites/equipment-checkout.md +0 -231
- package/docs/handbook/chatgpt-sites/generated-app.md +0 -84
- package/docs/handbook/chatgpt-sites/host-reference.md +0 -113
- package/docs/handbook/chatgpt-sites/index.md +0 -133
- package/docs/handbook/cloudflare/bindings.md +0 -291
- package/docs/handbook/cloudflare/conventional-worker.md +0 -166
- package/docs/handbook/cloudflare/deferred-hooks-queues.md +0 -170
- package/docs/handbook/cloudflare/low-level-composition.md +0 -169
- package/docs/handbook/cloudflare/public-web.md +0 -144
- package/docs/handbook/cloudflare/site-chrome.md +0 -75
- package/docs/handbook/examples/commerce-transaction.md +0 -11
- package/docs/handbook/examples/commerce.md +0 -11
- package/docs/handbook/examples/guarded-api.md +0 -10
- package/docs/handbook/examples/intake-form.md +0 -12
- package/docs/handbook/examples/intake-hooks.md +0 -11
- package/docs/handbook/examples/legal-documents.md +0 -10
- package/docs/handbook/examples/procurement-approvals.md +0 -10
- package/docs/handbook/examples/publication.md +0 -10
- package/docs/handbook/examples/reservation.md +0 -10
- package/docs/handbook/start/quickstart-admin.md +0 -52
- package/docs/labels.md +0 -71
- package/docs/media-uploads.md +0 -5
- package/docs/migration-0.1.2.md +0 -13
- package/docs/performance-harness.md +0 -228
- package/docs/release-process.md +0 -244
- package/docs/schema-indexes.md +0 -5
- package/docs/sealed-pipeline-ownership.md +0 -101
- package/docs/skills/plugin/SKILL.md +0 -132
- package/docs/skills/theme/SKILL.md +0 -77
- package/docs/spec-only-host-adoption.md +0 -157
- package/docs/transaction-patterns.md +0 -5
- package/skills/develop/SKILL.md +0 -258
- package/skills/install/SKILL.md +0 -112
- package/skills/integrate/SKILL.md +0 -60
- package/skills/media-gc/SKILL.md +0 -85
- package/skills/plugin/SKILL.md +0 -132
- package/skills/provision/SKILL.md +0 -194
- package/skills/theme/SKILL.md +0 -77
- package/skills/update/SKILL.md +0 -51
- package/templates/chatgpt-sites/auth.ts +0 -66
- package/templates/chatgpt-sites/build.mjs +0 -14
- package/templates/chatgpt-sites/env.ts +0 -1
- package/templates/chatgpt-sites/mcp.ts +0 -48
- package/templates/chatgpt-sites/public-mcp.ts +0 -29
- package/templates/chatgpt-sites/smoke-local.mjs +0 -20
|
@@ -1,194 +1,44 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: provision
|
|
3
|
-
description: Ship a Mantle project
|
|
3
|
+
description: Ship a Mantle 0.2 project to production — Cloudflare Workers with D1, production sign-in and secrets, or ChatGPT Sites through its guide — and verify the deployed service.
|
|
4
4
|
metadata:
|
|
5
5
|
source: "@aotter/mantle"
|
|
6
6
|
sourcePath: docs/skills/provision/SKILL.md
|
|
7
|
-
applies_to: mantle
|
|
7
|
+
applies_to: mantle 0.2
|
|
8
8
|
projection: package
|
|
9
|
-
projectionReason:
|
|
9
|
+
projectionReason: Creates remote resources and handles production secrets; opt-in only.
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
# Provision a Mantle
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
## Resume From Observed State
|
|
47
|
-
|
|
48
|
-
Do not branch on how the project was created. Verify these facts and skip
|
|
49
|
-
completed work:
|
|
50
|
-
|
|
51
|
-
1. `git remote get-url origin` confirms the GitHub repo.
|
|
52
|
-
2. An HTTPS `PUBLIC_ORIGIN` that responds confirms the Cloudflare deploy.
|
|
53
|
-
3. `/admin/sign-in` returning `503 setup_incomplete` means auth is not bound.
|
|
54
|
-
Use the recorded auth intent only to choose hosted or self-hosted setup;
|
|
55
|
-
live behavior is authoritative.
|
|
56
|
-
|
|
57
|
-
If there is no remote, confirm the target account, create a private repo,
|
|
58
|
-
commit, and push `main`. If there is no live Worker, confirm the Cloudflare
|
|
59
|
-
account, prefer an available connector, or use `pnpm exec wrangler login` with
|
|
60
|
-
the user's agreement, then run `pnpm deploy`.
|
|
61
|
-
|
|
62
|
-
Capture the live URL in `PUBLIC_ORIGIN` and `Public site:` in `AGENTS.md`, then
|
|
63
|
-
commit and push non-secret changes. Reuse any repo or Worker already created.
|
|
64
|
-
Do not recreate Landing artifacts for a new direct-authored app. Workers Builds
|
|
65
|
-
is optional after a direct deploy.
|
|
66
|
-
|
|
67
|
-
When the owner later adopts a custom domain, update `PUBLIC_ORIGIN` and the
|
|
68
|
-
provider's OAuth callback together, then redeploy. Do not patch `site_config`
|
|
69
|
-
directly; boot syncs its canonical origin from `PUBLIC_ORIGIN`.
|
|
70
|
-
|
|
71
|
-
## Choose Auth
|
|
72
|
-
|
|
73
|
-
- **Self-hosted email OTP:** use the application's production transactional-email sender. Replace `ConsoleEmailSender`; never deploy it.
|
|
74
|
-
- **Self-hosted GitHub OAuth — free fallback:** use when the application has no email provider. Configure the owner's per-site GitHub OAuth App and Worker secrets using the steps below.
|
|
75
|
-
- **Mantle hosted auth — paid, legacy/optional:** use only when a **legacy
|
|
76
|
-
Landing handoff** already records a hosted allocation and client
|
|
77
|
-
configuration. New direct-authored apps do not get this from Core. Mantle
|
|
78
|
-
Platform operates the identity provider; do not ask the user for a per-site
|
|
79
|
-
GitHub OAuth App.
|
|
80
|
-
|
|
81
|
-
Configure only the selected mode. Core deliberately rejects partial or mixed
|
|
82
|
-
hosted/self-managed bindings with `503 setup_incomplete`.
|
|
83
|
-
|
|
84
|
-
Do not claim that hosted auth can attach to an arbitrary local repo unless a
|
|
85
|
-
legacy Landing handoff already supplies that configuration.
|
|
86
|
-
|
|
87
|
-
For the exact boundary, read
|
|
88
|
-
`node_modules/@aotter/mantle/docs/auth-hosting-model.md`.
|
|
89
|
-
|
|
90
|
-
## Self-hosted email OTP
|
|
91
|
-
|
|
92
|
-
Keep the application's custom `createAuth()` factory, replace
|
|
93
|
-
`ConsoleEmailSender` with its production `EmailSender`, and retain
|
|
94
|
-
`bootstrapOwner: { match: "email", value: <owner email> }`. Store sender
|
|
95
|
-
credentials and `BETTER_AUTH_SECRET` as Worker secrets, put `PUBLIC_ORIGIN` in
|
|
96
|
-
non-secret vars, deploy, then verify that the owner receives an OTP at
|
|
97
|
-
`/admin/sign-in`. If there is no production email provider, use GitHub OAuth
|
|
98
|
-
below instead of deploying console delivery.
|
|
99
|
-
|
|
100
|
-
## Self-hosted GitHub OAuth
|
|
101
|
-
|
|
102
|
-
1. Ask the user to create a GitHub OAuth App:
|
|
103
|
-
|
|
104
|
-
- Homepage URL: `<worker-url>`
|
|
105
|
-
- Authorization callback URL: `<worker-url>/api/auth/callback/github`
|
|
106
|
-
- Device Flow: unchecked
|
|
107
|
-
|
|
108
|
-
2. Put non-secret values in `wrangler.toml`:
|
|
109
|
-
|
|
110
|
-
- `MANTLE_AUTH_MODE = "self-managed"`
|
|
111
|
-
- `PUBLIC_ORIGIN`
|
|
112
|
-
- `GITHUB_CLIENT_ID`
|
|
113
|
-
- `ADMIN_GITHUB_LOGIN`
|
|
114
|
-
- correct Worker `name`
|
|
115
|
-
|
|
116
|
-
Remove `MANTLE_HOSTED_AUTH_ISSUER` and `MANTLE_HOSTED_AUTH_CLIENT_ID` if they
|
|
117
|
-
were present for a hosted allocation.
|
|
118
|
-
|
|
119
|
-
3. Keep the Client Secret out of chat. Prefer a Cloudflare connector for
|
|
120
|
-
secrets; otherwise use hidden shell input:
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
read -rsp "GitHub OAuth client secret: " MANTLE_GITHUB_CLIENT_SECRET && printf "\n"
|
|
124
|
-
printf '%s' "$MANTLE_GITHUB_CLIENT_SECRET" | pnpm exec wrangler secret put GITHUB_CLIENT_SECRET
|
|
125
|
-
openssl rand -hex 32 | pnpm exec wrangler secret put BETTER_AUTH_SECRET
|
|
126
|
-
unset MANTLE_GITHUB_CLIENT_SECRET
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
Set `BETTER_AUTH_SECRET` once and preserve it. Rotating it invalidates existing
|
|
130
|
-
sessions.
|
|
131
|
-
|
|
132
|
-
4. Commit and push only non-secret config, then redeploy:
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
git add wrangler.toml AGENTS.md
|
|
136
|
-
git commit -m "mantle: wire production auth"
|
|
137
|
-
git push
|
|
138
|
-
pnpm deploy
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
## Hosted Auth (legacy Landing)
|
|
142
|
-
|
|
143
|
-
Skip this section unless a legacy Landing handoff is already present. New
|
|
144
|
-
direct-authored apps use self-hosted email OTP or GitHub OAuth above.
|
|
145
|
-
|
|
146
|
-
Follow that handoff and its client configuration. Hosted configuration remains
|
|
147
|
-
in landing-managed Cloudflare Worker bindings. Verify:
|
|
148
|
-
|
|
149
|
-
- `MANTLE_AUTH_MODE = "hosted"`;
|
|
150
|
-
- `MANTLE_HOSTED_AUTH_ISSUER` is the HTTPS root issuer;
|
|
151
|
-
- `MANTLE_HOSTED_AUTH_CLIENT_ID` is the same-origin `/clients/<id>` URL;
|
|
152
|
-
- `PUBLIC_ORIGIN` and `ADMIN_GITHUB_LOGIN` are set;
|
|
153
|
-
- `BETTER_AUTH_SECRET` exists as a Worker secret;
|
|
154
|
-
- `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` are absent.
|
|
155
|
-
|
|
156
|
-
Hosted clients use PKCE and have no client secret. Do not write secrets into
|
|
157
|
-
`wrangler.toml`.
|
|
158
|
-
|
|
159
|
-
Verify that admin sign-in redirects to Mantle Hosted Auth and Staff MCP
|
|
160
|
-
authenticates, then skip the self-hosted flow.
|
|
161
|
-
|
|
162
|
-
## Smoke Test
|
|
163
|
-
|
|
164
|
-
- public home route;
|
|
165
|
-
- `/admin/sign-in`;
|
|
166
|
-
- selected admin sign-in path;
|
|
167
|
-
- `/mcp/staff` with an agent client when available;
|
|
168
|
-
- one type-specific core workflow.
|
|
169
|
-
|
|
170
|
-
Media uploads are optional. Configure R2 only when the owner asks for
|
|
171
|
-
staff-managed files; then read
|
|
172
|
-
`node_modules/@aotter/mantle/docs/handbook/cloudflare/media-r2.md`.
|
|
173
|
-
|
|
174
|
-
## Handoff
|
|
175
|
-
|
|
176
|
-
Return:
|
|
177
|
-
|
|
178
|
-
- public URL;
|
|
179
|
-
- admin sign-in URL;
|
|
180
|
-
- Staff MCP URL;
|
|
181
|
-
- operator setup URL:
|
|
182
|
-
`https://mantle.tools/connect?site=<url-encoded-worker-url>`;
|
|
183
|
-
- remote resources created or reused;
|
|
184
|
-
- auth mode and any intentionally deferred setup.
|
|
185
|
-
|
|
186
|
-
## Don't
|
|
187
|
-
|
|
188
|
-
- Don't create remote resources before the user asks to ship.
|
|
189
|
-
- Don't ask for a Cloudflare API token in the base flow.
|
|
190
|
-
- Don't commit provider secrets.
|
|
191
|
-
- Don't require R2 for first production.
|
|
192
|
-
- Don't invent a second provision orchestrator.
|
|
193
|
-
- Don't use `/admin/auth/github/callback`; the callback is
|
|
194
|
-
`/api/auth/callback/github`.
|
|
12
|
+
# Provision a Mantle project
|
|
13
|
+
|
|
14
|
+
## Workflow
|
|
15
|
+
|
|
16
|
+
Read the installed SDK README, then follow the version-matched [project and CLI
|
|
17
|
+
workflow](../../handbook/start/project-and-cli.md) before editing.
|
|
18
|
+
|
|
19
|
+
Deploy/create resources only within the user's authorization; reuse configured
|
|
20
|
+
host resources. Apply reviewed and rehearsed migrations before routing traffic;
|
|
21
|
+
imports/first requests perform no DDL or schema checks. Keep auth dynamic, production
|
|
22
|
+
secrets in the host store, trusted HTTPS origin and verified bootstrap rule,
|
|
23
|
+
production email and selected media wiring. Configure Admin ASSETS worker-first
|
|
24
|
+
as in [Workers assets and secrets](../../handbook/cloudflare/service-entry.md#workers-assets-and-secrets). Hyperdrive caching stays off; PG settings are deployment-owned.
|
|
25
|
+
Keep the Workers [public-fetch flags](../../handbook/cloudflare/deploy-and-operate.md#public-fetch-routing),
|
|
26
|
+
including environment overrides; verify deployed MCP with a real OAuth bearer
|
|
27
|
+
(`initialize` and `tools/list`), not only a browser session cookie.
|
|
28
|
+
For MCP Apps, also follow [deployed host acceptance](../../handbook/guides/mcp-apps.md#verify-a-deployed-app-in-chatgpt):
|
|
29
|
+
compare the current resource URI and visibly verify a newly invoked render tool.
|
|
30
|
+
Archive deployed Admin pages using the guide's `previous` recipe and carry them
|
|
31
|
+
through clean builds. Test the same old conversation after changing the UI build,
|
|
32
|
+
without reconnecting, refreshing host tools or clearing caches. A deliberate tool
|
|
33
|
+
contract change has its own migration/host rediscovery step. OAuth renewal, `tools/call` success and a
|
|
34
|
+
local official-host replay do not establish that the deployed App rendered.
|
|
35
|
+
Bun uses native Database/SQL, an app server and scheduler. Verify admin sign-in
|
|
36
|
+
(role admin), denied access, public reads, a persisted write, MCP and selected UI.
|
|
37
|
+
Report artifact/resources/checks and keep rollback artifacts.
|
|
38
|
+
|
|
39
|
+
For ChatGPT Sites, follow [Mantle on ChatGPT Sites](../../handbook/cloudflare/chatgpt-sites.md).
|
|
40
|
+
|
|
41
|
+
The app builds Admin with `mantle bundle`; serve it as [Native
|
|
42
|
+
Admin](../../handbook/guides/native-admin.md) describes. Verify both member and
|
|
43
|
+
admin OTP/UI flows. Deployment of an authorized application does not authorize
|
|
44
|
+
SDK publication; a workspace preview remains a preview.
|
|
@@ -1,51 +1,36 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: update
|
|
3
|
-
description:
|
|
3
|
+
description: Upgrade a Mantle project's @aotter/mantle packages to a new exact version, including the move from 0.1.x to 0.2, without overwriting application-owned code.
|
|
4
4
|
metadata:
|
|
5
5
|
source: "@aotter/mantle"
|
|
6
6
|
sourcePath: docs/skills/update/SKILL.md
|
|
7
|
-
applies_to: mantle
|
|
8
|
-
projection:
|
|
7
|
+
applies_to: mantle 0.2
|
|
8
|
+
projection: package
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# Mantle Update
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
The old Starter bundle comparison command `mantle update` is removed. This
|
|
15
|
-
skill remains the version-matched upgrade workflow, not a replacement CLI.
|
|
13
|
+
## Workflow
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
and legacy `.mantle` metadata if present; they are context, not required.
|
|
20
|
-
2. Select an explicit target release and read that version's GitHub release
|
|
21
|
-
notes. Installed docs live under `node_modules/@aotter/mantle/docs/`;
|
|
22
|
-
`handbook/releases/index.md` is a stable-release index, so a prerelease
|
|
23
|
-
need not appear there. Do not use the old package's docs as the new API contract.
|
|
24
|
-
Do not resolve new Starter refs or compare the project to a baseline
|
|
25
|
-
template.
|
|
26
|
-
3. Update only selected `@aotter/mantle*` dependencies to the same exact target
|
|
27
|
-
version, preserving dependency sections. Use the package manager to update
|
|
28
|
-
the lockfile; inspect the dependency diff and required peer changes.
|
|
29
|
-
4. Remove scripts that invoke retired create/bundle-update commands. Keep all
|
|
30
|
-
application manifests, handlers, routes, theme, Worker/D1/KV names, origins,
|
|
31
|
-
provider bindings, secrets, and legacy metadata. Apply API migration edits
|
|
32
|
-
individually; do not copy the reference consumer over a real application.
|
|
33
|
-
5. Use the upgraded package to regenerate machine-owned bindings and project
|
|
34
|
-
its skills, then run the application's validation, types and tests:
|
|
15
|
+
Read the installed SDK README, then follow the version-matched [project and CLI
|
|
16
|
+
workflow](../../handbook/start/project-and-cli.md) before editing.
|
|
35
17
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
pnpm exec mantle skills --check
|
|
41
|
-
pnpm exec mantle validate
|
|
42
|
-
```
|
|
18
|
+
For 0.1.x, follow [Upgrade 0.1 to 0.2](../../upgrade-0.1-to-0.2.md): give the
|
|
19
|
+
0.2 service a new database and move data by export and import; never boot 0.2
|
|
20
|
+
against a 0.1.x database. Between 0.2 versions, apply reviewed migrations
|
|
21
|
+
rehearsed on a copy.
|
|
43
22
|
|
|
44
|
-
|
|
45
|
-
|
|
23
|
+
Pin exact target SDK/peer versions and read its release notes and Admin build
|
|
24
|
+
instructions. Core-to-native replaces app imports/wiring; it is not
|
|
25
|
+
a package-only update. For 0.2-to-0.2 updates, preserve users, sessions, content
|
|
26
|
+
and application code; review emitted SQL/policy diffs and rehearse explicit
|
|
27
|
+
data-preserving schema/auth/role migrations on a copy. Initial schema.sql never
|
|
28
|
+
authorizes rebuilding production tables. Verify auth/Admin/MCP/REST, locks and
|
|
29
|
+
persistence before authorized cutover.
|
|
46
30
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
31
|
+
Follow [Customising Admin](../../handbook/guides/native-admin.md#customising-admin)
|
|
32
|
+
for the current application-built Admin; old UI subpaths have no compatibility
|
|
33
|
+
aliases. Pin only actually published versions, or explicitly use a matching
|
|
34
|
+
workspace preview. Recheck member and Admin OTP, all affected native routes,
|
|
35
|
+
private field visibility, OCC and unknown outcomes. Do not publish a package as
|
|
36
|
+
part of an application dependency update.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/** Reusable application-owned adapter check. The factory must open an isolated, disposable database/schema. */
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdir, mkdtemp, rm } from "node:fs/promises";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { unstable_splitSqlQuery } from "wrangler";
|
|
7
|
+
import type { Postgres } from "../src/kernel/contracts.js";
|
|
8
|
+
import { procedure, transaction } from "../src/kernel/driver/postgres.js";
|
|
9
|
+
import { generateOrThrow, writeGenerated } from "../tests/apps/link.js";
|
|
10
|
+
|
|
11
|
+
export type PostgresAdapterFactory = () => Promise<{ db: Postgres; close(): Promise<void>; interactive?: boolean }>;
|
|
12
|
+
|
|
13
|
+
const atom = (kind: string, name: string, spec: object) => JSON.stringify({ apiVersion: "cms.mantle.aotter.net/v2", kind, metadata: { name }, spec });
|
|
14
|
+
const properties = { key: { type: "string" }, at: { type: "string", format: "date-time" }, day: { type: "string", format: "date" }, payload: { type: "object" }, units: { type: "integer" }, enabled: { type: "boolean" }, amount: { type: "number" } };
|
|
15
|
+
const source = [
|
|
16
|
+
atom("Schema", "adapter_items", { title: "Adapter conformance", schema: { type: "object", properties, required: Object.keys(properties) }, uniqueIndexes: [["key"]] }),
|
|
17
|
+
atom("View", "adapter-items", { surface: "internal", sql: "SELECT id, key, at, day, payload, units, enabled, amount, created_at, CAST(at AS timestamptz) AS computed_at FROM adapter_items ORDER BY id" }),
|
|
18
|
+
atom("View", "adapter-wire", { surface: "public", sql: `SELECT units AS "quantityAlias", enabled, amount, version AS "versionAlias" FROM adapter_items ORDER BY id` }),
|
|
19
|
+
atom("Procedure", "adapter-create", { input: { type: "object", properties, required: Object.keys(properties) }, output: { type: "object" }, handler: { sql: "INSERT INTO adapter_items(key, at, day, payload, units, enabled, amount) VALUES(input.key, input.at, input.day, input.payload, input.units, input.enabled, input.amount) RETURNING id, at, day, payload, units, enabled, amount" } }),
|
|
20
|
+
].join("\n---\n");
|
|
21
|
+
|
|
22
|
+
/** Returns only a check count; assertions retain the exact failing contract. No latency or benchmark data. */
|
|
23
|
+
export async function verifyPostgresAdapter(factory: PostgresAdapterFactory): Promise<{ checks: number }> {
|
|
24
|
+
const files = await generateOrThrow("bun-postgres", [{ sourceId: "adapter-conformance.yaml", text: source }]);
|
|
25
|
+
await mkdir(resolve(import.meta.dir, "../tests/apps/.generated"), { recursive: true });
|
|
26
|
+
const directory = await mkdtemp(resolve(import.meta.dir, "../tests/apps/.generated/adapter-"));
|
|
27
|
+
let client: Awaited<ReturnType<PostgresAdapterFactory>> | undefined;
|
|
28
|
+
let checks = 0;
|
|
29
|
+
try {
|
|
30
|
+
await writeGenerated(directory, files);
|
|
31
|
+
const app = await import(pathToFileURL(resolve(directory, "generated.ts")).href);
|
|
32
|
+
client = await factory();
|
|
33
|
+
const { db, interactive = true } = client;
|
|
34
|
+
for (const sql of unstable_splitSqlQuery(files["schema.sql"]!)) await db.unsafe(sql, []);
|
|
35
|
+
const caller = { kind: "system", reason: "adapter conformance" } as const;
|
|
36
|
+
const input = { key: "first", at: "2026-10-12T08:00:00.123456+08:00", day: "2024-02-29", payload: JSON.stringify({ text: "'\\", nested: [false, null] }), units: Number.MAX_SAFE_INTEGER, enabled: true, amount: 0.1 };
|
|
37
|
+
const output = await app.procedures["adapter-create"](db, caller, input);
|
|
38
|
+
const created = output.results[0][0];
|
|
39
|
+
assert.equal(created.at, "2026-10-12T00:00:00.123456Z", "declared RETURNING date-time retains microseconds");
|
|
40
|
+
assert.equal(created.day, input.day, "declared RETURNING date retains calendar day");
|
|
41
|
+
assert.deepEqual(created.payload, JSON.parse(input.payload), "JSON text is bound verbatim, not JSON-encoded twice");
|
|
42
|
+
assert.equal(String(created.units), String(input.units), "int8 retains a safe integer exactly");
|
|
43
|
+
assert.equal(created.enabled, true, "PostgreSQL boolean native output"); assert.equal(created.amount, input.amount, "declared number native output"); checks += 6;
|
|
44
|
+
const rows = await app.views["adapter-items"](db, caller, {});
|
|
45
|
+
const count = rows.count;
|
|
46
|
+
assert.ok(Array.isArray(rows), "unsafe returns a row array");
|
|
47
|
+
assert.equal(count, 1, "unsafe array carries affected count");
|
|
48
|
+
assert.equal(rows[0].at, created.at, "declared SELECT date-time retains microseconds");
|
|
49
|
+
assert.equal(rows[0].day, input.day);
|
|
50
|
+
assert.match(rows[0].created_at, /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{6}Z$/, "system timestamps have the same wire form");
|
|
51
|
+
// Computed outputs are native: the driver may return Date (milliseconds) or text (microseconds).
|
|
52
|
+
assert.ok(rows[0].computed_at instanceof Date || typeof rows[0].computed_at === "string"); checks += 6;
|
|
53
|
+
await assert.rejects(() => app.procedures["adapter-create"](db, caller, input), { code: "CONFLICT" }); checks++;
|
|
54
|
+
const step = { sql: "UPDATE adapter_items SET key = $1 WHERE id = $2 RETURNING id", binds: ["changed", "missing"], row: true };
|
|
55
|
+
await assert.rejects(() => procedure(db, [step], "adapter/zero"), { code: "CONFLICT" }); checks++;
|
|
56
|
+
const integer = await db.unsafe("SELECT CAST($1 AS bigint) AS positive, CAST($2 AS bigint) AS negative, CAST($3 AS numeric(15,2)) AS decimal", ["9007199254740993", "-9007199254740993", "1234567890123.45"]);
|
|
57
|
+
assert.equal(String(integer[0].positive), "9007199254740993", "unsafe int8 must not be rounded by an adapter");
|
|
58
|
+
assert.equal(String(integer[0].negative), "-9007199254740993");
|
|
59
|
+
assert.equal(integer[0].decimal, "1234567890123.45", "numeric text retains declared scale and precision"); checks += 3;
|
|
60
|
+
const { default: page } = await import(pathToFileURL(resolve(directory, "paged/wire/views/adapter-wire.ts")).href);
|
|
61
|
+
for (const value of ["9007199254740991", "9007199254740993", "-9007199254740993", "9223372036854775807", "-9223372036854775808"]) {
|
|
62
|
+
await db.unsafe("UPDATE adapter_items SET units=CAST($1 AS bigint), version=CAST($1 AS bigint)", [value]);
|
|
63
|
+
const wire = (await page(db, caller, {})).rows[0];
|
|
64
|
+
// The selected-boundary module returns exact integer text; REST/MCP turn safe values into numbers.
|
|
65
|
+
assert.equal(String(wire.quantityAlias), value); assert.equal(String(wire.versionAlias), value);
|
|
66
|
+
assert.equal(wire.enabled, true); assert.equal(wire.amount, input.amount); checks += 4;
|
|
67
|
+
}
|
|
68
|
+
await db.unsafe("UPDATE adapter_items SET version=1", []);
|
|
69
|
+
const store = app.store(db, caller), collection = store.collection("adapter_items");
|
|
70
|
+
const changed = { sql: "UPDATE adapter_items SET key = $1 WHERE id = $2 RETURNING id", binds: ["transaction", created.id], row: true };
|
|
71
|
+
if (!interactive) {
|
|
72
|
+
let calls = 0;
|
|
73
|
+
const tapped = { unsafe: (sql: string, values: any[]) => { calls++; return db.unsafe(sql, values); }, begin: db.begin.bind(db) };
|
|
74
|
+
const limitedStore = app.store(tapped, caller);
|
|
75
|
+
for (const run of [() => procedure(tapped, [changed, changed], "adapter/multi"), () => limitedStore.transaction(async () => { throw Error("callback must not run"); }), () => limitedStore.batch([limitedStore.collection("adapter_items").update(created.id, { key: "no-write" }, { lock: 1 }), limitedStore.collection("adapter_items").update(created.id, { key: "also-no-write" }, { lock: 1 })])]) {
|
|
76
|
+
await assert.rejects(run, (error: unknown) => {
|
|
77
|
+
const e = error as Error & { code?: string; status?: number };
|
|
78
|
+
return e instanceof Error && e.name === "ContractError" && e.code === "TRANSACTION_UNSUPPORTED" && e.status === 400 && /transaction/i.test(e.message);
|
|
79
|
+
});
|
|
80
|
+
assert.equal(calls, 0, "unsupported interactive transaction refuses before any statement"); checks += 2;
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
await transaction(db, async tx => {
|
|
84
|
+
await procedure(tx, [changed, changed], "adapter/nested-handle");
|
|
85
|
+
assert.equal((await tx.unsafe("SELECT key FROM adapter_items WHERE id = $1", [created.id]))[0].key, "transaction");
|
|
86
|
+
await assert.rejects(() => transaction(tx, async () => {}), /Nested transactions/);
|
|
87
|
+
}); checks += 2;
|
|
88
|
+
assert.equal((await db.unsafe("SELECT key FROM adapter_items WHERE id = $1", [created.id]))[0].key, "transaction", "begin commits after callback"); checks++;
|
|
89
|
+
const rollback = new Error("rollback sentinel");
|
|
90
|
+
await assert.rejects(() => transaction(db, async tx => { await tx.unsafe("UPDATE adapter_items SET key = $1 WHERE id = $2", ["rolled-back", created.id]); throw rollback; }), error => error === rollback);
|
|
91
|
+
assert.equal((await db.unsafe("SELECT key FROM adapter_items WHERE id = $1", [created.id]))[0].key, "transaction", "begin rolls back callback failure"); checks += 2;
|
|
92
|
+
await store.batch([collection.update(created.id, { key: "batch" }, { lock: 1 })]); checks++;
|
|
93
|
+
}
|
|
94
|
+
const uncertain = { unsafe: db.unsafe.bind(db), begin: async <T>(_options: string, callback: (tx: Postgres) => Promise<T>) => { await callback(db); throw new Error("socket lost at COMMIT"); } };
|
|
95
|
+
await assert.rejects(() => transaction(uncertain, async () => {}), { code: "OUTCOME_UNKNOWN" }); checks++;
|
|
96
|
+
return { checks };
|
|
97
|
+
} finally {
|
|
98
|
+
try { if (client) await client.close(); }
|
|
99
|
+
finally { await rm(directory, { recursive: true, force: true }); }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Upgrading from 0.1.x to 0.2.0 (for AI agents)
|
|
2
|
+
|
|
3
|
+
For **0.2.0-alpha.7 and later**, use the
|
|
4
|
+
[native SDK migration](handbook/releases/index.md#native-sdk-migration-alpha7),
|
|
5
|
+
[current CLI](handbook/start/project-and-cli.md) and
|
|
6
|
+
[app shell](examples/app-shell/). The application owns routing,
|
|
7
|
+
Auth, native handles and database migrations; the former `createMantle` runtime,
|
|
8
|
+
host presets, boot-time convergence and CLI flags below have been replaced.
|
|
9
|
+
Install the same exact version of `@aotter/mantle`, `@aotter/mantle-ui` and,
|
|
10
|
+
when selecting Admin, `@aotter/mantle-admin`. Start with a new database for a
|
|
11
|
+
0.1.x migration and preserve every authorization, scope and OCC requirement.
|
|
12
|
+
The remainder describes the earlier alpha contract for historical reference.
|
|
13
|
+
|
|
14
|
+
No codemod, and no in-place upgrade of a 0.1.x database. A coding agent moves
|
|
15
|
+
one project by hand with this guide. When it and
|
|
16
|
+
[ADR-0032](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0032-store-centric-core-and-service-contract.md),
|
|
17
|
+
[ADR-0033](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0033-storage-converges-to-the-plan.md),
|
|
18
|
+
[ADR-0034](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0034-store-is-authored-as-sql.md),
|
|
19
|
+
[ADR-0035](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0035-sql-dialects.md),
|
|
20
|
+
[ADR-0036](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0036-host-and-dialect.md) or
|
|
21
|
+
[ADR-0038](https://github.com/aotter/mantle/blob/v0.2.0-alpha.7/docs/adr/0038-bun-native-host.md) disagree, the ADR wins.
|
|
22
|
+
|
|
23
|
+
## Rules
|
|
24
|
+
|
|
25
|
+
- Work on a branch. Keep the 0.1.x project running until the 0.2.0 one passes
|
|
26
|
+
its checks.
|
|
27
|
+
- Move meaning, not text. If a manifest does something no row below covers,
|
|
28
|
+
stop and ask. Vercel projects have no 0.2.0 preset: stop and ask. A Bun
|
|
29
|
+
project moves to `host: bun` (section 2).
|
|
30
|
+
- Never loosen access. Every 0.1.x `requires`, guard, `scope` and
|
|
31
|
+
`x-mantle-bind` must come out at least as strict. `x-mantle-bind` is
|
|
32
|
+
refused in 0.2.0 and stamps nothing, so each one becomes `scope` or an
|
|
33
|
+
explicit value in the write (section 3).
|
|
34
|
+
- Give the 0.2.0 service a new database; never boot it against the 0.1.x
|
|
35
|
+
one. Move data by export and import (section 5).
|
|
36
|
+
- `mantle generate --check` is the gate. Never hand-edit `.mantle/generated/`.
|
|
37
|
+
|
|
38
|
+
## 1. Packages
|
|
39
|
+
|
|
40
|
+
| 0.1.x | 0.2.0 |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `@aotter/mantle` and `@aotter/mantle-spec`, `-runtime`, `-cloudflare`, `-auth`, `-admin`, `-web`, `-bun`, `-vercel`, `-indexeddb` | `@aotter/mantle` (subpaths `/spec`, `/d1`, `/postgres`, `/cloudflare`, `/bun`, `/auth`, `/admin`, `/mcp`, `/web`, `/testing`) |
|
|
43
|
+
| `@aotter/mantle-admin-ui` | `@aotter/mantle-ui` at the same version as `@aotter/mantle`: the preset serves the console at `/admin` from its `dist/admin` files; nothing to import. Its `/kit` re-export is gone: import `@aotter/mantle-ui/kit` |
|
|
44
|
+
|
|
45
|
+
Remove every old package and install `@aotter/mantle` at the exact target
|
|
46
|
+
version. `mantle generate` names any other package it needs, with the install
|
|
47
|
+
command; it never installs anything.
|
|
48
|
+
|
|
49
|
+
## 2. `mantle.config.json`
|
|
50
|
+
|
|
51
|
+
0.1.4 had none; 0.1.5-alpha.1 wrote a `version: 1` one. Write version 2:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{ "version": 2, "identity": "mantle", "features": ["mcp", "admin", "web"] }
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- `version: 1` is refused.
|
|
58
|
+
- `host` is `cloudflare` (the default, left out), `bun` or `none`; `none`
|
|
59
|
+
writes the plan and types only, for a platform with no preset. `host: bun`
|
|
60
|
+
refuses an enabled schedule Trigger: Bun has no cron dispatcher in the
|
|
61
|
+
preset.
|
|
62
|
+
- `identity`: `mantle` if the project used Mantle auth (Better Auth sign-in,
|
|
63
|
+
Admin users, OAuth for MCP), `custom` if it resolved callers itself, `none`
|
|
64
|
+
if it had no signed-in callers. `admin` needs an identity.
|
|
65
|
+
- `features` is a subset of `mcp`, `admin`, `web`, in that order. The 0.1.x
|
|
66
|
+
features `spec`, `runtime` and `api` are dropped: REST is always mounted.
|
|
67
|
+
- `dialect` is `sqlite` (alias `d1`, the default, left out) or `postgres`
|
|
68
|
+
(PostgreSQL through Hyperdrive on `cloudflare`; node-postgres on `bun`, where it
|
|
69
|
+
is the default). A 0.1.x project was SQLite: keep `sqlite` (with
|
|
70
|
+
`--host bun`, pass `--dialect sqlite`) unless the data also moves to
|
|
71
|
+
PostgreSQL.
|
|
72
|
+
- `host` and `dialect` are chosen on the first `mantle generate` (`--host`,
|
|
73
|
+
`--dialect`); a rerun that asks for another is refused.
|
|
74
|
+
|
|
75
|
+
## 3. Manifests
|
|
76
|
+
|
|
77
|
+
Set `apiVersion: cms.mantle.aotter.net/v2` on every document; a v1 document
|
|
78
|
+
is refused.
|
|
79
|
+
|
|
80
|
+
**Native columns are snake_case in SQL**: `id`, `status`, `version`,
|
|
81
|
+
`created_at`, `updated_at`, `author_id`. (`ctx.store` JSON keeps the
|
|
82
|
+
camelCase names.)
|
|
83
|
+
|
|
84
|
+
### Schema
|
|
85
|
+
|
|
86
|
+
| 0.1.x | 0.2.0 |
|
|
87
|
+
|---|---|
|
|
88
|
+
| `scope: { owner: $ctx.user.id }` | `scope: "owner = auth.uid()"`. The field is a required string, the leftmost column of an index (`indexes: [[owner]]`), and the first column of every `uniqueIndexes` entry. Store fills it; a write never sets it |
|
|
89
|
+
| `x-mantle-bind: ctx.user` on a field only its owner may see | remove the bind; add `scope` as above |
|
|
90
|
+
| any other `x-mantle-bind: ctx.user` | remove the bind; every write that sets the field sets it to `auth.uid()`, never to an input |
|
|
91
|
+
| `x-mantle-bind: ctx.staff` | remove the bind; set it to `auth.uid()` in writes whose Procedure requires `ctx.admin` |
|
|
92
|
+
| `x-mantle-bind: now` | remove the bind; set it to `now()` in every write that sets it |
|
|
93
|
+
| `searchableFields` | unchanged, now trigram full-text search; date and date-time fields are refused |
|
|
94
|
+
|
|
95
|
+
`checks` (boolean SQL over the row's own columns) is new and optional.
|
|
96
|
+
|
|
97
|
+
A JSON Schema `pattern` (in a Schema, a View input or a Procedure input/output)
|
|
98
|
+
is now refused with `INVALID_PATTERN` when it could backtrack without bound on
|
|
99
|
+
a caller's string:
|
|
100
|
+
|
|
101
|
+
- a repeated group whose body repeats or alternates, such as the 0.1.x slug
|
|
102
|
+
`^[a-z0-9]+(-[a-z0-9]+)*$` or `(a|b)+`, and any backreference;
|
|
103
|
+
- a pattern whose backtracking, branches × maxLength^e, could exceed
|
|
104
|
+
10,000,000 — e is its variable quantifiers (`*`, `+`, `?`, `{m,}`,
|
|
105
|
+
`{m,n}`) plus 1 unless it starts with `^`, branches the product of its
|
|
106
|
+
alternations' alternatives — or one with e > 0 whose string has no
|
|
107
|
+
`maxLength`: `^[a-z0-9-]+$` (e = 1) allows any maxLength up to
|
|
108
|
+
10,000,000, an email `^[^@]+@[^@]+$` (e = 2) up to 3162, the unanchored
|
|
109
|
+
`[^@]+@` (e = 2) also 3162, `^a*a*a*…$` almost none.
|
|
110
|
+
|
|
111
|
+
Rewrite a slug as `{ type: string, maxLength: 120, pattern: "^[a-z0-9-]+$" }`,
|
|
112
|
+
which also accepts leading, trailing and doubled hyphens. On a Schema field
|
|
113
|
+
that must not have them, add a `checks` entry:
|
|
114
|
+
`"slug NOT LIKE '-%' AND slug NOT LIKE '%-' AND slug NOT LIKE '%--%'"`.
|
|
115
|
+
|
|
116
|
+
### View
|
|
117
|
+
|
|
118
|
+
A View is one SQL `SELECT` in `spec.sql`, with `input` for its parameters.
|
|
119
|
+
The SQL is PostgreSQL syntax: it names declared Schemas (never
|
|
120
|
+
schema-qualified), `input.<name>`, `auth.uid()`, `auth.role()`, `now()` and
|
|
121
|
+
`mantle.*` functions. The runtime adds scope, TTL and published-only to every
|
|
122
|
+
Schema reference; do not repeat them.
|
|
123
|
+
|
|
124
|
+
| 0.1.x | 0.2.0 |
|
|
125
|
+
|---|---|
|
|
126
|
+
| `from: posts`, `fields: [id, title]` | `SELECT id, title FROM posts` |
|
|
127
|
+
| `filter: { eq: { field: status, value: published } }`, with `and` / `or` | `WHERE status = 'published'`, with `AND` / `OR` |
|
|
128
|
+
| `value: { "$ctx.user": id }` | `auth.uid()`, or `scope` on the Schema |
|
|
129
|
+
| `params` and `{ "$param": q }` | `input: { type: object, properties: { q: … } }` and `input.q` |
|
|
130
|
+
| `orderBy: [{ field: createdAt, direction: desc }]` | `ORDER BY created_at DESC` |
|
|
131
|
+
| `limit: 20` | `LIMIT 20` (a `LIMIT` needs an `ORDER BY`) |
|
|
132
|
+
| `select:` (`columns`, `where`, `$input.x`, `$ctx.user.id`, `$now`) | the same query as SQL: `input.x`, `auth.uid()`, `now()` |
|
|
133
|
+
| `sql:` (SQLite, `:name` parameters) | the same `sql:`, `:name` → `input.name`, rewritten to PostgreSQL syntax where it differs |
|
|
134
|
+
| free-text search over `searchableFields` | `WHERE mantle.search(p, input.q) ORDER BY mantle.search_rank(p)`; an unqualified `search` is refused |
|
|
135
|
+
| `page` / `show` on the wire | `limit` / `cursor`, `nextCursor` in the response |
|
|
136
|
+
|
|
137
|
+
### Procedure
|
|
138
|
+
|
|
139
|
+
0.1.x builtin ops are `create`, `update`, `upsert`, `delete` and `archive`.
|
|
140
|
+
|
|
141
|
+
| 0.1.x | 0.2.0 |
|
|
142
|
+
|---|---|
|
|
143
|
+
| builtin `create` | `handler: { sql: "INSERT INTO s (a, b) VALUES (input.a, input.b) RETURNING id" }` (on a `publishing` Schema the row starts as a draft, as before) |
|
|
144
|
+
| builtin `update` (always version-checked, merges over the stored row) | `UPDATE s SET a = COALESCE(input.a, a), … WHERE id = input.id AND version = input.expectedVersion`. Keep both: without the version the lock is lost; without `COALESCE` an omitted optional field is cleared |
|
|
145
|
+
| builtin `upsert` with `match` | `INSERT … ON CONFLICT (<match columns>) DO UPDATE SET x = EXCLUDED.x`; the conflict columns are a unique index; on a scoped Schema the scope field may be named or left out, Store adds it |
|
|
146
|
+
| builtin `delete` | `DELETE FROM s WHERE id = input.id AND version = input.expectedVersion` |
|
|
147
|
+
| builtin `archive` | a `ref` handler: `ctx.store.write([{ update: "s", set: { status: "archived" }, where: { id }, lock: expectedVersion }])` (SQL cannot set `status`) |
|
|
148
|
+
| `handler: { kind: ref, ref: fn }` | `handler: { ref: fn }`; the function's signature changes (section 4) |
|
|
149
|
+
| `target: { schema, id, version }` | optional: inferred from `WHERE id = input.… AND version = input.…` |
|
|
150
|
+
|
|
151
|
+
A write never sets the scope field, `version`, `status`, `author_id` or the
|
|
152
|
+
timestamps. On a scoped Schema an `INSERT` names no `id`. Several statements
|
|
153
|
+
in one `sql` apply together or not at all. `requires.auth` and
|
|
154
|
+
`requires.guard` are unchanged.
|
|
155
|
+
|
|
156
|
+
### Trigger
|
|
157
|
+
|
|
158
|
+
- `schedule` cron is POSIX: weekday 0 = Sunday. 0.1.x used Cloudflare's
|
|
159
|
+
numbering (1 = Sunday), so subtract 1 from every weekday number, in ranges
|
|
160
|
+
and lists too (`1` → `0`, `2-6` → `1-5`, `7` → `6`); `*` and `*/n` stay.
|
|
161
|
+
On host `cloudflare`, a cron that sets both a day of month and a weekday
|
|
162
|
+
is refused.
|
|
163
|
+
- `lifecycle` `errorPolicy` is removed: a before hook fails closed (a 0.1.x
|
|
164
|
+
`continue` now blocks the write), an after hook is best effort.
|
|
165
|
+
- `http`, `mcp` and other `lifecycle` fields keep their shape.
|
|
166
|
+
|
|
167
|
+
## 4. Handler code
|
|
168
|
+
|
|
169
|
+
A `ref` handler is `(input, ctx) => output` in the service's `handlers` map.
|
|
170
|
+
The generated types list exactly the plan's refs.
|
|
171
|
+
|
|
172
|
+
| 0.1.x | 0.2.0 |
|
|
173
|
+
|---|---|
|
|
174
|
+
| `ctx.user` present | `ctx.caller.kind === "user"`. Never test `!== "anonymous"`: a schedule runs as `system` |
|
|
175
|
+
| `ctx.user.id` | `ctx.caller.subject` (after that check) |
|
|
176
|
+
| `ctx.staff` | `isAdmin(ctx.caller)` from `@aotter/mantle/contracts` |
|
|
177
|
+
| `ctx.event`, `ctx.schedule` | `ctx.cause` (`kind`: `http`, `mcp`, `internal`, `schedule`, `lifecycle`); `ctx.cause.cron` is POSIX, so shift any cron string the code compares |
|
|
178
|
+
| `ctx.event.entry` | **a loop** over `ctx.cause.rows` (one statement can touch many rows; never `rows[0]`). Rows are flat: `entry.data.x` → `row.x` |
|
|
179
|
+
| `ctx.store.view(name, { params, page, show, search, filters })` | `ctx.store.view(name, { input, limit, cursor, search, filters })`; `search` and `filters` match only the View's `uiSchema.list.searchFields` and `filterFields` |
|
|
180
|
+
| `runtime.entries`, `runtime.executeView`, `ctx.writeAtomically` | `ctx.db.<schema>.get / first / find`, `ctx.store.view`, `ctx.store.write([...ops])` (all or nothing), `ctx.store.id()` |
|
|
181
|
+
| `runtime.store.as(ctx)` | `runtime.store.as(caller)` |
|
|
182
|
+
| calling another Procedure through a `getRuntime` closure | `await ctx.invoke("procedure-name", input)` (keeps the caller, re-checks its auth) |
|
|
183
|
+
|
|
184
|
+
`ctx.store` is scoped to the caller. `runtime.store` is not scoped; use it
|
|
185
|
+
only in trusted host code.
|
|
186
|
+
|
|
187
|
+
## 5. The Worker and the data
|
|
188
|
+
|
|
189
|
+
1. Move the old `src/`, `wrangler.jsonc` (or `.toml`) and `tsconfig.json`
|
|
190
|
+
aside. `mantle generate` writes the preset (`src/*.ts`, `tsconfig.json`,
|
|
191
|
+
and `wrangler.jsonc` on `cloudflare` or `.env.example` on `bun`) only
|
|
192
|
+
where no such file exists, and only while `src/service.ts` is missing.
|
|
193
|
+
2. Run `mantle generate`, then move custom routes, bindings and env from the
|
|
194
|
+
old Worker into the new files by hand.
|
|
195
|
+
3. With `identity: mantle`, sign-in uses a console email sender locally;
|
|
196
|
+
replace it with a real `EmailSender` before deploying.
|
|
197
|
+
4. Admin MCP moves from `/mcp/staff` to `/mcp/admin`. Update MCP client URLs.
|
|
198
|
+
It admits only the admin layer classified by Better Auth’s admin plugin
|
|
199
|
+
`adminRoles` (default `["admin"]`), and requires the `mcp` scope for tokens.
|
|
200
|
+
5. Import data into the new database through `runtime.store`. A scoped row
|
|
201
|
+
goes through `runtime.store.as(<its owner's caller>)`, so the scope field is
|
|
202
|
+
the owner. `status`, `created_at` and `author_id` cannot be imported:
|
|
203
|
+
publish with a second update that sets `status`, and accept new
|
|
204
|
+
timestamps and author.
|
|
205
|
+
|
|
206
|
+
## 6. CLI
|
|
207
|
+
|
|
208
|
+
| 0.1.x | 0.2.0 |
|
|
209
|
+
|---|---|
|
|
210
|
+
| `mantle validate`, `mantle emit-openapi` | `mantle generate --check` (fails if anything is stale; `--database <file>` also prints the storage SQL) |
|
|
211
|
+
| `mantle generate --host …` (0.1.5-alpha.1) | `mantle generate --host cloudflare\|bun\|none` on the first run only |
|
|
212
|
+
| `mantle generate --output`, `--namespace` | removed: output is always `.mantle/generated/` |
|
|
213
|
+
| `mantle skills`, the `mantle-harness` bin | removed |
|
|
214
|
+
|
|
215
|
+
## 7. Verify
|
|
216
|
+
|
|
217
|
+
1. `mantle generate` and `mantle generate --check` exit 0.
|
|
218
|
+
2. The project typechecks against the generated `mantle.ts`.
|
|
219
|
+
3. Each 0.1.x View and Procedure returns the same rows for the same caller,
|
|
220
|
+
including a caller who must see nothing (scope) and an anonymous request.
|
|
221
|
+
4. A write cannot set an owner, author or timestamp field from its input.
|
|
222
|
+
5. Every schedule fires on the intended weekday.
|
|
223
|
+
6. `wrangler dev` (or `bun src/index.ts`) starts, sign-in works if
|
|
224
|
+
`identity` is `mantle`, and Admin, MCP and REST answer on their paths.
|
|
225
|
+
|
|
226
|
+
Report what changed per manifest, every place where access got stricter, and
|
|
227
|
+
anything you could not map.
|