@onesub/cli 0.1.41 → 0.1.43

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,24 @@
1
+ /**
2
+ * The `onesub init` templates must scaffold a project on a current server.
3
+ *
4
+ * ## Why this test exists
5
+ *
6
+ * `templates/package.json` pinned `@onesub/server: ^0.7.0` while the published server
7
+ * was on `0.27.0`. A caret range on a `0.x` version admits only patches — `^0.7.0`
8
+ * resolves to `0.7.x` — so every `onesub init` scaffolded a project **twenty minors
9
+ * behind**, missing the log formatter, the conditional webhook mount and the webhook
10
+ * authentication requirement. It had been that way since the express
11
+ * peerDependencies move.
12
+ *
13
+ * Nothing caught it, and that is the interesting part. Changesets bumps *workspace*
14
+ * versions; this file is copied template content, not a workspace, so no release step
15
+ * touches it. `docs:check` validates links and catalogues, not dependency ranges. The
16
+ * only mechanism was remembering — and twenty releases is a fair sample of how well
17
+ * that works.
18
+ *
19
+ * So the fix is not just the bump. Releasing a server minor now has to bump this pin
20
+ * too, and this test is what says so, at the release that introduces the drift rather
21
+ * than a year later.
22
+ */
23
+ export {};
24
+ //# sourceMappingURL=templates.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/templates.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * The `onesub init` templates must scaffold a project on a current server.
3
+ *
4
+ * ## Why this test exists
5
+ *
6
+ * `templates/package.json` pinned `@onesub/server: ^0.7.0` while the published server
7
+ * was on `0.27.0`. A caret range on a `0.x` version admits only patches — `^0.7.0`
8
+ * resolves to `0.7.x` — so every `onesub init` scaffolded a project **twenty minors
9
+ * behind**, missing the log formatter, the conditional webhook mount and the webhook
10
+ * authentication requirement. It had been that way since the express
11
+ * peerDependencies move.
12
+ *
13
+ * Nothing caught it, and that is the interesting part. Changesets bumps *workspace*
14
+ * versions; this file is copied template content, not a workspace, so no release step
15
+ * touches it. `docs:check` validates links and catalogues, not dependency ranges. The
16
+ * only mechanism was remembering — and twenty releases is a fair sample of how well
17
+ * that works.
18
+ *
19
+ * So the fix is not just the bump. Releasing a server minor now has to bump this pin
20
+ * too, and this test is what says so, at the release that introduces the drift rather
21
+ * than a year later.
22
+ */
23
+ import { describe, expect, it } from 'vitest';
24
+ import { readFileSync } from 'fs';
25
+ import { dirname, join } from 'path';
26
+ import { fileURLToPath } from 'url';
27
+ const CLI = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
28
+ const REPO = dirname(dirname(CLI));
29
+ function readJson(...parts) {
30
+ return JSON.parse(readFileSync(join(...parts), 'utf8'));
31
+ }
32
+ /**
33
+ * Does a caret range admit this version?
34
+ *
35
+ * Hand-rolled rather than pulling in `semver`, because the CLI has no runtime
36
+ * dependency on it and adding one to satisfy a test is the wrong trade. Only the
37
+ * shapes this repo actually writes are handled: `^x.y.z` and an exact version. A
38
+ * range this cannot parse throws rather than silently passing — a test that quietly
39
+ * approves an unrecognised pin is worse than no test.
40
+ */
41
+ function caretAdmits(range, version) {
42
+ const v = version.split('.').map(Number);
43
+ if (v.length !== 3 || v.some(Number.isNaN))
44
+ throw new Error(`unparseable version: ${version}`);
45
+ if (/^\d+\.\d+\.\d+$/.test(range))
46
+ return range === version;
47
+ const m = /^\^(\d+)\.(\d+)\.(\d+)$/.exec(range);
48
+ if (!m)
49
+ throw new Error(`unhandled range shape, teach this test about it: ${range}`);
50
+ const [lo0, lo1, lo2] = [Number(m[1]), Number(m[2]), Number(m[3])];
51
+ const [hi0, hi1, hi2] = v;
52
+ // Below the floor is out, whatever the majors do.
53
+ if (hi0 < lo0)
54
+ return false;
55
+ if (hi0 === lo0 && (hi1 < lo1 || (hi1 === lo1 && hi2 < lo2)))
56
+ return false;
57
+ // npm treats a 0.x caret as minor-locked: ^0.7.0 is >=0.7.0 <0.8.0. That is the
58
+ // whole reason the pin went stale, so it is the case worth being explicit about.
59
+ if (lo0 === 0)
60
+ return hi0 === 0 && hi1 === lo1;
61
+ return hi0 === lo0;
62
+ }
63
+ describe('caretAdmits', () => {
64
+ // The helper decides whether the assertion below means anything, so it is tested
65
+ // before it is trusted.
66
+ it('locks a 0.x caret to its minor', () => {
67
+ expect(caretAdmits('^0.7.0', '0.7.5')).toBe(true);
68
+ expect(caretAdmits('^0.7.0', '0.8.0')).toBe(false);
69
+ expect(caretAdmits('^0.7.0', '0.27.0')).toBe(false);
70
+ expect(caretAdmits('^0.27.0', '0.27.1')).toBe(true);
71
+ });
72
+ it('allows minors on a stable major', () => {
73
+ expect(caretAdmits('^5.2.1', '5.9.0')).toBe(true);
74
+ expect(caretAdmits('^5.2.1', '6.0.0')).toBe(false);
75
+ expect(caretAdmits('^5.2.1', '5.2.0')).toBe(false);
76
+ });
77
+ it('matches an exact pin exactly', () => {
78
+ expect(caretAdmits('0.27.0', '0.27.0')).toBe(true);
79
+ expect(caretAdmits('0.27.0', '0.27.1')).toBe(false);
80
+ });
81
+ it('refuses to guess at a range shape it does not know', () => {
82
+ expect(() => caretAdmits('>=0.27 <1', '0.27.0')).toThrow(/unhandled range shape/);
83
+ expect(() => caretAdmits('~0.27.0', '0.27.0')).toThrow(/unhandled range shape/);
84
+ });
85
+ });
86
+ describe('onesub init templates', () => {
87
+ const serverVersion = readJson(REPO, 'packages/server/package.json').version;
88
+ it('scaffolds a project that can install the current server', () => {
89
+ const range = readJson(CLI, 'templates/package.json')
90
+ .dependencies['@onesub/server'];
91
+ expect(range).toBeDefined();
92
+ expect(caretAdmits(range, serverVersion), `templates/package.json pins @onesub/server ${range}, which cannot install ` +
93
+ `${serverVersion}. On a 0.x version a caret admits only patches, so bump the ` +
94
+ `pin in the same change as the server release.`).toBe(true);
95
+ });
96
+ it('keeps every template the CLI copies present', () => {
97
+ // A missing template makes `onesub init` produce a half-scaffolded project, and
98
+ // the failure surfaces at the user rather than here.
99
+ const src = readFileSync(join(CLI, 'src/index.ts'), 'utf8');
100
+ const declared = [...src.matchAll(/\{\s*src:\s*'([^']+)'/g)].map((m) => m[1]);
101
+ expect(declared.length).toBeGreaterThan(3);
102
+ for (const file of declared) {
103
+ expect(() => readFileSync(join(CLI, 'templates', file), 'utf8')).not.toThrow();
104
+ }
105
+ });
106
+ });
107
+ //# sourceMappingURL=templates.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.test.js","sourceRoot":"","sources":["../../src/__tests__/templates.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onesub/cli",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
4
4
  "description": "CLI scaffolding for onesub — `npx onesub init` bootstraps a server + Expo app.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -35,8 +35,8 @@
35
35
  },
