diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..a0c6b2a7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +# Shared with .gitattributes: LF, and a final newline, because committed artifacts are compared byte for +# byte and an editor's default is not a good reason for a fixture to differ. +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +[*.md] +# Two trailing spaces are a hard line break in Markdown. Trimming them silently rewrites shipped prose — +# and README.md and AGENT-INSTALL.md are read by agents auditing the package. +trim_trailing_whitespace = false + +[*.{lockb}] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..475ffc79 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +# One line ending in the repository, whatever a contributor's platform does locally. +# +# This matters more here than in most repositories: several committed artifacts are compared BYTE for byte +# — the vendored rule contract, the capability manifest, the canary map and rule — and CRLF slipping into +# one of them turns a cross-repository agreement check into a diff nobody can read. +* text=auto eol=lf + +# Bun's binary lockfile. Marked so a diff never mangles it and `text=auto` never guesses. +*.lockb binary diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 00000000..01e6cc66 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,100 @@ +name: Bug report +description: Something in @patchstack/connect does not work as documented. +labels: [bug] +body: + - type: markdown + attributes: + value: | + **This repository is public, and so is everything you paste here.** + + Before you fill this in: redact tokens, site UUIDs, customer hostnames and internal URLs. A + deleted issue is not a retracted one — it stays in notification emails, caches and forks. + + Found a security vulnerability in this package? Do not file it here. Contact Patchstack directly so + it can be fixed before it is described in public. + + - type: input + id: connect-version + attributes: + label: "@patchstack/connect version" + description: "`npm ls @patchstack/connect --depth=0` in your project, or the version in your lockfile." + placeholder: "0.3.31" + validations: + required: true + + - type: input + id: node-version + attributes: + label: Node version + description: "`node --version`. Supported from 18." + placeholder: "v22.12.0" + validations: + required: true + + - type: input + id: package-manager + attributes: + label: Package manager and version + description: npm, pnpm, Yarn or Bun — the resolution differs between them, and so do the bugs. + placeholder: "pnpm 9.12.0" + validations: + required: true + + - type: input + id: framework + attributes: + label: Framework or runtime + description: Next.js, SvelteKit, Astro, NestJS, Fastify, Express, TanStack Start, Cloudflare Workers, Deno, Bun — and its version. + placeholder: "Next.js 15.1.0 (app router), Node runtime" + validations: + required: true + + - type: input + id: os + attributes: + label: Operating system + placeholder: "macOS 15.1 (arm64) / Ubuntu 24.04 / Windows 11" + validations: + required: true + + - type: textarea + id: reproduction + attributes: + label: Steps to reproduce + description: The commands you ran, in order. A repository or a minimal project that shows it is worth more than a description. + placeholder: | + 1. npm install --save @patchstack/connect + 2. npx patchstack-connect setup + 3. ... + validations: + required: true + + - type: textarea + id: expected + attributes: + label: What you expected + description: And where it says so — the README, AGENT-INSTALL.md, the CLI's own output. A documented promise that is wrong is a different bug from a surprising behaviour. + validations: + required: true + + - type: textarea + id: actual + attributes: + label: What happened instead + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Output and logs, sanitized + description: Command output, stack traces, the `patchstack:` lines. Replace tokens, site UUIDs and hostnames with placeholders. + render: shell + + - type: checkboxes + id: disclosure + attributes: + label: Before you submit + options: + - label: This report contains no credentials or tokens, no customer data, no third-party proprietary material such as copied system prompts, and no private implementation details. + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..6d583454 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,14 @@ +# Blank issues are disabled deliberately. +# +# The bug-report form asks for the versions and the reproduction that make a report actionable, and asks +# the reporter to confirm it carries nothing private. A blank issue bypasses both prompts, and this +# repository is public: the cost of a pasted log with a token or a customer hostname in it is not +# recoverable by deleting the issue. +blank_issues_enabled: false +contact_links: + - name: Security vulnerability in this package + url: https://patchstack.com/contact/ + about: Do not open a public issue. Contact Patchstack directly so it can be fixed before it is described. + - name: Help with your Patchstack account, sites or billing + url: https://patchstack.com/contact/ + about: Anything about your account rather than this package. diff --git a/.node-version b/.node-version new file mode 100644 index 00000000..a45fd52c --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +24 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..3d3bedd9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,91 @@ +# Contributing to `@patchstack/connect` + +The human counterpart to `AGENTS.md`. Same boundary, same verification; this one explains the setup. + +## Before anything else: this repository is public + +Its history, code comments, test output, commit messages and pull-request descriptions are all public, and +so is anything you paste into an issue. + +`AGENTS.md` carries the boundary in full and is worth reading once. The two rules that catch people: + +- **Nothing from private Patchstack systems** unless it is already in this repository's public + documentation or public API contract — no private repository names or paths, no internal topology or + endpoints, no customer or vulnerability-review data, no local filesystem paths. +- **Nothing third-party confidential**, which is a separate obligation and not a special case of the + first: copied system prompts, private transcripts, unpublished documentation. Material can be entirely + free of Patchstack internals and still not be ours to publish. Verify that something is public rather + than assuming it. + +Fixtures are synthetic or explicitly public. Do not copy one from a private repository to make a test more +realistic — write it. + +## Setup + +```bash +# .node-version records the version releases are built with (24). fnm, nodenv and asdf read it directly; +# nvm looks for .nvmrc, so it needs the file passed in. +nvm use "$(cat .node-version)" # or: fnm use / nodenv local +npm ci +``` + +Consumers are supported from Node 18 — `engines.node` is that contract, and it is a different question +from the version this repository is developed and released on. + +## The loop + +```bash +npm run typecheck # tsc --noEmit, plus the guard templates, which are typechecked separately +npm run build # tsup -> dist/ (gitignored; built on publish) +npm test # vitest +``` + +Build before testing, in that order. `dist/` is not committed, and several tests only run once it exists — +they skip in a plain checkout and are required in CI. Run in the other order and those tests skip, which +reads exactly like passing. + +## Before you open a pull request + +- `npm run typecheck`, `npm test`, `npm run build` all clean. +- `npm pack --dry-run` if you touched anything about packaging. `files` in `package.json` is the + authoritative allowlist of what ships. +- Read your own diff, commit messages and pull-request description against the boundary above. The + pull-request template asks you to confirm this; it is the only control that covers prose, because secret + scanning finds credentials and nothing finds a paragraph of architecture. + +Changing onboarding, the install prompt or the setup guide? Read `MAINTAINING.md` first — it maps which +files are load-bearing — and note that the install prompt is a tested artifact with its own gate in +`field-test/`. + +## Generated artifacts that must stay in step + +Each of these is generated from a source of truth and committed. Changing the source without regenerating +leaves two files disagreeing, and the check that notices is not always the one that fails first: + +| artifact | generated from | regenerate with | +|---|---|---| +| `capabilities.json` | `src/map/capabilities.ts` | `node scripts/emit-capabilities.mjs` | +| `rule-contract.json` | the engine's match types and actions | `npm run rule-contract` | +| the canary attack-surface map | `tests/map/canary-case.ts` | `PS_CANARY_EMIT_DIR=