@faable/auth-sdk 2.6.0 → 2.6.2

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.
@@ -31,3 +31,8 @@ jobs:
31
31
  - run: npm run release
32
32
  env:
33
33
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
34
+ # Proves the RESULT: downloads what was just published and fails if the
35
+ # sentinels survived. test:pack checks they are present before the
36
+ # release; only this catches something rebuilding dist/ in between —
37
+ # which is exactly how 2.6.0 shipped as `auth-sdk/0.0.0-dev`.
38
+ - run: npm run verify:published
package/dist/version.js CHANGED
@@ -9,13 +9,13 @@
9
9
  // login pages could not import the SDK at all. Same pattern as auth-js.
10
10
  //
11
11
  // The sentinels MUST stay byte-identical to the `from` values in `.releaserc`.
12
- export const version = "0.0.0-dev";
12
+ export const version = "2.6.2";
13
13
  // Short git SHA of the released commit. The version dates a build; this names
14
14
  // the exact tree, so a canonical log line or an audit entry leads straight to
15
15
  // `git show <sha>`. Deliberately NOT hex: an unreleased build (dev, a local
16
16
  // link) cannot be mistaken for a real commit — auth only records values that
17
17
  // look like a SHA, and this one never will.
18
- export const commit = "0000000dev";
18
+ export const commit = "6d035ad";
19
19
  // What this SDK writes in `x-faable-client`. Exported for a consumer that
20
20
  // builds a strategy on its own (outside `FaableAuthApi`) and still wants the
21
21
  // token request attributed — a bare `authClientCredentials` stamps only what
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faable/auth-sdk",
3
- "version": "2.6.0",
3
+ "version": "2.6.2",
4
4
  "author": "Marc Pomar <marc@faable.com>",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -30,10 +30,10 @@
30
30
  "gentypes": "openapi-typescript ${AUTH_OPENAPI:-spec/openapi.json} -o src/api/types.ts && node scripts/gen-client.mjs",
31
31
  "build": "rimraf dist && tsc",
32
32
  "prebuild": "npm run gentypes",
33
- "prepublishOnly": "npm run build",
34
33
  "test": "ava",
35
34
  "release": "semantic-release",
36
- "test:pack": "node tests/pack/verify-pack.mjs"
35
+ "test:pack": "node tests/pack/verify-pack.mjs",
36
+ "verify:published": "node tests/pack/verify-published.mjs"
37
37
  },
