Skip to content

Add primitive functions:init scaffolder#63

Merged
etbyrd merged 2 commits into
mainfrom
functions-init-scaffolder
May 11, 2026
Merged

Add primitive functions:init scaffolder#63
etbyrd merged 2 commits into
mainfrom
functions-init-scaffolder

Conversation

@etbyrd
Copy link
Copy Markdown
Member

@etbyrd etbyrd commented May 11, 2026

Summary

  • New CLI command primitive functions:init <name> that stamps a deployable Function project (handler.ts, package.json, build.mjs, tsconfig.json, .gitignore, README.md) into ./<name>/. First-touch UX for new Function authors: they go from zero to deployed in two commands (npm install && npm run build then primitive functions:deploy --file ./dist/handler.js).
  • The scaffolded handler.ts imports createPrimitiveClient from @primitivedotdev/sdk/api (NOT the package root, which pulls in node:crypto-dependent webhook helpers and breaks Workers-style bundles) and demonstrates client.send({...}) rather than raw fetch against /api/v1/send-mail.
  • Refuses to overwrite an existing directory. Validates the name to a slug shape so it's safe as both a directory name and a package.json name.

Test plan

  • pnpm test (sdk-node): 681 tests pass, including new functions-init.test.ts covering name validation, handler-shape regression guards (asserts the /api subpath import), template substitution, refuses-to-overwrite behavior, and a no-write rollback on invalid name.
  • pnpm lint (sdk-node): zero errors.
  • pnpm typecheck (sdk-node): clean.
  • make node-check and make shared-check: clean.
  • End-to-end smoke: ran primitive functions:init test-fn-tmp --out-dir /tmp/..., then npm install (138 packages, 3s), then npm run build (esbuild produces dist/handler.js), then npx tsc --noEmit (clean). Output bundle imports the SDK correctly and emits a working ESM Worker default export.
  • Refuses-to-overwrite path verified manually: re-running against an existing dir exits non-zero with a clear error and leaves the existing tree untouched.

Stamps handler.ts, package.json, build.mjs, tsconfig.json, .gitignore, and README.md into ./<name>/ so a new Function author can run npm install && npm run build then primitive functions:deploy --file ./dist/handler.js without writing the boilerplate themselves.

The scaffolded handler imports createPrimitiveClient from @primitivedotdev/sdk/api (NOT the package root, which pulls in node:crypto-dependent webhook helpers and breaks Workers bundles) and demonstrates client.send() rather than a raw fetch against /api/v1/send-mail. This is the first-touch UX hook for in-handler SDK use.
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 11, 2026

Greptile Summary

Adds primitive functions:init <name>, a scaffolding command that writes a ready-to-deploy Function project (handler.ts, package.json, build.mjs, tsconfig.json, .gitignore, README.md) into ./<name>/. The two issues raised in prior review rounds — the TOCTOU overwrite window and the inconsistent cd hint — are both resolved: mkdirSync is now called with recursive: false for an atomic existence check, and the cd hint uses the fully resolved outDir throughout.

  • The scaffolded handler.ts imports from @primitivedotdev/sdk/api (not the package root) and demonstrates client.send(), matching the documented Workers-style bundle pattern.
  • writeScaffold validates the slug-shaped name before touching disk, rolls back the partial tree on any write error, and surfaces EEXIST/ENOENT as friendly CLIError messages.
  • Test coverage spans name validation, handler-import regression guards, template substitution, overwrite refusal, and the no-write-on-invalid-name path.

Confidence Score: 5/5

Safe to merge — the scaffolder is well-contained and both issues flagged in earlier rounds have been properly addressed.

The atomic mkdirSync with recursive: false closes the overwrite-guard race, the cd hint consistently uses the resolved absolute path, name validation fires before any filesystem work, and the rollback path on write failure is exercised by tests. No correctness or data-loss concerns remain.

No files require special attention.

Important Files Changed

Filename Overview
sdk-node/src/oclif/commands/functions-init.ts New command implementing scaffold logic; TOCTOU guard uses atomic mkdirSync with recursive: false; cd hint correctly uses resolved outDir; name validation, rollback on error, and error codes all handled properly.
sdk-node/tests/oclif/functions-init.test.ts Comprehensive unit tests covering name validation, handler shape regression guards, template substitution, overwrite refusal, and invalid-name no-write behavior.
sdk-node/src/oclif/index.ts Registers functions:init in the COMMANDS map alongside the existing functions:* commands.
sdk-node/package.json Updates the functions topic description to mention the new functions:init command.

Reviews (2): Last reviewed commit: "Close TOCTOU window and show resolved pa..." | Re-trigger Greptile

Comment thread sdk-node/src/oclif/commands/functions-init.ts Outdated
Comment thread sdk-node/src/oclif/commands/functions-init.ts Outdated
Greptile review on #63 flagged two minor quality issues:

- The overwrite guard used a separate existsSync check before a recursive: true mkdirSync, leaving a small window where a concurrent invocation could slip past undetected. Replaced with a single recursive: false mkdirSync, which throws EEXIST atomically; the friendly "directory already exists" message still surfaces. ENOENT (missing parent) is also mapped to a friendly message.
- The cd hint after a successful scaffold printed the raw --out-dir value, which can mislead a user who invoked the command from a different cwd than the printed path. Switched to the already-resolved absolute outDir so cd always points where the files actually landed.

Tests unchanged; the existing "refuses to overwrite" test still passes because the error path returns the same CLIError. All 681 sdk-node tests green.
@etbyrd
Copy link
Copy Markdown
Member Author

etbyrd commented May 11, 2026

Addressed Greptile findings in 7acb0a0:

  1. TOCTOU window between existsSync and mkdirSync: replaced with a single mkdirSync(..., { recursive: false }) which throws EEXIST atomically. ENOENT (missing parent) also gets a friendly message now. The original 'directory already exists' CLIError surfaces unchanged, so the existing refuses-to-overwrite test still passes.
  2. cd hint in next-steps: now prints the resolved absolute outDir instead of the raw --out-dir value, so cd always points where the files actually landed.

All 681 sdk-node tests still green.

@etbyrd etbyrd merged commit 73ce143 into main May 11, 2026
8 checks passed
@etbyrd etbyrd deleted the functions-init-scaffolder branch May 11, 2026 03:56
@etbyrd etbyrd mentioned this pull request May 11, 2026
2 tasks
etbyrd added a commit that referenced this pull request May 11, 2026
First release that bundles the functions:init scaffolder (PR #63) on top of 0.22.0:
- primitive functions:init <name> stamps a deployable Function project (handler.ts, package.json, build.mjs, tsconfig.json, .gitignore, README.md) with one command. The scaffolded handler.ts imports createPrimitiveClient from @primitivedotdev/sdk/api and demos client.send({...}), not raw fetch against /api/v1/send-mail. The /api subpath avoids dragging in node:crypto-dependent webhook helpers, which breaks Workers-style bundles. Closes the load-bearing gap that had every AGX run reimplementing send-mail with raw fetch in the handler.

No API-shape changes; safe upgrade from 0.22.0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant