@fabrika/builder 0.2.0

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 (112) hide show
  1. package/BUILD-CONTRACT.md +41 -0
  2. package/LICENSE +21 -0
  3. package/README.md +100 -0
  4. package/cli/src/bin.ts +66 -0
  5. package/cli/src/build-status.ts +109 -0
  6. package/cli/src/catalog.ts +42 -0
  7. package/cli/src/commands/advance.ts +73 -0
  8. package/cli/src/commands/build.ts +215 -0
  9. package/cli/src/commands/ensure-daemon.ts +25 -0
  10. package/cli/src/commands/gate.ts +165 -0
  11. package/cli/src/commands/induce.ts +61 -0
  12. package/cli/src/commands/init.ts +91 -0
  13. package/cli/src/commands/next.ts +31 -0
  14. package/cli/src/config.ts +47 -0
  15. package/cli/src/context.ts +28 -0
  16. package/cli/src/daemon-client.ts +280 -0
  17. package/cli/src/daemon-lifecycle.ts +171 -0
  18. package/cli/src/derive.ts +109 -0
  19. package/cli/src/index.ts +38 -0
  20. package/cli/src/induce.ts +151 -0
  21. package/cli/src/inject-skills.ts +313 -0
  22. package/cli/src/issues.ts +75 -0
  23. package/cli/src/layout.ts +124 -0
  24. package/cli/src/packet.ts +108 -0
  25. package/cli/src/phase-state.ts +19 -0
  26. package/cli/src/phases.ts +131 -0
  27. package/cli/src/skeletons.ts +104 -0
  28. package/daemon/src/bin.ts +70 -0
  29. package/daemon/src/build-stream.ts +38 -0
  30. package/daemon/src/handlers.ts +472 -0
  31. package/daemon/src/index.ts +123 -0
  32. package/daemon/src/paths.ts +30 -0
  33. package/daemon/src/phases.ts +221 -0
  34. package/daemon/src/static.ts +72 -0
  35. package/decisions/README.md +133 -0
  36. package/decisions/patterns/app-deploy.md +55 -0
  37. package/decisions/patterns/auth-password-kv.md +45 -0
  38. package/decisions/patterns/count-store.md +44 -0
  39. package/decisions/patterns/di-container.md +47 -0
  40. package/decisions/patterns/feature-architecture.md +48 -0
  41. package/decisions/patterns/lib-publish.md +46 -0
  42. package/decisions/patterns/monorepo.md +47 -0
  43. package/decisions/patterns/named-seeds.md +45 -0
  44. package/decisions/patterns/observability.md +45 -0
  45. package/decisions/patterns/single-worker-spa.md +74 -0
  46. package/decisions/patterns/structured-logging.md +49 -0
  47. package/decisions/patterns/typed-worker-rpc.md +87 -0
  48. package/decisions/patterns/vozka-app-deploy.md +74 -0
  49. package/decisions/patterns/write-split.md +47 -0
  50. package/decisions/technologies/analytics-engine.md +44 -0
  51. package/decisions/technologies/bindx.md +55 -0
  52. package/decisions/technologies/bun.md +51 -0
  53. package/decisions/technologies/buzola.md +80 -0
  54. package/decisions/technologies/cloudflare-access.md +50 -0
  55. package/decisions/technologies/cloudflare-vite-plugin.md +43 -0
  56. package/decisions/technologies/cloudflare.md +50 -0
  57. package/decisions/technologies/contember.md +65 -0
  58. package/decisions/technologies/cron-triggers.md +46 -0
  59. package/decisions/technologies/d1.md +51 -0
  60. package/decisions/technologies/durable-objects.md +44 -0
  61. package/decisions/technologies/kv.md +42 -0
  62. package/decisions/technologies/lopata.md +55 -0
  63. package/decisions/technologies/nuasite.md +50 -0
  64. package/decisions/technologies/oblaka.md +48 -0
  65. package/decisions/technologies/opice.md +40 -0
  66. package/decisions/technologies/pletivo.md +53 -0
  67. package/decisions/technologies/postgres.md +47 -0
  68. package/decisions/technologies/preact.md +44 -0
  69. package/decisions/technologies/propustka.md +137 -0
  70. package/decisions/technologies/queue.md +55 -0
  71. package/decisions/technologies/r2.md +41 -0
  72. package/decisions/technologies/react-router.md +47 -0
  73. package/decisions/technologies/react.md +37 -0
  74. package/decisions/technologies/roj.md +51 -0
  75. package/decisions/technologies/sentry.md +49 -0
  76. package/decisions/technologies/tailwind.md +45 -0
  77. package/decisions/technologies/vite.md +47 -0
  78. package/decisions/technologies/vozka.md +122 -0
  79. package/decisions/technologies/workers-ai.md +59 -0
  80. package/decisions/technologies/wrangler.md +41 -0
  81. package/decisions/tooling/deptective.md +45 -0
  82. package/decisions/tooling/edvabe.md +45 -0
  83. package/index.ts +2 -0
  84. package/package.json +51 -0
  85. package/skill/SKILL.md +76 -0
  86. package/skill/agents/build-orchestrator.md +170 -0
  87. package/skill/agents/flows-author.md +65 -0
  88. package/skill/agents/implementer.md +37 -0
  89. package/skill/agents/openai.yaml +4 -0
  90. package/skill/agents/opice-skeleton-author.md +68 -0
  91. package/skill/gates/brief.jsx +20 -0
  92. package/skill/gates/build.jsx +20 -0
  93. package/skill/gates/design.jsx +22 -0
  94. package/skill/gates/flows.jsx +14 -0
  95. package/skill/gates/review-grid.jsx +32 -0
  96. package/skill/gates/tech.jsx +17 -0
  97. package/skill/gates/vision.jsx +27 -0
  98. package/stacks/README.md +44 -0
  99. package/stacks/cf-fat.md +64 -0
  100. package/stacks/cf-internal-app.md +125 -0
  101. package/stacks/cf-lightweight.md +54 -0
  102. package/stacks/contember-app.md +53 -0
  103. package/stacks/content-site.md +45 -0
  104. package/ui/dist/assets/annotations-runtime.js +1 -0
  105. package/ui/dist/assets/client.css +2 -0
  106. package/ui/dist/assets/client.fhjppkvr.js +214 -0
  107. package/ui/dist/assets/components.js +63 -0
  108. package/ui/dist/assets/preact-compat.js +1 -0
  109. package/ui/dist/assets/react-jsx-dev-runtime.js +3 -0
  110. package/ui/dist/assets/react-jsx-runtime.js +3 -0
  111. package/ui/dist/assets/runtime.js +1 -0
  112. package/ui/dist/index.html +36 -0
