@bounded-systems/mint 0.4.15 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ name: standard
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+ permissions:
7
+ contents: read
8
+ jobs:
9
+ standard:
10
+ uses: bounded-systems/.github/.github/workflows/repo-standard.yml@d43c3280588ef05f4ead43426db1091d4cb8f520
11
+ with:
12
+ security: true
13
+ test: true
14
+ runtime: node
15
+ test-command: "npm ci --no-audit --no-fund && npm test"
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.0 — 2026-07-05
4
+
5
+ ### Minor
6
+
7
+ - read + bump `deno.json` (not just `package.json`/`jsr.json`) — unblocks every Deno/JSR package in the org from adopting mint (fixes #13; unblocks gh-project-room#47)
8
+
9
+ ### Patch
10
+
11
+ - docs: document the minimumDependencyAge 24h JSR/npm cooldown for consumers and its batch-rollout ripple (#11)
12
+
3
13
  ## 0.4.15 — 2026-06-29
4
14
 
5
15
  ### Patch
package/README.md CHANGED
@@ -137,6 +137,54 @@ JSR type-checker resolves mint's `node:` imports.
137
137
  > authorizes the keyless OIDC publish; after it, every tagged release publishes
138
138
  > with no token.
139
139
 
140
+ ### Consumers: the 24h JSR/npm cooldown after a release
141
+
142
+ Every fresh JSR/npm publish immediately hits Deno's
143
+ [`minimumDependencyAge`](https://docs.deno.com/runtime/packages/supply_chain/)
144
+ in any consumer running Deno 2.9+: since 2.9 it defaults to **24 hours** — Deno
145
+ refuses to resolve a version published inside that window, full stop, no
146
+ config needed to trigger it. It's a real supply-chain guard (malicious
147
+ versions are usually caught/yanked within days), but it applies to first-party
148
+ `@bounded-systems/*` releases exactly the same as to a random third party.
149
+
150
+ Concretely: `baobab@0.2.0` published, and every downstream consumer's CI
151
+ (e.g. a `check-contrast.yml` caller) hard-failed for the next 24h trying to
152
+ resolve it — not a flake, a guaranteed wait baked into every release
153
+ ([bounded-systems/mint#11](https://github.com/bounded-systems/mint/issues/11)).
154
+ For one package that's a wait; for a coordinated rollout of several
155
+ interdependent `@bounded-systems/*` packages in one sitting, it ripples —
156
+ every consumer of every package in the batch eats the same 24h, and chains of
157
+ dependencies compound it.
158
+
159
+ **The fix does NOT live in a consumer's `deno.json`.** An earlier version of
160
+ this doc claimed a `minimumDependencyAge.exclude` list there would work —
161
+ that was wrong, and only looked right because it was tested against Deno
162
+ 2.8.3 (which doesn't enforce the 24h default at all; the default only started
163
+ in 2.9). Verified against the real `2.9.1` binary: `deno.json` is never even
164
+ read when `deno run`'s entrypoint is a bare `jsr:...` specifier — config-file
165
+ discovery only applies in normal "project mode." Planting invalid JSON in a
166
+ `deno.json` and confirming Deno never complained is what exposed this.
167
+
168
+ **What actually works:** the `--minimum-dependency-age` CLI flag on the
169
+ invocation itself. That means the fix has to live wherever the `deno run`
170
+ command is composed — for a reusable GitHub Actions workflow like
171
+ `check-contrast.yml`, that's an input on the workflow, not something a caller
172
+ can override from its own repo
173
+ ([bounded-systems/baobab#7](https://github.com/bounded-systems/baobab/pull/7)
174
+ adds `minimum-dependency-age`, defaulting to `"0"`, since the only thing that
175
+ workflow ever resolves from the network is `@bounded-systems/baobab` itself —
176
+ a first-party package the caller already trust-bounds via its own version
177
+ range input, so the age gate adds no real protection there). Any other
178
+ reusable workflow or script that shells out to `deno run jsr:...`/`npm:...`
179
+ directly needs the same treatment: an explicit `--minimum-dependency-age`
180
+ (or `--minimum-dependency-age=0` if every dependency it touches is
181
+ first-party) on the command, not a config file a caller drops in.
182
+
183
+ This still doesn't scale past a handful of hand-fixed workflows — #11 is the
184
+ open ask for mint to own generating this consistently across every
185
+ `@bounded-systems/*` reusable workflow, rather than fixing each one by hand
186
+ as it's hit.
187
+
140
188
  ## Library
141
189
 
142
190
  ```js
package/jsr.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bounded-systems/mint",
3
- "version": "0.4.15",
3
+ "version": "0.5.0",
4
4
  "license": "PolyForm-Noncommercial-1.0.0",
5
5
  "exports": {
6
6
  ".": "./plan.mjs",
package/mint.mjs CHANGED
@@ -34,10 +34,23 @@ async function readJson(path) {
34
34
  return JSON.parse(await readFile(path, "utf8"));
35
35
  }
36
36
 
37
+ // The version lives in whichever manifest a repo uses: deno.json (Deno),
38
+ // jsr.json (JSR-from-npm), or package.json (npm). A repo may carry several kept
39
+ // in lockstep. Detect the first present one that declares a version.
40
+ const MANIFESTS = ["deno.json", "jsr.json", "package.json"];
41
+
37
42
  async function currentVersion() {
38
- const pkg = await readJson("package.json");
39
- if (!pkg.version) throw new Error("package.json has no version field");
40
- return pkg.version;
43
+ for (const path of MANIFESTS) {
44
+ try {
45
+ const m = await readJson(path);
46
+ if (m.version) return m.version;
47
+ } catch (e) {
48
+ if (e.code !== "ENOENT") throw e;
49
+ }
50
+ }
51
+ throw new Error(
52
+ `no manifest with a version field (looked for: ${MANIFESTS.join(", ")})`,
53
+ );
41
54
  }
42
55
 
43
56
  async function cmdPlan() {
@@ -63,10 +76,25 @@ async function cmdVersion() {
63
76
  return;
64
77
  }
65
78
 
66
- // Bump the manifest (+ lockfile if present), preserving 2-space JSON.
67
- const pkg = await readJson("package.json");
68
- pkg.version = p.nextVersion;
69
- await writeFile("package.json", JSON.stringify(pkg, null, 2) + "\n");
79
+ // Bump every manifest present (deno.json / jsr.json / package.json) in
80
+ // lockstep, preserving 2-space JSON.
81
+ const bumped = [];
82
+ for (const path of MANIFESTS) {
83
+ try {
84
+ const m = await readJson(path);
85
+ m.version = p.nextVersion;
86
+ await writeFile(path, JSON.stringify(m, null, 2) + "\n");
87
+ bumped.push(path);
88
+ } catch (e) {
89
+ if (e.code !== "ENOENT") throw e;
90
+ }
91
+ }
92
+ if (bumped.length === 0) {
93
+ throw new Error(
94
+ `no manifest to bump (looked for: ${MANIFESTS.join(", ")})`,
95
+ );
96
+ }
97
+ // Keep an npm lockfile in lockstep if present.
70
98
  try {
71
99
  const lock = await readJson("package-lock.json");
72
100
  lock.version = p.nextVersion;
@@ -75,14 +103,6 @@ async function cmdVersion() {
75
103
  } catch (e) {
76
104
  if (e.code !== "ENOENT") throw e;
77
105
  }
78
- // Keep jsr.json in lockstep (JSR has its own manifest version).
79
- try {
80
- const jsr = await readJson("jsr.json");
81
- jsr.version = p.nextVersion;
82
- await writeFile("jsr.json", JSON.stringify(jsr, null, 2) + "\n");
83
- } catch (e) {
84
- if (e.code !== "ENOENT") throw e;
85
- }
86
106
 
87
107
  // Prepend the entry to CHANGELOG.md (create with a header if absent).
88
108
  let changelog = "";
@@ -95,7 +115,7 @@ async function cmdVersion() {
95
115
  // Consume the intents.
96
116
  for (const i of intents) if (i.file) await unlink(i.file);
97
117
 
98
- console.log(`mint: ${p.currentVersion} → ${p.nextVersion} (${p.bump}). Updated package.json + CHANGELOG.md, consumed ${intents.length} intent(s).`);
118
+ console.log(`mint: ${p.currentVersion} → ${p.nextVersion} (${p.bump}). Updated ${bumped.join(" + ")} + CHANGELOG.md, consumed ${intents.length} intent(s).`);
99
119
  console.log(`Next: commit, then \`mint release\` to cut the tag + emit release provenance.`);
100
120
  }
101
121
 
@@ -122,9 +142,7 @@ function ciBuilder() {
122
142
  // (reads package.json + CHANGELOG + git HEAD + CI env); delegates the pure shape
123
143
  // to release.mjs. Throws with a CLI-friendly message on a missing changelog entry.
124
144
  async function gatherStatement() {
125
- const pkg = await readJson("package.json");
126
- const version = pkg.version;
127
- if (!version) throw new Error("package.json has no version");
145
+ const version = await currentVersion();
128
146
  const tag = `v${version}`;
129
147
 
130
148
  let changelog = "";
package/mint.test.mjs CHANGED
@@ -177,3 +177,37 @@ test("DSSE pre-authentication encoding wraps the canonical statement", () => {
177
177
  assert.ok(pae.startsWith(`DSSEv1 ${DSSE_PAYLOAD_TYPE.length} ${DSSE_PAYLOAD_TYPE} ${Buffer.byteLength(payload)} `));
178
178
  assert.ok(pae.endsWith(payload));
179
179
  });
180
+
181
+ // ── CLI manifest handling: mint reads/bumps deno.json, not just package.json (#13)
182
+ import { execFileSync } from "node:child_process";
183
+ import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync } from "node:fs";
184
+ import { tmpdir } from "node:os";
185
+ import { join } from "node:path";
186
+ import { fileURLToPath } from "node:url";
187
+
188
+ const MINT = fileURLToPath(new URL("./mint.mjs", import.meta.url));
189
+
190
+ function bumpIn(manifestName) {
191
+ const d = mkdtempSync(join(tmpdir(), "mint-cli-"));
192
+ try {
193
+ writeFileSync(
194
+ join(d, manifestName),
195
+ JSON.stringify({ name: "@x/y", version: "0.1.0", exports: "./m.ts" }, null, 2) + "\n",
196
+ );
197
+ mkdirSync(join(d, ".release"));
198
+ writeFileSync(join(d, ".release", "i.md"), "---\nbump: minor\n---\nx\n");
199
+ execFileSync("node", [MINT, "version"], { cwd: d, stdio: "pipe" });
200
+ return JSON.parse(readFileSync(join(d, manifestName), "utf8")).version;
201
+ } finally {
202
+ rmSync(d, { recursive: true, force: true });
203
+ }
204
+ }
205
+
206
+ test("mint version bumps a deno.json-only repo (#13)", () => {
207
+ assert.equal(bumpIn("deno.json"), "0.2.0");
208
+ });
209
+
210
+ test("mint version still bumps a jsr.json / package.json repo", () => {
211
+ assert.equal(bumpIn("jsr.json"), "0.2.0");
212
+ assert.equal(bumpIn("package.json"), "0.2.0");
213
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bounded-systems/mint",
3
- "version": "0.4.15",
3
+ "version": "0.5.0",
4
4
  "description": "Deterministic versioning capability — intent files in, signed release out. A seam over semver: own the flow, delegate the arithmetic.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,20 +0,0 @@
1
- name: ci
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- pull_request:
7
-
8
- permissions:
9
- contents: read
10
-
11
- jobs:
12
- test:
13
- runs-on: ubuntu-latest
14
- steps:
15
- - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
16
- - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
17
- with:
18
- node-version: "22"
19
- - run: npm ci --no-audit --no-fund
20
- - run: npm test