36
36
  "license": "MIT",
37
37
  "dependencies": {
38
- "@onesub/server": "0.26.0",
39
- "@onesub/shared": "0.15.1",
38
+ "@onesub/server": "0.27.0",
39
+ "@onesub/shared": "0.16.0",
40
40
  "express": "^5.2.1"
41
41
  },
42
42
  "devDependencies": {
@@ -7,8 +7,12 @@ APPLE_SHARED_SECRET=your_shared_secret
7
7
  # Service account JSON from Google Cloud Console (single-line)
8
8
  GOOGLE_PACKAGE_NAME=com.yourapp.id
9
9
  GOOGLE_SERVICE_ACCOUNT_KEY={"type":"service_account","project_id":"..."}
10
- # Optional: push endpoint URL for Pub/Sub JWT verification
10
+ # REQUIRED IN PRODUCTION: the Pub/Sub push endpoint URL, verified as the OIDC `aud`.
11
+ # Without it the Google webhook cannot attribute a request to Google and answers 401
12
+ # when NODE_ENV=production. Leave it unset for local development.
11
13
  # GOOGLE_PUSH_AUDIENCE=https://api.yourapp.com/onesub/webhook/google
14
+ # Restricts push tokens to your Pub/Sub service account; set it with the audience.
15
+ # GOOGLE_PUSH_SERVICE_ACCOUNT_EMAIL=push@your-project.iam.gserviceaccount.com
12
16
 
13
17
  # ── Database ────────────────────────────────────────────
14
18
  # Uncomment for Postgres; leave unset for in-memory (dev only)
@@ -8,7 +8,7 @@
8
8
  "start": "tsx server.ts"
9
9
  },
10
10
  "dependencies": {
11
- "@onesub/server": "^0.7.0",
11
+ "@onesub/server": "^0.27.0",
12
12
  "dotenv": "^16.4.5",
13
13
  "express": "^5.2.1",
14
14
  "pg": "^8.13.0"
@@ -28,7 +28,10 @@ app.use(
28
28
  ? {
29
29
  packageName: process.env.GOOGLE_PACKAGE_NAME,
30
30
  serviceAccountKey: process.env.GOOGLE_SERVICE_ACCOUNT_KEY,
31
+ // Required in production: without it the RTDN webhook cannot attribute a
32
+ // request to Google and refuses it. See docs/SECURITY.md.
31
33
  pushAudience: process.env.GOOGLE_PUSH_AUDIENCE,
34
+ pushServiceAccountEmail: process.env.GOOGLE_PUSH_SERVICE_ACCOUNT_EMAIL,
32
35
  }
33
36
  : undefined,
34
37
  database: { url: dbUrl ?? '' },