Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions packages/graphile-pnpm-policy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# @constructive-io/pnpm-policy-graphile

Release-age exemptions for the Graphile packages Constructive runs.

Pair it with `@constructive-io/pnpm-policy` (our own packages) in any workspace that consumes Graphile:

```yaml
inventory:
- "@constructive-io/pnpm-policy"
- "@constructive-io/pnpm-policy-graphile"
```

`pnpm-policy` merges the two, and `intersect: true` narrows the result to whatever that workspace's lockfile actually resolves — so a repo with no Graphile dependency emits none of these names.

## Why this is a list and not a maintainer query

The obvious way to exempt an upstream is to add its npm account under `maintainers`. That is what we did first, and it was wrong.

`maintainers` exempts **everything the account publishes**, and the account that publishes the Graphile stack also co-maintains `graphql` — the reference implementation — along with `graphiql`, `monaco-graphql`, and `graphql-language-service`. Those swept into our exemption list, so the most widely depended-upon package in the JavaScript ecosystem was skipping the quarantine. Worse, `graphql` has six maintainers, so a compromise of *any* of those accounts would have published a release that installed immediately, with no wait.

An account is a delegation as wide as everything that account will ever touch. That is fine for accounts we control and wrong for anyone who also maintains popular upstream software.

So this package enumerates package names instead. Nothing is exempt unless it is written down here.

## How the list is derived

1. Take every package the Constructive workspaces resolve (read from their lockfiles, so transitive dependencies are included — not just direct ones).
2. Keep the ones whose npm `repository` points at a Graphile-owned repository (`github.com/graphile/*`, `github.com/graphile-contrib/*`).
3. Drop everything else, including packages that merely look Graphile-adjacent.

Step 2 is the whole test, and it is checkable: `npm view <pkg> repository.url`. `graphql`, `graphiql`, `monaco-graphql`, `graphql-language-service`, `@graphiql/*`, `graphql-tag`, `graphql-upload` and `graphql-ws` all fail it — they belong to `graphql/graphql-js`, `graphql/graphiql`, Apollo, and individual authors. They wait like any other third-party dependency.

The current list is the union across `constructive` and `constructive-db`, which independently resolve the same 16 packages.

Scope globs are deliberately **not** used. `@graphile/*` or `@dataplan/*` would exempt anything published into those scopes in the future, which is the same open-ended delegation as a maintainer entry, just smaller. Names cost a line each and say exactly what was decided.

## Updating it

Adding a Graphile dependency that is not listed here means the install waits out the quarantine. Add the name, note why it is needed, and confirm its `repository` is Graphile-owned before merging. That review is the point: this file is a trust boundary, and it should be as boring and explicit as one.
23 changes: 23 additions & 0 deletions packages/graphile-pnpm-policy/inventory.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"generatedAt": "2026-08-06T08:25:26.000Z",
"maintainers": [],
"scopes": [],
"packages": [
"@dataplan/json",
"@dataplan/pg",
"@graphile-contrib/pg-many-to-many",
"@graphile/lru",
"grafast",
"grafserv",
"graphile-build",
"graphile-build-pg",
"graphile-config",
"graphile-utils",
"pg-introspection",
"pg-sql2",
"postgraphile",
"ruru",
"ruru-types",
"tamedevil"
]
}
46 changes: 46 additions & 0 deletions packages/graphile-pnpm-policy/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "@constructive-io/pnpm-policy-graphile",
"version": "0.1.0",
"author": "Constructive <developers@constructive.io>",
"description": "Release-age exemptions for the Graphile packages Constructive runs \u2014 a reviewed allowlist, not a maintainer query",
"homepage": "https://github.com/constructive-io/dev-utils",
"license": "MIT",
"exports": {
"./inventory.json": "./inventory.json",
"./package.json": "./package.json"
},
"files": [
"inventory.json",
"README.md",
"LICENSE"
],
"publishConfig": {
"access": "public",
"directory": "dist"
},
"repository": {
"type": "git",
"url": "https://github.com/constructive-io/dev-utils"
},
"bugs": {
"url": "https://github.com/constructive-io/dev-utils/issues"
},
"scripts": {
"clean": "makage clean",
"copy": "makage assets",
"build": "makage clean && makage copy inventory.json dist --flat && makage assets",
"prepublishOnly": "npm run build",
"test": "node test.js"
},
"devDependencies": {
"makage": "0.1.10"
},
"keywords": [
"pnpm",
"pnpm-policy",
"supply-chain",
"minimumReleaseAge",
"graphile",
"inventory"
]
}
53 changes: 53 additions & 0 deletions packages/graphile-pnpm-policy/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env node
/**
* Guards the trust boundary this package exists to draw.
*
* Every name here is exempt from the release-age quarantine, so the list must
* stay Graphile-owned. The account that publishes Graphile also co-maintains
* `graphql` and the GraphiQL packages; a maintainer-derived list swept those in
* once already, which is why this package enumerates names instead.
*
* Plain node asserts, no test framework: this is data, and the check should run
* anywhere with no install.
*/
const assert = require('assert');
const inventory = require('./inventory.json');

const FORBIDDEN = [
'graphql',
'graphiql',
'monaco-graphql',
'graphql-language-service',
'graphql-tag',
'graphql-upload',
'graphql-ws'
];

assert.ok(Array.isArray(inventory.packages), 'inventory.packages must be an array');
assert.ok(inventory.packages.length > 0, 'inventory.packages must not be empty');

// Scope globs would exempt anything published into that scope later — the same
// open-ended delegation as a maintainer entry. Names only.
assert.deepStrictEqual(inventory.scopes, [], 'scopes must stay empty: enumerate names instead');
assert.deepStrictEqual(
inventory.maintainers,
[],
'maintainers must stay empty: this is a reviewed allowlist, not a maintainer query'
);

for (const name of FORBIDDEN) {
assert.ok(
!inventory.packages.includes(name),
`${name} is not Graphile-owned and must not skip the quarantine`
);
}

const sorted = [...inventory.packages].sort();
assert.deepStrictEqual(inventory.packages, sorted, 'packages must stay sorted for reviewable diffs');
assert.strictEqual(
new Set(inventory.packages).size,
inventory.packages.length,
'packages must not contain duplicates'
);

console.log(`ok — ${inventory.packages.length} Graphile packages, no forbidden names`);
7 changes: 7 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading