popcomputer-web

agent
Security Audit
Fail
Health Pass
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Community trust — 11 GitHub stars
Code Fail
  • spawnSync — Synchronous process spawning in exams/check.ts
  • exec() — Shell command execution in exams/grade.ts
  • spawnSync — Synchronous process spawning in exams/grade.ts
  • Hardcoded secret — Potential hardcoded credential in exams/shop/hidden.exam.ts
Permissions Pass
  • Permissions — No dangerous permissions requested

No AI report is available for this listing yet.

SUMMARY

A batteries-included Effect framework for full-stack apps on Cloudflare Workers, in the spirit of Laravel: Hono routing, Inertia and React pages, Better Auth sign-in, Drizzle with D1 or Postgres, and secure defaults tested against other frameworks' CVEs.

README.md
Popcomputer

@popcomputer/web

@popcomputer/web, popweb for short, is a batteries-included Effect framework
for full-stack apps on Cloudflare Workers, in the spirit of Laravel. Hono routes
the requests, Inertia and React render the pages, Better Auth signs people in,
and Drizzle reads and writes D1 or Postgres. Forms, validation, caching,
background work and tests come with it, and so do a rulebook and lint rules for
coding agents. Its website is web.popcomputer.com.

  • Laravel's request-to-page workflow, on Workers. A route loads its record,
    its action validates the request and renders a page or redirects, and a
    failed form goes back with its errors. Pages, forms, scheduled jobs and
    queues run in one Cloudflare Worker.
  • Effect from request to page, with typed failures. Each action is an
    Effect that names the services it needs and the failures it can return. A
    route whose services are missing or whose failures go unhandled is a type
    error, and so is a page given the wrong props.
  • Secure defaults, tested against other frameworks' advisories. The
    security suite attacks the framework the way published
    vulnerabilities attacked Next.js, Laravel, Hono, Better Auth and others, and
    fails if one works here.
    The list names each
    one.

To start a new app from the template:

bun create PatrickOgilvie/popcomputer-web-demo my-app

To build with an AI, paste the
setup prompt into it. It tells your AI
where the docs are, how to make the app from the template, and how to check its
work.

An action that shows a project:

// Only its owner loads a project: anyone else gets the 404 a missing one gets.
const bindings = { project: routeBinding(projects, { ownedBy: 'userId' }) }

export const showProject = action(
  Effect.gen(function* () {
    const project = yield* bound('project')

    return yield* render('Projects/Show', { project })
  })
)

Documentation

The documentation is one short file per task, in docs/. The package
ships it as node_modules/@popcomputer/web/docs/, matching the installed
version. Which API for which task names the API for each
task and the file that explains it.

Start here:

Setting up:

  • Installation: the demo, the packages, tsconfig.json, Oxlint, Vite's dev server and the supported versions
  • Quick start: the bindings, setupWeb and its registration, the database, the Worker's entry
  • Vars and secrets: defineEnv, the check when an isolate starts, and checkEnv for scripts
  • Deploying: version values, and what the framework does around a deploy

Building:

  • Pages and props: page schemas, React pages, shared props, the error page and the page map
  • Forms: form schemas, form pages, flash messages, failed forms and error bags
  • Validating input: validateRequest, the validators, dates, uploads and filters
  • Actions and routes: paths, actions, registrations, the response helpers and defects
  • Cookies, files and headers: declared cookies, files, polling, the Origin check and security headers
  • JSON clients: who gets JSON, the error envelope, JSON declared by a schema, and call
  • JSON endpoints: contracts, their actions, routes.endpoint and openApi
  • Route model binding: bindings, bound(), and models scoped to their parent
  • Application services: web.services, config, and rules in services
  • The database: dbQuery, dbMutation, dbTransaction, dbBatch, migrations and cursors
  • Constraints: invariants concurrent requests would break, D1 batches and rowOf
  • Webhooks: verifying the raw body, and recording each event once

Auth:

Running:

Testing and reference:

  • Testing: requests through the application, the readers, one visitor, fakes and time
  • Fuzzing: properties on generated inputs, generated requests to the application's routes and the rules their answers keep, and sequences of steps
  • A database for tests: PGlite for Postgres, and D1 through Miniflare
  • Lower-level composition: the middleware without setupWeb
  • State machines: what the framework keeps across requests, the rule each keeps, and the models that check them