@@ -0,0 +1,51 @@
1
+ ---
2
+ id: bun
3
+ kind: technology
4
+ title: Bun (JS runtime + toolchain) — PREFERRED
5
+ slot: js-runtime
6
+ provides: [js-runtime, package-manager, test-runner, bundler, native-bundling]
7
+ requires: []
8
+ recommends: [lopata]
9
+ conflicts: []
10
+ npm: []
11
+ status: stable
12
+ sources:
13
+ - where: "bun.sh/docs + bun (cloned source for internals)"
14
+ read: "The runtime/bundler/test-runner surface; `bun build` and bun plugins for the non-vite path."
15
+ - where: "Our repos that run on Bun: lopata, opice (`bun test`), pletivo"
16
+ read: "How our toolchain assumes Bun end to end."
17
+ ---
18
+
19
+ # Bun
20
+
21
+ > **Team preference.** We prefer **Bun + lopata + a non-vite stack**. Pick Bun as the
22
+ > runtime/toolchain and Bun-native bundling (e.g. `@buzola/bun-plugin`, pletivo's
23
+ > Bun build) wherever a decision offers it; reach for `vite` only when a chosen lib
24
+ > still mandates it (e.g. React Router v7).
25
+
26
+ ## What it is
27
+
28
+ The JS runtime, package manager, test runner, and bundler our stack standardizes on.
29
+ Most of our libs assume Bun: `lopata` runs on it, `opice` runs under `bun test`,
30
+ `pletivo` builds with it.
31
+
32
+ ## When & why to use
33
+
34
+ - Default for every fabrika app — one fast toolchain (install, run, test, build).
35
+ - Bun-native bundling avoids a separate vite pipeline (the **non-vite** preference).
36
+
37
+ ## When NOT to use
38
+
39
+ - A dependency genuinely needs Node-only APIs Bun doesn't implement, or a lib hard-requires vite (then `vite` is a scoped exception, not the default).
40
+
41
+ ## Impacts & gotchas
42
+
43
+ - `bun test` is the test runner (opice rides on it); `bunx` runs CLIs (oblaka, lopata, opice).
44
+ - Bun-native bundling differs from vite plugins — when a lib offers both (e.g. buzola), prefer the **bun-plugin**.
45
+ - Some Node libs assume Node built-ins; verify under Bun before depending on them.
46
+
47
+ ## Relationships — why
48
+
49
+ - **slot `js-runtime`** — the runtime choice (vs Node).
50
+ - **recommends `lopata`** — the preferred local CF runtime, also Bun-based.
51
+ - Required/assumed by `pletivo`; `opice` runs under it.
@@ -0,0 +1,80 @@
1
+ ---
2
+ id: buzola
3
+ kind: technology
4
+ title: Buzola — type-safe SPA router for React (Navigation API)
5
+ slot: router
6
+ provides: [router, spa, file-based-routing, type-safe-routes, loaders]
7
+ requires: [react]
8
+ recommends: [bun]
9
+ conflicts: [react-router]
10
+ npm: ["@buzola/router@^0.0.12", "@buzola/vite-plugin@^0.0.12", "@buzola/bun-plugin@^0.0.12", "@buzola/codegen@^0.0.12"]
11
+ status: wip
12
+ sources:
13
+ - where: "buzola/packages/buzola/src/define/create-page.tsx + src/react/{hooks.ts,link.tsx}"
14
+ read: "The createPage() builder + the React surface (useNavigate/useParams/useRoute, Link, Outlet, BuzolaProvider)."
15
+ - where: "buzola/packages/{vite-plugin,bun-plugin,codegen}/src/"
16
+ read: "How codegen emits src/buzola.gen.ts + the virtual:buzola/routes module, wired via EITHER the vite-plugin OR the bun-plugin (the non-vite path)."
17
+ - where: "propustka/packages/admin-ui/src/{buzola.gen.ts,routes/principals/detail.tsx,routes/_layout.tsx}"
18
+ read: "A real app: codegen output, chained loaders + error boundary + type-safe Link, layout nesting."
19
+ ---
20
+
21
+ # Buzola
22
+
23
+ ## What it is
24
+
25
+ A **type-safe SPA router for React** built over the browser **Navigation API**, with
26
+ **file-based routing** and **type codegen**. Routes are defined with a `createPage()`
27
+ builder; codegen emits `src/buzola.gen.ts` + a `virtual:buzola/routes` module and
28
+ augments a `BuzolaPageMap` so `navigate(pageId, params)` and `<Link to params>` are
29
+ fully typechecked. It is suitable for Contember-style admin SPAs, including Propustka's public admin UI.
30
+ Ships **both** a `@buzola/vite-plugin` and a **`@buzola/bun-plugin`** — the bun-plugin
31
+ is the **non-vite path** that fits our bun preference.
32
+
33
+ ## When & why to use
34
+
35
+ - A client-rendered **SPA / admin panel** (auth-gated dashboards) where SSR isn't needed.
36
+ - You want **type-safe page ids + params** with zero manual route registration.
37
+ - Our **bun / non-vite** preference: drive codegen with `@buzola/bun-plugin` when
38
+ the app has no other reason for Vite. Use the Vite plugin for a client-rendered
39
+ two-process SPA/Worker stack.
40
+
41
+ ## When NOT to use
42
+
43
+ - You need **SSR / data in loaders on the Worker** → `react-router`.
44
+ - You must support **Safari/Firefox or old browsers** — buzola **requires the Navigation API** (Chrome/Edge/Opera only); it throws at `BuzolaProvider` mount otherwise.
45
+
46
+ ## Routing API (real shape)
47
+
48
+ ```ts
49
+ export default createPage()
50
+ .params({ id: 'string' }) // typed params (or a Standard Schema)
51
+ .loader(async ({ params, redirect }) => { // chainable; results merge, first redirect wins
52
+ const user = await fetchUser(params.id)
53
+ if (!user) return redirect('login', {})
54
+ return { user }
55
+ })
56
+ .route('/principals/:id') // URL pattern — comes ONLY from .route(), not the filename
57
+ .render(({ params, data, invalidate, isLoading }) => (
58
+ <Detail user={data.user} />
59
+ ))
60
+ ```
61
+
62
+ Routes live in `src/routes/` (`_layout.tsx`, `_404.tsx`, `index.tsx`, `(group)/` are
63
+ special); the **page id** is the file path (`principals/detail.tsx` → `'principals/detail'`).
64
+
65
+ ## Load-bearing gotchas (anti-staleness)
66
+
67
+ - **URL comes only from `.route()`**, never the filename — agents assuming Next-style file routing will get 404s. Check `buzola.gen.ts`.
68
+ - **`.render()` is required** — without it `createPage()` is incomplete and codegen drops the page.
69
+ - **Navigation API is mandatory** — no SSR, no old browsers; polyfill or use a memory adapter in tests.
70
+ - **Codegen needs a real module loader** — the vite-plugin/bun-plugin passes one (`ssrLoadModule` under vite); a bare Node `import()` silently loses routes.
71
+ - **Single `<Outlet/>` per layout** — no React-Router-style parallel/named outlets.
72
+
73
+ ## Best practices (testability)
74
+
75
+ - Semantic markup + accessible names (`<Link>Dashboard</Link>`, `<label htmlFor>`, `role="alert"`) so opice selects by role/text, not brittle classes.
76
+
77
+ ## Relationships — why
78
+
79
+ - **requires `react`** (it's a React router). Bundle via `@buzola/bun-plugin` (preferred, non-vite) or `@buzola/vite-plugin`; hence **recommends `bun`** rather than hard-requiring `vite`.
80
+ - **conflicts `react-router`** — same `router` slot; SPA (buzola) vs SSR (RRv7). One per app.
@@ -0,0 +1,50 @@
1
+ ---
2
+ id: cloudflare-access
3
+ kind: technology
4
+ title: Cloudflare Access (legacy/alternate edge authentication)
5
+ slot: authentication
6
+ provides: [authentication, sso]
7
+ requires: [cloudflare]
8
+ recommends: []
9
+ conflicts: [auth-password-kv, propustka]
10
+ npm: ["jose@^5"]
11
+ status: stable
12
+ sources:
13
+ - where: "developers.cloudflare.com/cloudflare-one/policies/access (Access JWT, application audience, identity)"
14
+ read: "How Access fronts a Worker, the JWT it injects, and how to validate it (aud/iss, JWKS)."
15
+ - where: "A legacy app that still uses Access JWT validation"
16
+ read: "A live example of validating the Access JWT and resolving identity downstream; do not copy it into native Propustka apps."
17
+ ---
18
+
19
+ # Cloudflare Access (authentication)
20
+
21
+ ## What it is
22
+
23
+ Cloudflare's zero-trust **authentication** layer sitting in front of a Worker. It
24
+ handles "who you are" (SSO / IdP / one-time PIN), injects a signed JWT, and the
25
+ Worker validates it (via `jose` — `aud`, `iss`, JWKS). It owns _authentication
26
+ only_. This is now the legacy/alternate front door for apps that have **not**
27
+ adopted native Propustka sessions.
28
+
29
+ ## When & why to use
30
+
31
+ - Self-hosted / internal tools where you want SSO without building login yourself.
32
+ - You intentionally keep an older Access-fronted app and are not migrating it to
33
+ native Propustka sessions yet.
34
+
35
+ ## When NOT to use
36
+
37
+ - Public sign-up product (consumers can't be behind Access) → use `auth-password-kv` or an IdP-direct flow.
38
+ - You need app-owned credential lifecycle (password reset, invites) as a product feature.
39
+
40
+ ## Impacts
41
+
42
+ - Production traffic must pass through Access; **local dev needs a dev-only
43
+ identity shim**.
44
+ - The Worker trusts the Access JWT — validate `aud`/`iss` against your application audience + team JWKS, never skip it.
45
+
46
+ ## Relationships — why
47
+
48
+ - **conflicts `propustka`** — current Propustka owns native SSO sessions and
49
+ in-process path gates; it no longer expects a Cloudflare Access edge.
50
+ - **conflicts `auth-password-kv`** — both fill the `authentication` slot. Access = managed SSO; auth-password-kv = app-owned password+session. Pick one.
@@ -0,0 +1,43 @@
1
+ ---
2
+ id: cloudflare-vite-plugin
3
+ kind: technology
4
+ title: "@cloudflare/vite-plugin (real-CF dev via workerd)"
5
+ slot: cf-dev-runtime
6
+ provides: [cf-dev-server, workerd-runtime, local-bindings]
7
+ requires: [cloudflare, vite, wrangler]
8
+ recommends: [oblaka]
9
+ conflicts: [lopata]
10
+ npm: ["@cloudflare/vite-plugin@^1", "wrangler@^4"]
11
+ status: stable
12
+ sources:
13
+ - where: "developers.cloudflare.com/workers/vite-plugin + installed @cloudflare/vite-plugin types"
14
+ read: "Plugin options and how it runs the Worker under workerd in dev, reading wrangler.jsonc bindings."
15
+ ---
16
+
17
+ # @cloudflare/vite-plugin
18
+
19
+ ## What it is
20
+
21
+ Cloudflare's official vite plugin: runs the Worker in dev under **real workerd**
22
+ (miniflare), emulating D1/KV/R2/Queue/Cron locally from `wrangler.jsonc`. The
23
+ real-CF dev path (vs lopata's Bun-based emulation).
24
+
25
+ ## When & why to use
26
+
27
+ - You use more/fresher CF primitives than lopata emulates (Queue, AE-adjacent, etc.) and want production-fidelity local behavior.
28
+ - The cf-fat path.
29
+
30
+ ## When NOT to use
31
+
32
+ - You want the lightest local-first loop with the lopata dashboard and don't need workerd fidelity → use `lopata`.
33
+
34
+ ## Impacts & gotchas
35
+
36
+ - `vite.config.ts`: `@cloudflare/vite-plugin` + the RRv7 plugin, SSR environment configured for Workers. Verify options against installed versions.
37
+ - Reads bindings from `wrangler.jsonc` (generated by `oblaka`); local D1 via `wrangler d1 … --local`.
38
+ - **`analytics-engine` is still not emulated** even here — counts go through `count-store`.
39
+
40
+ ## Relationships — why
41
+
42
+ - **requires `cloudflare` + `vite` + `wrangler`** (workerd/miniflare + CLI).
43
+ - **conflicts `lopata`** — same `cf-dev-runtime` slot; pick one dev runtime (often env-toggled with `CF=1`). **`lopata` is our preferred default**; use this plugin when you specifically need workerd fidelity (e.g. Queue/Cron) lopata doesn't emulate yet.
@@ -0,0 +1,50 @@
1
+ ---
2
+ id: cloudflare
3
+ kind: technology
4
+ title: Cloudflare Workers (platform)
5
+ slot: platform
6
+ provides: [platform, edge-compute, serverless]
7
+ requires: []
8
+ recommends: [d1, durable-objects, kv, r2, queue, oblaka, wrangler]
9
+ conflicts: []
10
+ npm: ["@cloudflare/workers-types@^4", "wrangler@^4"]
11
+ status: stable
12
+ sources:
13
+ - where: "developers.cloudflare.com/workers (fetch/queue/scheduled handlers, bindings, compatibility flags)"
14
+ read: "The Worker handler shapes (fetch/queue/scheduled), how bindings reach the handler via env, and compatibility flags (nodejs_compat)."
15
+ - where: "oblaka and lopata public examples"
16
+ read: "Resource definitions, local bindings, and multi-worker composition."
17
+ ---
18
+
19
+ # Cloudflare Workers
20
+
21
+ ## What it is
22
+
23
+ The serverless edge-compute platform fabrika's generated apps target: V8-isolate
24
+ Workers with first-party bindings to storage and messaging primitives
25
+ (`d1`, `kv`, `r2`, `queue`, `analytics-engine`, `durable-objects`) and
26
+ `cron-triggers` and Workers AI. Bindings arrive on `env`; a Worker exports
27
+ `fetch`, optionally `queue(batch, env)` and `scheduled(event, env)`.
28
+
29
+ ## When & why to use
30
+
31
+ - The default platform for fabrika apps — SSR React + a database in one deployable unit, no cold-start servers.
32
+ - You want managed primitives (SQLite/KV/object-store/queue) without operating infrastructure.
33
+
34
+ ## When NOT to use
35
+
36
+ - You need a long-lived process, raw TCP, or a runtime workerd/lopata can't emulate.
37
+ - Hard requirement on a non-CF managed service with no equivalent binding.
38
+
39
+ ## Impacts
40
+
41
+ - App is a Worker: code runs in a V8 isolate (web APIs, `nodejs_compat` flag for selected Node built-ins) — **not** full Node.
42
+ - Bindings are defined in an oblaka resource graph, directly in `oblaka.ts` or
43
+ through `vozka.config.ts` for a Vozka-managed app; never hand-edit the generated
44
+ `wrangler.jsonc`.
45
+ - Local dev emulates D1/KV/R2/Queue/Cron (`lopata` or `wrangler dev`/workerd). **`analytics-engine` has no local runtime** (see `count-store`).
46
+
47
+ ## Recommendations — why
48
+
49
+ - **d1 / durable-objects / kv / r2 / queue** — the storage/messaging menu; pick per workload (relational state → `d1`, coordination/strong-consistency → `durable-objects`, hot keys/sessions → `kv`, blobs → `r2`, buffering bursts → `queue`).
50
+ - **oblaka** — define bindings as code; **wrangler** — CLI for d1/dev/deploy.
@@ -0,0 +1,65 @@
1
+ ---
2
+ id: contember
3
+ kind: technology
4
+ title: Contember — GraphQL CMS / backend framework
5
+ slot: backend
6
+ provides: [graphql-api, content-model, acl, admin-ui, migrations, tenant-auth, multi-tenant]
7
+ requires: [postgres]
8
+ recommends: [react, bindx]
9
+ conflicts: []
10
+ npm: ["@contember/schema-definition", "@contember/react-binding", "@contember/interface", "@contember/client-content", "@contember/cli"]
11
+ status: stable
12
+ sources:
13
+ - where: "contember-oss — packages/schema-definition (c.* DSL + @c.Allow ACL), CLAUDE.md, docker-compose.yaml"
14
+ read: "The schema-as-code DSL (entities/relations/validation), predicate-based ACL, the engine architecture (Content/System/Tenant API), and local Postgres bootstrap."
15
+ - where: "@contember/* on npm (installed types) + packages/client-content/tests/schema.ts"
16
+ read: "The package surface an app imports (schema-definition, react-binding, client-content, cli) + a real sample schema."
17
+ - where: "contember-oss/packages/playground/"
18
+ read: "A minimal working app: schema + CLI config + admin wiring."
19
+ ---
20
+
21
+ # Contember
22
+
23
+ ## What it is
24
+
25
+ A **batteries-included GraphQL CMS/backend framework**. You declare the data model,
26
+ ACL, and validation as TypeScript decorators (`@contember/schema-definition`); the
27
+ engine auto-generates a GraphQL Content API (with row/field-level ACL enforced in
28
+ SQL), Postgres migrations (by diffing schema versions), a React admin UI, and a
29
+ tenant/identity system (sign-up, projects, memberships, roles, API keys). It is a
30
+ monolithic Node service speaking GraphQL over HTTP against Postgres — not a library
31
+ you compose, a stack you adopt.
32
+
33
+ ## When & why to use
34
+
35
+ - You need a **working admin UI** + schema-as-code + Postgres migrations out of the box.
36
+ - **Row/field-level ACL** matters (author-only rows, role-hidden fields) — predicates baked into the query layer.
37
+ - **Multi-tenant + member roles** (per-project teams, invites) — the tenant API gives it free.
38
+ - You can run a **standalone Node backend** with a managed Postgres.
39
+
40
+ ## When NOT to use
41
+
42
+ - You're committed to **Cloudflare Workers + D1** — Contember's engine is a Node service on Postgres; it does **not** run on Workers (this is the big architectural fork vs the cf-* stacks).
43
+ - You need a headless-only API with no React/admin → roll a thinner GraphQL layer yourself, or use `kvalt`.
44
+ - Bespoke SQL / aggregations the generated GraphQL fights.
45
+
46
+ ## Impacts (what adopting it forces)
47
+
48
+ - **Postgres** as the relational store (so `d1` is out — see the `relational-db` slot). You run/manage Postgres.
49
+ - A **Node backend service** separate from any Workers SSR frontend — the deployment shape differs from cf-lightweight/cf-fat.
50
+ - **Schema is code**; migrations are diff-generated TypeScript committed under `.contember/migrations/`. Regenerate the snapshot whenever you add one or fresh DBs drift.
51
+ - A **projects/memberships** model (tenancy) even for single-tenant apps.
52
+ - The admin UI + data binding are **React 19** (see `bindx` / `@contember/react-binding`).
53
+
54
+ ## Gotchas
55
+
56
+ - **ACL is per-field predicates**, declared on the entity (`@c.Allow(role, { when, read, … })`) and injected into SQL — not per-resolver. Complex scoping needs entity variables.
57
+ - **Tenant API owns identity** — custom auth (SAML, external JWT, machine principals) means bridging it, not swapping it.
58
+ - **No built-in realtime** — admin uses HTTP GraphQL; live updates need your own WS/polling.
59
+ - Migrations are diff-generated; hand-editing one (e.g. a data backfill) drops you into the migration format's internals.
60
+
61
+ ## Relationships — why
62
+
63
+ - **requires `postgres`** — the entire schema/migration/ACL story is Postgres-specific (transitively excludes `d1`, same `relational-db` slot).
64
+ - **recommends `react` + `bindx`** — the admin UI and data binding are React; `bindx`/`@contember/react-binding` is the typed binding layer over its GraphQL API.
65
+ - **slot `backend`** — the backend choice (one backend per app). It also subsumes auth/identity, so it overlaps `auth-password-kv`/`propustka` conceptually — if Contember owns tenancy, don't double up.
@@ -0,0 +1,46 @@
1
+ ---
2
+ id: cron-triggers
3
+ kind: technology
4
+ title: Cloudflare Cron Triggers (scheduled jobs)
5
+ slot: null
6
+ provides: [scheduled-jobs, cron]
7
+ requires: [cloudflare]
8
+ recommends: [oblaka]
9
+ conflicts: []
10
+ npm: []
11
+ status: stable
12
+ sources:
13
+ - where: "developers.cloudflare.com/workers/configuration/cron-triggers (scheduled handler, triggers.crons)"
14
+ read: "The scheduled(event, env) handler and triggers.crons config in wrangler."
15
+ ---
16
+
17
+ # Cloudflare Cron Triggers
18
+
19
+ ## What it is
20
+
21
+ Scheduled invocation of a Worker on a cron expression. The Worker exports
22
+ `scheduled(event, env)`; the schedule is `triggers.crons` in `wrangler.jsonc`. For
23
+ periodic work — spike/anomaly detection, digests, cleanup.
24
+
25
+ ## When & why to use
26
+
27
+ - Periodic background work with no incoming request (alert spike detection, rollups, GC of expired sessions).
28
+
29
+ ## When NOT to use
30
+
31
+ - Sub-minute or event-driven timing → use `queue` (event-driven) or a `durable-objects` alarm (precise per-object timer).
32
+
33
+ ## Impacts & gotchas
34
+
35
+ - Export a **`scheduled(event, env)`** handler alongside `fetch`/`queue`. Emulated locally by workerd/lopata; trigger manually in dev.
36
+ - Schedule lives in `triggers.crons` — declare it via `oblaka.ts`, never hand-edit the generated `wrangler.jsonc`.
37
+ - Put the real operation in a reusable function and call it from both
38
+ `scheduled()` and a protected manual/internal endpoint used by local tests.
39
+ Do not make the scheduled handler call its own public HTTP endpoint.
40
+ - When several schedules share one Worker, branch on `event.cron` explicitly;
41
+ keep recovery/sweep operations idempotent because a trigger can overlap a
42
+ queue retry or a manual run.
43
+
44
+ ## Relationships — why
45
+
46
+ - **requires `cloudflare`** — platform feature. **recommends `oblaka`** to declare the cron trigger.
@@ -0,0 +1,51 @@
1
+ ---
2
+ id: d1
3
+ kind: technology
4
+ title: Cloudflare D1 (relational database)
5
+ slot: relational-db
6
+ provides: [relational-db, sql, mutable-state]
7
+ requires: [cloudflare]
8
+ recommends: [oblaka]
9
+ conflicts: [postgres]
10
+ npm: []
11
+ status: stable
12
+ sources:
13
+ - where: "developers.cloudflare.com/d1 (bindings, migrations, query API)"
14
+ read: "The D1 binding API (prepare/bind/all/run), local migrations, and the execute CLI."
15
+ - where: "lopata + examples/react-router (local D1 via lopata d1)"
16
+ read: "How D1 is bound and queried in the canonical local-first RRv7-on-Workers example."
17
+ ---
18
+
19
+ # Cloudflare D1
20
+
21
+ ## What it is
22
+
23
+ SQLite-at-the-edge: the relational store for an app's **mutable, human-driven
24
+ state**. Bound as `env.DB` and queried inside the Worker (request/RPC handlers,
25
+ services, queue or scheduled handlers). SSR loaders/actions are one possible
26
+ transport; a Buzola SPA reaches D1 only through the Worker API. Migrations are
27
+ SQL files; local D1 lives under `.lopata/` (lopata) or `--local` (wrangler).
28
+
29
+ ## When & why to use
30
+
31
+ - Default store for relational/mutable state (users, projects, config, issues…).
32
+ - You want SQL + local-first dev with no account.
33
+
34
+ ## When NOT to use
35
+
36
+ - **High-frequency append/counter workload** (per-event count UPDATE = hot-row contention) → use `analytics-engine` behind `count-store`, append don't UPDATE.
37
+ - Coordination / strong single-object consistency → `durable-objects`.
38
+ - Large binary payloads → `r2`.
39
+
40
+ ## Impacts & gotchas
41
+
42
+ - **Migrations are idempotent**: `migrations/NNNN_name.sql`, `CREATE TABLE IF NOT EXISTS`. Dir = oblaka `migrationsDir`.
43
+ - Keep D1 behind the Worker boundary. Inject `env.DB` into handlers/services;
44
+ never import a Worker binding into client SPA code.
45
+ - Apply: `lopata d1 migrations apply` (local-first) or `wrangler d1 migrations apply DB --local`.
46
+
47
+ ## Relationships — why
48
+
49
+ - **requires `cloudflare`** — it's a CF binding.
50
+ - **recommends `oblaka`** — declare the `D1Database` binding in `oblaka.ts` rather than hand-editing `wrangler.jsonc`.
51
+ - **conflicts `postgres`** — same `relational-db` slot (edge SQLite vs a Postgres server). A `contember` stack uses Postgres, not D1.
@@ -0,0 +1,44 @@
1
+ ---
2
+ id: durable-objects
3
+ kind: technology
4
+ title: Cloudflare Durable Objects (coordination / strong consistency)
5
+ slot: null
6
+ provides: [coordination, stateful-objects, strong-consistency, websockets]
7
+ requires: [cloudflare]
8
+ recommends: [oblaka]
9
+ conflicts: []
10
+ npm: []
11
+ status: stable
12
+ sources:
13
+ - where: "developers.cloudflare.com/durable-objects (class binding, storage API, alarms, WebSocket hibernation)"
14
+ read: "The DO class + binding model, transactional storage, alarms, and WebSocket hibernation."
15
+ ---
16
+
17
+ # Cloudflare Durable Objects
18
+
19
+ ## What it is
20
+
21
+ Single-instance stateful objects addressed by id, giving **strong single-object
22
+ consistency** and **coordination** — the thing D1/KV can't: a serialized point of
23
+ truth per entity. Each DO has transactional storage, alarms, and WebSocket support.
24
+
25
+ ## When & why to use
26
+
27
+ - Per-entity coordination: rate limiters, locks, counters that must be exact, live presence.
28
+ - Real-time: WebSocket fan-out / rooms (hibernation keeps idle sockets cheap).
29
+ - A serialized actor where concurrent requests to the same id must not race.
30
+
31
+ ## When NOT to use
32
+
33
+ - Plain relational/queryable state → `d1`. Hot cache/sessions → `kv`.
34
+ - High-cardinality append metrics (one DO per series doesn't scale) → `analytics-engine`.
35
+
36
+ ## Impacts & gotchas
37
+
38
+ - Routed by id (`idFromName`/`idFromString`) — design the id space deliberately; all requests for one id serialize through one instance.
39
+ - Storage API is transactional but per-object; no cross-object queries.
40
+ - Local emulation under workerd/lopata; declare the class binding + migration in `oblaka.ts`.
41
+
42
+ ## Relationships — why
43
+
44
+ - **requires `cloudflare`** — CF binding. **recommends `oblaka`** to declare the DO namespace/class.
@@ -0,0 +1,42 @@
1
+ ---
2
+ id: kv
3
+ kind: technology
4
+ title: Cloudflare KV (key-value store)
5
+ slot: null
6
+ provides: [kv-store, sessions, dedup-cache]
7
+ requires: [cloudflare]
8
+ recommends: [oblaka]
9
+ conflicts: []
10
+ npm: []
11
+ status: stable
12
+ sources:
13
+ - where: "developers.cloudflare.com/kv (get/put/delete, TTL, eventual consistency)"
14
+ read: "The KV binding API and its eventual-consistency / TTL semantics."
15
+ ---
16
+
17
+ # Cloudflare KV
18
+
19
+ ## What it is
20
+
21
+ Eventually-consistent key-value store. Used for **session tokens**, **API
22
+ keys / DSN lookups**, and **dedup caches** — small values read on the hot path,
23
+ keyed by a stable id. Bound as `env.KV`.
24
+
25
+ ## When & why to use
26
+
27
+ - Session storage (`auth-password-kv`), API-key validation on ingest, idempotency/dedup keys.
28
+ - Hot-path lookups where eventual consistency is acceptable.
29
+
30
+ ## When NOT to use
31
+
32
+ - Read-after-write strong consistency required → `d1` or `durable-objects`.
33
+ - Relational queries / joins → `d1`.
34
+
35
+ ## Impacts & gotchas
36
+
37
+ - **Eventually consistent** — a freshly written key may not be readable immediately everywhere; don't rely on it for read-after-write invariants.
38
+ - Values are opaque blobs (string/ArrayBuffer); model structure yourself. Use TTL for ephemeral session/dedup keys.
39
+
40
+ ## Relationships — why
41
+
42
+ - **requires `cloudflare`** — CF binding. **recommends `oblaka`** — declare `KVNamespace` in `oblaka.ts`.
@@ -0,0 +1,55 @@
1
+ ---
2
+ id: lopata
3
+ kind: technology
4
+ title: lopata (local Cloudflare runtime, local-first)
5
+ slot: cf-dev-runtime
6
+ provides: [local-cf-runtime, cf-dev-server, local-d1, local-bindings]
7
+ requires: [cloudflare]
8
+ recommends: [oblaka, bun, vite]
9
+ conflicts: [cloudflare-vite-plugin]
10
+ npm: ["lopata@^0.18.4"]
11
+ status: wip
12
+ sources:
13
+ - where: "lopata (repo) + installed CLI/plugin source"
14
+ read: "Worker entry, standalone dev command, optional vite-plugin wiring and the d1 CLI."
15
+ - where: "lopata examples"
16
+ read: "Vite integration, resource config, development commands, and multi-worker service bindings."
17
+ ---
18
+
19
+ # lopata
20
+
21
+ ## What it is
22
+
23
+ A drop-in CF runtime for **local dev** that runs on Bun (not workerd). Bindings are
24
+ backed by SQLite + FS under `.lopata/`, with a dashboard at `/__dashboard`. Lets
25
+ the whole stack run locally with **no CF credentials**.
26
+
27
+ ## When & why to use
28
+
29
+ - Local-first development: D1/KV/R2 emulated on disk, zero account.
30
+ - The cf-lightweight path; `bunx lopata d1 …` for migrations/seeds/queries.
31
+
32
+ ## When NOT to use
33
+
34
+ - You need the **real workerd** behavior or a binding lopata doesn't emulate yet → use `cloudflare-vite-plugin` (workerd) instead. lopata is **WIP — expect churn**, so verify what it emulates.
35
+
36
+ ## Impacts & gotchas
37
+
38
+ - **Buzola SPA / Worker split:** run lopata as the standalone Worker dev server;
39
+ run Vite separately and proxy only `/rpc`/`/api`/`/internal` to it. No lopata
40
+ Vite plugin is needed in this shape.
41
+ - **Integrated SSR shape:** when using `lopata/vite-plugin`, verify its current
42
+ `viteEnvironment`/config options. The `'ssr'` environment is specific to the
43
+ SSR-on-Workers path, not a requirement for a client-only SPA.
44
+ - `CF=1` can swap lopata → `@cloudflare/vite-plugin` (real workerd) in stacks that
45
+ explicitly implement that seam. The two share the `cf-dev-runtime` slot.
46
+ - Local D1 under `.lopata/d1/*.sqlite`; CLI `bunx lopata d1 migrations apply` / `execute DB --file …`.
47
+ - Generate `wrangler.jsonc` from oblaka/the shared Vozka resource builder before
48
+ starting lopata. Never maintain local bindings in a second hand-written config.
49
+
50
+ ## Relationships — why
51
+
52
+ - **requires `cloudflare`** (it emulates CF). Vite is recommended, not required:
53
+ lopata can run the Worker separately from a Vite SPA.
54
+ - **conflicts `cloudflare-vite-plugin`** — same `cf-dev-runtime` slot; they're swappable dev paths (env-toggled), not used together. **lopata is our preferred default** (local-first, Bun); reach for the cloudflare-vite-plugin only when you need workerd fidelity.
55
+ - **recommends `bun`** — lopata is Bun-based; part of our bun/lopata preference.
@@ -0,0 +1,50 @@
1
+ ---
2
+ id: nuasite
3
+ kind: technology
4
+ title: Nua Site — Astro meta-package + visual CMS
5
+ slot: site-framework
6
+ provides: [astro-site, visual-cms, markdown, seo-checks, sitemap]
7
+ requires: []
8
+ recommends: [tailwind, cloudflare]
9
+ conflicts: [pletivo]
10
+ npm: ["@nuasite/nua@^0.43", "@nuasite/cms@^0.43", "@nuasite/cli@^0.43", "@nuasite/components@^0.43", "@nuasite/checks@^0.43"]
11
+ status: stable
12
+ sources:
13
+ - where: "nuasite/README.md + packages/nua/README.md"
14
+ read: "The meta-package (pins Astro 6 + Tailwind 4 + MDX + sitemap) and how to configure it."
15
+ - where: "nuasite/packages/cms/README.md"
16
+ read: "The visual editor: how it injects data-cms-id markers and writes edits back to .astro source via HMR."
17
+ ---
18
+
19
+ # Nua Site
20
+
21
+ ## What it is
22
+
23
+ A published set of **Astro meta-packages** for building **content sites with a visual
24
+ CMS**. `@nuasite/nua` pins the toolchain (Astro 6, Tailwind 4, MDX, sitemap);
25
+ `@nuasite/cms` is an Astro integration that serves a visual editor in dev and writes
26
+ edits straight back to `.astro` source files; `@nuasite/checks` validates SEO/a11y/perf
27
+ at build. It mirrors the hosted Nua Site service so local builds match production.
28
+
29
+ ## When & why to use
30
+
31
+ - A **marketing/docs/blog site** that needs **in-place visual editing** with edits flowing back to Git-tracked source.
32
+ - You want a validated Astro toolchain + LLM-friendly content export (`.md` endpoints, `AGENTS.md`).
33
+
34
+ ## When NOT to use
35
+
36
+ - A dynamic **app** (SPA/SSR) → `react-router` / `buzola`.
37
+ - You want the fastest minimal SSG with no CMS → `pletivo`.
38
+ - **Note vs our bun/non-vite preference:** Astro is vite-based, so nuasite leans on vite under the hood — fine for content sites, but it's not the bun-native path.
39
+
40
+ ## Impacts & gotchas
41
+
42
+ - Pages must be **`.astro`** for CMS editing (React/TSX components are islands, not CMS-marked).
43
+ - **Locked dependency versions** (the meta-package pins Astro/Tailwind) — you upgrade when Nua Site releases.
44
+ - The CMS editor parses **Tailwind `class="…"`** (style via utilities, not inline) and has **no built-in auth** — front it with `cloudflare-access` if edits must be gated.
45
+ - (Optional) `@pletivo/astro-jsx-pages` / `astro-jsx-pages` lets you author pages in `.tsx` instead of `.astro` — pre-release; not for the CMS-editing workflow.
46
+
47
+ ## Relationships — why
48
+
49
+ - **conflicts `pletivo`** — same `site-framework` slot.
50
+ - **recommends `tailwind`** (the CMS depends on utility classes) + **`cloudflare`** (a common deploy target).