Add create-scratchwork-server: npm create scaffolding for self-hosted servers#25
Merged
Conversation
… servers New create/ workspace publishing the create-scratchwork-server package, so `npm create scratchwork-server my-server -- --platform cloudflare` scaffolds a standalone self-hosted server project (platforms: cloudflare, aws, local). - Templates are generated from the real deploy/* projects at build/pack time (create/generate-templates.ts) and never committed, so template content structurally cannot drift from the deploy sources; @scratchwork/* deps are pinned to the lockstep version read at staging time (set-version keeps it stamped via the root manifest). - The bin is non-interactive with args alone (agents first); it prompts for the platform only on a TTY. - Joins the publish flows: PUBLISHABLE staging order, tarball verification, lockstep version check, publish order, RELEASING.md. - check-npm-pack now also runs the packed bin under plain Node to scaffold every template into the hermetic consumer (staged tarballs, no network) and typechecks each scaffolded project; it also no longer symlinks packed package names into the consumer, which would overwrite workspace sources through the repo node_modules symlink. - Docs: scratchwork.dev/www/index.md self-hosting section now shows the real commands, server/README.md leads with the scaffolder, AGENTS.md workspace map gains create/. The regenerated default-renderer.generated.js hash follows the bun.lock change (new workspace), as expected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per Pete's decision: scaffolded servers should not inherit the deploy sources' allowedUsers (deploy/cloudflare-vanilla ships "public"). The template transform now forces allowedUsers: "@example.com" in every platform template — replacing an existing value, or inserting one after the auth: line where the source sets none (aws, local) — so a scaffolded server fails closed and the policy rides the same example.com placeholder edit as the domains. Generation now asserts the placeholder lands exactly once per template, the workspace tests pin the "@example.com" default (and its README mention) per platform, and the scaffolded READMEs explain replacing the placeholder policy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
npm create scratchwork-server my-server -- --platform cloudflarenow scaffolds a standalone self-hosted Scratchwork server project. Platforms:cloudflare(Worker + R2 + D1),aws(Lambda Function URL + S3 + DynamoDB),local(single-machine Bun server).New top-level
create/workspace publishing one package,create-scratchwork-server, with the platform chosen by--platform(or a TTY-only prompt).Why one package instead of per-platform packages
The homepage promised
npm create scratchwork-server-cloudflare(and variants). A single package is cleaner here:scratchwork.dev/www/index.md's self-hosting section now documents the real commands (includingbun run local/bun run deployreplacing the fictionalnode local.ts/node deploy.ts). The site is not republished here.Template drift prevention (structural)
Templates are generated from the real
deploy/*projects at build/pack time (create/generate-templates.ts, called fromscripts/build-packages.tswhile staging) and are never committed, so template code cannot drift from the deploy sources:cloudflare←deploy/cloudflare-vanilla,aws←deploy/generic-aws,local←deploy/local-dev;sndbx.sh→example.complaceholders,workspace:*→ the exact lockstep version read from the root manifest at staging time (scripts/set-version.tskeeps it stamped — nothing hardcoded), repo-onlyci/testscripts dropped,../../sharedtsconfig includes dropped;create/templates-src/) — the deploy projects' READMEs describe this repo's own deployments, so verbatim copies would be wrong;_env.example,_gitignore) because npm strips/repurposes some dotfiles in tarballs; the scaffolder renames them back.ci coverage added
create/joins the root workspaces, sobun run ciruns itstypecheck+test(22 tests: template transform guarantees, scaffolder behavior, bin arg handling) andcheck-versionsenforces its lockstep version.createjoinsPUBLISHABLE, soscripts/check-npm-pack.tspacks and shape-checks its tarball (bin target present, templates ship, no stray sources), and adds a new stage: it runs the packed bin under plain Node (whatnpm createuses) to scaffold every platform template into the hermetic consumer — where the scaffolded@scratchwork/*deps resolve to the staged tarballs, no network — then typechecks each scaffolded project with the consumer-pinned tsc and asserts the deps are pinned to the exact lockstep version.create-scratchwork-serverpublishes last via the samescripts/publish-packages.ts; RELEASING.md updated.Also fixed in
check-npm-pack.ts: the consumer assembly used to symlink every non-scoped reponode_modulesentry, then copy extracted tarballs through those symlinks — for an unscoped packed package (like this one) that overwrote the actual workspace source. Packed package names are no longer symlinked.Invariants
create/dir and is deliberately plain Effect-free TypeScript, likescripts/— the shipped bin must run under npm's plain Node with zero runtime dependencies. It is outside the Effect-boundary lint scope by placement, and AGENTS.md's workspace map now says so explicitly. Nocli/,server/, orshared/source changed (only the generated renderer hash, which follows the bun.lock change from adding a workspace).deploy/*at pack time makes drift structurally impossible, and the scaffold+typecheck ci stage verifies the result against exactly what ships.bun run cipasses locally end to end under the pinned Bun 1.2.15 (including the e2e LocalStack/miniflare lanes).🤖 Generated with Claude Code