Why this shape

  • Declare once; the rest derives. setupWeb takes each of the
    application's declarations as a value: the database factory, the Drizzle
    schema and the tables route models load from, the signed-in user's schema,
    pages, route paths, shared props and flash. Registering its result once
    types everything else: bound('project') is the table's row, render
    checks a page's props, redirect and href fill a path's placeholders, and
    a React page's props follow its schema. A renamed column, prop or
    placeholder is a type error wherever it is used.
  • Actions are ordinary Effect values. They declare only the services and
    failures they use, so small actions stay small and complex workflows
    compose. routes accepts an action only when the setup provides its
    services and handles its failures; otherwise the registration is a type
    error naming the missing service or the unhandled error.
  • External data is parsed at the edge. Sessions, request input, route
    parameters, database rows, cached values and webhook bodies are checked with
    Effect Schema before application code relies on them, and what reaches the
    browser is encoded through the page's schema.
  • Correct by default where it is easy to get wrong: cached pages vary on
    cookies and are purged by the records they show, failed forms go back to the
    form with their errors, and a record a visitor may not see is a 404.
  • The standard Inertia protocol. Existing Inertia clients keep using
    X-Inertia, partial reloads, asset versions, error bags and redirects.

Getting started

To start from a complete application, create one from the
demo:

bun create PatrickOgilvie/popcomputer-web-demo my-app

To add the package to an application of your own:

bun add @popcomputer/web effect hono

Installation lists the optional integrations, the
tsconfig.json and the supported versions, and the Quick start
sets an application up.

Coding agents should read node_modules/@popcomputer/web/AGENTS.md, a short
rulebook naming the one API to use for each task, and the files in
node_modules/@popcomputer/web/docs/ as a task needs them.
The agent fence holds them to it with checks: a commit
gate, a hook that refuses a command skipping it and asks the owner before a
deploy, and a count of disable directives that only shrinks.

Data flow

flowchart LR
  Request["HTTP request"] --> Hono["Hono middleware"]
  Hono --> Boundary["Schema and auth boundaries"]
  Boundary --> Action["Effect action"]
  Action --> Services["Typed application services"]
  Services --> Result["Page, redirect, JSON, or typed failure"]
  Result --> Protocol["Inertia or HTTP response"]

setupWeb builds the request-scoped Effect runtime and provides only the
services configured for that request. A missing database or auth service is
reported as a configuration error rather than being silently replaced with an
untyped value.

Patterns from real applications

These patterns come from applications built on the package, most of them by
coding agents. Each replaces a first attempt that compiled and then
misbehaved.

Each lives in the file for its task:

Upgrading

UPGRADING.md, shipped in the package as
node_modules/@popcomputer/web/UPGRADING.md, takes an application on any
earlier release, or on the honertia package, to this one. It moves an
application's own code from Effect 3 to Effect 4 too, which most of an upgrade
from honertia is, maps every former Honertia* name to its replacement,
and lists the changes no type error points at.

Tradeoffs and current limits

  • This is a server-driven page architecture. It is not a replacement for a
    client-side router when the browser must own navigation and data fetching.
  • Effect is a deliberate application-level dependency. The package is most
    valuable when actions, failures, services, and background work all use the
    same Effect model.
  • Route model binding currently relies on Drizzle metadata. Other database
    clients still work through DatabaseService, but do not receive automatic
    binding queries.
  • Better Auth and Cloudflare-specific caching are optional integrations rather
    than hidden requirements.
  • The package does not run database migrations. Use drizzle-kit or
    wrangler d1 migrations directly.
  • Cloudflare D1 has no interactive transactions. dbBatch applies several
    writes atomically there; dbTransaction needs Postgres, MySQL or libsql,
    and is a type error for synchronous drivers such as bun:sqlite.
  • Page props cross to the browser as JSON. Jsonify describes the result
    (dates and bigints become strings); page schemas should describe plain
    data rather than classes or Effect data types.
  • A table binding checks each column's database type, nullability and enum
    values, but not a narrowing declared only in TypeScript, such as
    text('role').$type<Role>(): name an Effect Schema for that binding, or
    give the column an enum.
  • Workers Cache answers before the Worker runs, so a page cached with the
    cache option is shared only by visitors who send no cookies. Visitors
    with analytics cookies render it afresh; the default page cache, which runs
    in the Worker, ignores those cookies but caches only pages that read no
    data.

Developing this package

bun run test

After changing AGENTS.md, regenerate the skill with
bun scripts/build-skill.ts; a test fails while it is stale.

The repository verifies runtime behavior, strict TypeScript and module
augmentation contracts, that every TypeScript example in this README, in
docs/, in AGENTS.md and in UPGRADING.md type-checks, that every link
between them resolves and no file in docs/ passes 250 lines, and that each
built entrypoint imports under plain Node.

License

MIT

Reviews (0)

No results found