38
38
  "resolutions": {
39
39
  "npm/chalk": "^4.1.2"
package/spec/openapi.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "info": {
4
4
  "title": "@faablecloud/auth",
5
5
  "description": "Auth Platform made by Faable. Manage Users and Roles",
6
- "version": "2.10.0",
6
+ "version": "2.11.0",
7
7
  "license": {
8
8
  "name": "private",
9
9
  "url": "https://faable.com/docs/platform/privacy-policy"
@@ -30,6 +30,7 @@ console.log("→ Building dist/");
30
30
  run("npm run build", { cwd: ROOT });
31
31
 
32
32
  // 1. Sentinels, verbatim, straight from .releaserc so the two cannot drift.
33
+ const pkgScripts = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8")).scripts ?? {};
33
34
  const releaserc = JSON.parse(readFileSync(join(ROOT, ".releaserc"), "utf8"));
34
35
  const replacePlugin = releaserc.plugins.find(
35
36
  (p) => Array.isArray(p) && p[0] === "semantic-release-replace-plugin",
@@ -43,6 +44,23 @@ for (const { from } of replacePlugin[1].replacements) {
43
44
  }
44
45
  console.log("✓ sentinels present in dist/version.js");
45
46
 
47
+ // The sentinels being here is only half the story: anything that rebuilds
48
+ // dist/ AFTER semantic-release's replacement and BEFORE the upload undoes it.
49
+ // `npm pack` fires `prepack`, never `prepublishOnly`, so this guard cannot
50
+ // observe that hook — it refuses to let one exist instead. (2.6.0 shipped
51
+ // anonymous exactly this way.) The release workflow builds explicitly, so no
52
+ // publish-time rebuild hook is needed.
53
+ const REBUILD_HOOKS = ["prepublishOnly", "prepublish", "prepack", "prepare"];
54
+ const offending = REBUILD_HOOKS.filter((hook) => pkgScripts[hook]);
55
+ if (offending.length) {
56
+ fail(
57
+ `package.json defines ${offending.join(", ")} — npm runs these around publish, ` +
58
+ "and a rebuild there would overwrite the version semantic-release just injected. " +
59
+ "Build in the workflow instead.",
60
+ );
61
+ }
62
+ console.log("✓ no publish-time rebuild hook");
63
+
46
64
  // 2. Browser-safety: no node: specifiers anywhere in the published JS.
47
65
  const jsFiles = (dir) =>
48
66
  readdirSync(dir, { withFileTypes: true }).flatMap((e) =>
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ // Post-publish guard: download the version that was just published and fail if
3
+ // it still carries the release sentinels.
4
+ //
5
+ // `verify-pack.mjs` checks the OPPOSITE — that the sentinels ARE in dist/, so
6
+ // the replacement has something to hit. Both are needed, and only this one
7
+ // proves the result: 2.6.0 shipped anonymous because `prepublishOnly` ran
8
+ // `rimraf dist && tsc` between the replacement and the upload, and `npm pack`
9
+ // (which the pre-publish guard uses) never fires that hook.
10
+ import { execSync } from "node:child_process";
11
+ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import { join, resolve } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+
16
+ const ROOT = resolve(fileURLToPath(import.meta.url), "../../..");
17
+ const pkg = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf8"));
18
+ const version = pkg.version;
19
+
20
+ // semantic-release writes the released version into package.json during
21
+ // `prepare`. Still the dev placeholder ⇒ no release was cut on this run.
22
+ if (version === "0.0.0-dev") {
23
+ console.log("→ No release cut on this run; nothing to verify.");
24
+ process.exit(0);
25
+ }
26
+
27
+ const sentinels = JSON.parse(readFileSync(join(ROOT, ".releaserc"), "utf8"))
28
+ .plugins.find((p) => Array.isArray(p) && p[0] === "semantic-release-replace-plugin")[1]
29
+ .replacements.map((r) => r.from);
30
+
31
+ const tmp = mkdtempSync(join(tmpdir(), "faable-auth-sdk-published-"));
32
+ try {
33
+ // The registry needs a moment to serve a brand-new version.
34
+ let tarball;
35
+ for (let attempt = 1; attempt <= 10; attempt++) {
36
+ try {
37
+ tarball = execSync(`npm pack ${pkg.name}@${version} --silent`, {
38
+ cwd: tmp,
39
+ encoding: "utf8",
40
+ }).trim();
41
+ break;
42
+ } catch {
43
+ console.log(`→ ${pkg.name}@${version} not served yet (attempt ${attempt}/10)`);
44
+ execSync("sleep 10");
45
+ }
46
+ }
47
+ if (!tarball) {
48
+ console.error(`✖ ${pkg.name}@${version} never became available on the registry`);
49
+ process.exit(1);
50
+ }
51
+
52
+ execSync(`tar xzf ${tarball}`, { cwd: tmp });
53
+ const published = readFileSync(join(tmp, "package/dist/version.js"), "utf8");
54
+ for (const sentinel of sentinels) {
55
+ if (published.includes(sentinel)) {
56
+ console.error(
57
+ `✖ ${pkg.name}@${version} was published with the sentinel "${sentinel}" intact — ` +
58
+ "every consumer will report as that. Something between the replacement and the " +
59
+ "upload rebuilt dist/ (a `prepublishOnly`/`prepack` hook is the usual culprit).",
60
+ );
61
+ process.exit(1);
62
+ }
63
+ }
64
+ if (!published.includes(version)) {
65
+ console.error(`✖ ${pkg.name}@${version} does not carry its own version in dist/version.js`);
66
+ process.exit(1);
67
+ }
68
+ console.log(`✓ ${pkg.name}@${version} reports itself correctly on the wire`);
69
+ } finally {
70
+ rmSync(tmp, { recursive: true, force: true });
71
+ }