@simple-auth-kit/cli 1.2.0 → 1.3.1

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.
package/README.md CHANGED
@@ -31,10 +31,13 @@ without `--force` or `--check`, a file that's changed locally since install prom
31
31
  per file — before it's overwritten; outside a TTY (CI/scripts) it's left alone by default and
32
32
  reported at the end, same as `--force` always applying and `--check` never writing anything.
33
33
 
34
- **Dependencies are installed for you**, shadcn-`add`-style — no separate `npm install` step.
35
- The package manager is auto-detected from a lockfile already in the target directory (npm if
36
- none found); override it with `--pm <npm|pnpm|yarn|bun>`, or skip the install entirely with
37
- `--skip-install` if you'd rather review `package.json` first.
34
+ **Dependencies are installed for you**, shadcn-`add`-style — no separate `npm install` step, and
35
+ version-pinned to match what each combo is actually built and tested against (not just bare
36
+ package names left to resolve to "latest"). The package manager is auto-detected from a lockfile
37
+ already in the target directory; with no lockfile to go on (a brand new directory, most
38
+ commonly) a real terminal is asked which one to use. Override either with
39
+ `--pm <npm|pnpm|yarn|bun>`, or skip the install entirely with `--skip-install` if you'd rather
40
+ review `package.json` first.
38
41
 
39
42
  ## Available combos
40
43
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simple-auth-kit/cli",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "license": "MIT",
5
5
  "description": "simple-auth-kit add <combo> — copies registry source (backend combos, admin consoles, mobile apps) into a consumer repo, shadcn-CLI-style. Nothing is ever installed as a runtime dependency of the consumer.",
6
6
  "bin": {
@@ -65,7 +65,7 @@ variants; not evidence of drift to reconcile.
65
65
 
66
66
  ## `@simple-auth-kit/auth-client`
67
67
 
68
- Both products' `package.json` declare `"@simple-auth-kit/auth-client": "^1.0.0"`, resolved from
68
+ Both products' `package.json` declare `"@simple-auth-kit/auth-client": "^1.0.2"`, resolved from
69
69
  the published package on npm: https://www.npmjs.com/package/@simple-auth-kit/auth-client. A
70
70
  `scaffold`-mode generated app stands alone with a plain `npm install` — no monorepo/pnpm
71
71
  workspace required.
@@ -13,7 +13,7 @@
13
13
  "dependencies": {
14
14
  "@casl/ability": "^7.0.1",
15
15
  "@casl/react": "^7.0.1",
16
- "@simple-auth-kit/auth-client": "^1.0.0",
16
+ "@simple-auth-kit/auth-client": "^1.0.2",
17
17
  "@hookform/resolvers": "^5.2.2",
18
18
  "@tanstack/react-query": "^5.90.12",
19
19
  "@tanstack/react-table": "^8.21.3",
@@ -13,7 +13,7 @@
13
13
  "dependencies": {
14
14
  "@casl/ability": "^7.0.1",
15
15
  "@casl/react": "^7.0.1",
16
- "@simple-auth-kit/auth-client": "^1.0.0",
16
+ "@simple-auth-kit/auth-client": "^1.0.2",
17
17
  "@tanstack/react-query": "^5.90.12",
18
18
  "@tanstack/react-table": "^8.21.3",
19
19
  "class-variance-authority": "^0.7.1",
@@ -13,7 +13,7 @@
13
13
  "dependencies": {
14
14
  "@casl/ability": "^6.7.3",
15
15
  "@casl/react": "^4.0.0",
16
- "@simple-auth-kit/auth-client": "^1.0.0",
16
+ "@simple-auth-kit/auth-client": "^1.0.2",
17
17
  "@fontsource-variable/bricolage-grotesque": "^5.3.0",
18
18
  "@radix-ui/react-label": "^2.1.15",
19
19
  "@radix-ui/react-slot": "^1.3.3",
@@ -13,7 +13,7 @@
13
13
  "dependencies": {
14
14
  "@casl/ability": "^6.7.3",
15
15
  "@casl/react": "^4.0.0",
16
- "@simple-auth-kit/auth-client": "^1.0.0",
16
+ "@simple-auth-kit/auth-client": "^1.0.2",
17
17
  "@fontsource-variable/bricolage-grotesque": "^5.3.0",
18
18
  "@radix-ui/react-label": "^2.1.15",
19
19
  "@radix-ui/react-slot": "^1.3.3",
@@ -12,7 +12,7 @@
12
12
  "typecheck": "tsc --noEmit"
13
13
  },
14
14
  "dependencies": {
15
- "@simple-auth-kit/auth-client": "^1.0.0",
15
+ "@simple-auth-kit/auth-client": "^1.0.2",
16
16
  "@react-native-async-storage/async-storage": "^3.1.1",
17
17
  "@react-native/new-app-screen": "0.86.2",
18
18
  "@react-navigation/native": "^7.3.16",
@@ -12,7 +12,7 @@
12
12
  "typecheck": "tsc --noEmit"
13
13
  },
14
14
  "dependencies": {
15
- "@simple-auth-kit/auth-client": "^1.0.0",
15
+ "@simple-auth-kit/auth-client": "^1.0.2",
16
16
  "@react-native-async-storage/async-storage": "^3.1.1",
17
17
  "@react-native/new-app-screen": "0.86.2",
18
18
  "@react-navigation/native": "^7.3.16",
@@ -12,7 +12,7 @@
12
12
  "typecheck": "tsc --noEmit"
13
13
  },
14
14
  "dependencies": {
15
- "@simple-auth-kit/auth-client": "^1.0.0",
15
+ "@simple-auth-kit/auth-client": "^1.0.2",
16
16
  "@react-native-async-storage/async-storage": "2.2.0",
17
17
  "@react-navigation/native": "^7.3.16",
18
18
  "@react-navigation/native-stack": "^7.18.8",
@@ -12,7 +12,7 @@
12
12
  "typecheck": "tsc --noEmit"
13
13
  },
14
14
  "dependencies": {
15
- "@simple-auth-kit/auth-client": "^1.0.0",
15
+ "@simple-auth-kit/auth-client": "^1.0.2",
16
16
  "@react-native-async-storage/async-storage": "2.2.0",
17
17
  "@react-navigation/native": "^7.3.16",
18
18
  "@react-navigation/native-stack": "^7.18.8",
package/registry.json CHANGED
@@ -2,8 +2,8 @@
2
2
  "core": {
3
3
  "dir": "core",
4
4
  "peerDependencies": [
5
- "argon2",
6
- "jose"
5
+ "argon2@^0.45.1",
6
+ "jose@^6.2.8"
7
7
  ]
8
8
  },
9
9
  "variants": {
@@ -26,18 +26,18 @@
26
26
  "workspaces"
27
27
  ],
28
28
  "peerDependencies": [
29
- "@nestjs/common",
30
- "@nestjs/core",
31
- "@nestjs/platform-express",
32
- "@nestjs/swagger",
33
- "@nestjs/throttler",
34
- "reflect-metadata",
35
- "rxjs",
36
- "express",
37
- "@prisma/client",
38
- "@prisma/adapter-pg",
39
- "prisma",
40
- "@casl/ability"
29
+ "@nestjs/common@^11.1.28",
30
+ "@nestjs/core@^11.1.28",
31
+ "@nestjs/platform-express@^11.1.28",
32
+ "@nestjs/swagger@^11.4.6",
33
+ "@nestjs/throttler@^6.5.0",
34
+ "reflect-metadata@^0.2.2",
35
+ "rxjs@^7.8.2",
36
+ "express@^5.2.1",
37
+ "@prisma/client@^7.9.1",
38
+ "@prisma/adapter-pg@^7.9.1",
39
+ "prisma@^7.9.1",
40
+ "@casl/ability@^7.0.1"
41
41
  ],
42
42
  "postInstall": [
43
43
  "Set DATABASE_URL in .env (see .env.example)",
@@ -66,17 +66,17 @@
66
66
  "workspaces"
67
67
  ],
68
68
  "peerDependencies": [
69
- "@nestjs/common",
70
- "@nestjs/core",
71
- "@nestjs/platform-express",
72
- "@nestjs/swagger",
73
- "reflect-metadata",
74
- "rxjs",
75
- "express",
76
- "drizzle-orm",
77
- "pg",
78
- "drizzle-kit",
79
- "@casl/ability"
69
+ "@nestjs/common@^11.1.28",
70
+ "@nestjs/core@^11.1.28",
71
+ "@nestjs/platform-express@^11.1.28",
72
+ "@nestjs/swagger@^11.4.6",
73
+ "reflect-metadata@^0.2.2",
74
+ "rxjs@^7.8.2",
75
+ "express@^5.2.1",
76
+ "drizzle-orm@^0.45.2",
77
+ "pg@^8.23.0",
78
+ "drizzle-kit@^0.31.10",
79
+ "@casl/ability@^7.0.1"
80
80
  ],
81
81
  "postInstall": [
82
82
  "Set DATABASE_URL in .env (see .env.example)",
@@ -104,12 +104,12 @@
104
104
  "workspaces"
105
105
  ],
106
106
  "peerDependencies": [
107
- "express",
108
- "@prisma/client",
109
- "@prisma/adapter-pg",
110
- "prisma",
111
- "swagger-ui-express",
112
- "@casl/ability"
107
+ "express@^5.2.1",
108
+ "@prisma/client@^7.9.1",
109
+ "@prisma/adapter-pg@^7.9.1",
110
+ "prisma@^7.9.1",
111
+ "swagger-ui-express@^5.0.1",
112
+ "@casl/ability@^7.0.1"
113
113
  ],
114
114
  "postInstall": [
115
115
  "Set DATABASE_URL in .env (see .env.example)",
@@ -138,12 +138,12 @@
138
138
  "workspaces"
139
139
  ],
140
140
  "peerDependencies": [
141
- "express",
142
- "drizzle-orm",
143
- "pg",
144
- "drizzle-kit",
145
- "swagger-ui-express",
146
- "@casl/ability"
141
+ "express@^5.2.1",
142
+ "drizzle-orm@^0.45.2",
143
+ "pg@^8.23.0",
144
+ "drizzle-kit@^0.31.10",
145
+ "swagger-ui-express@^5.0.1",
146
+ "@casl/ability@^7.0.1"
147
147
  ],
148
148
  "postInstall": [
149
149
  "Set DATABASE_URL in .env (see .env.example)",
@@ -183,18 +183,41 @@ async function resolveForcePaths(runDry: () => Promise<{ result: CopyResult }>,
183
183
  const PACKAGE_MANAGERS = ["npm", "pnpm", "yarn", "bun"] as const;
184
184
  type PackageManager = (typeof PACKAGE_MANAGERS)[number];
185
185
 
186
- /** Detected from whichever lockfile is already sitting in targetRoot — npm if none match,
187
- * matching what a fresh `npm install`-first project would have. Overridden by `--pm` when
188
- * given, so e.g. a first-ever install (no lockfile yet to detect from) can still pick pnpm. */
189
- function detectPackageManager(targetRoot: string, flags: Record<string, string | true>): PackageManager {
186
+ /** Detected from whichever lockfile is already sitting in targetRoot — unambiguous, so used
187
+ * without asking. Returns null when nothing on disk says which tool to use and `--pm` wasn't
188
+ * given either, leaving it to the caller to ask (interactive) or default (non-interactive). */
189
+ function detectPackageManagerFromLockfile(targetRoot: string): PackageManager | null {
190
+ if (existsSync(join(targetRoot, "pnpm-lock.yaml"))) return "pnpm";
191
+ if (existsSync(join(targetRoot, "yarn.lock"))) return "yarn";
192
+ if (existsSync(join(targetRoot, "bun.lockb")) || existsSync(join(targetRoot, "bun.lock"))) return "bun";
193
+ if (existsSync(join(targetRoot, "package-lock.json"))) return "npm";
194
+ return null;
195
+ }
196
+
197
+ /** `--pm` wins outright. Otherwise: a lockfile already in targetRoot is unambiguous, so it's used
198
+ * silently. Only when neither says anything — most commonly a scaffold install into an empty
199
+ * directory — and this is a real terminal do we ask; a non-interactive run in that same situation
200
+ * still falls back to npm rather than blocking. */
201
+ async function resolvePackageManager(targetRoot: string, flags: Record<string, string | true>): Promise<PackageManager> {
190
202
  const requested = flagString(flags.pm);
191
203
  if (requested) {
192
204
  if ((PACKAGE_MANAGERS as readonly string[]).includes(requested)) return requested as PackageManager;
193
205
  console.error(`--pm "${requested}" isn't one of ${PACKAGE_MANAGERS.join(", ")} — falling back to auto-detection.`);
194
206
  }
195
- if (existsSync(join(targetRoot, "pnpm-lock.yaml"))) return "pnpm";
196
- if (existsSync(join(targetRoot, "yarn.lock"))) return "yarn";
197
- if (existsSync(join(targetRoot, "bun.lockb")) || existsSync(join(targetRoot, "bun.lock"))) return "bun";
207
+
208
+ const detected = detectPackageManagerFromLockfile(targetRoot);
209
+ if (detected) return detected;
210
+
211
+ if (isTTY()) {
212
+ const picked = await ask<PackageManager>({
213
+ type: "select",
214
+ name: "pm",
215
+ message: "Which package manager?",
216
+ choices: PACKAGE_MANAGERS.map((value) => ({ title: value, value })),
217
+ initial: 0,
218
+ });
219
+ if (picked) return picked;
220
+ }
198
221
  return "npm";
199
222
  }
200
223
 
@@ -204,12 +227,12 @@ function detectPackageManager(targetRoot: string, flags: Record<string, string |
204
227
  * package.json" (the scaffold-mode case — dependencies are already declared, nothing to name).
205
228
  * Skipped entirely under `--skip-install`, or when `deps` is non-empty but there's nothing new
206
229
  * to add (an update that touched no files has nothing worth re-installing for). `--pm
207
- * <npm|pnpm|yarn|bun>` picks the tool explicitly instead of auto-detecting it from a lockfile.
230
+ * <npm|pnpm|yarn|bun>` picks the tool explicitly instead of auto-detecting/asking.
208
231
  */
209
- function installDependencies(targetRoot: string, deps: string[], flags: Record<string, string | true>): void {
232
+ async function installDependencies(targetRoot: string, deps: string[], flags: Record<string, string | true>): Promise<void> {
210
233
  if (flags["skip-install"] === true) return;
211
234
 
212
- const pm = detectPackageManager(targetRoot, flags);
235
+ const pm = await resolvePackageManager(targetRoot, flags);
213
236
  // "install everything in package.json" (zero deps named) is the same bare verb across all
214
237
  // four; naming specific packages is "install <pkgs>" for npm, "add <pkgs>" for the others.
215
238
  const args = deps.length ? [pm === "npm" ? "install" : "add", ...deps] : ["install"];
@@ -218,6 +241,15 @@ function installDependencies(targetRoot: string, deps: string[], flags: Record<s
218
241
  const result = spawnSync(pm, args, { cwd: targetRoot, stdio: "inherit" });
219
242
  if (result.status !== 0) {
220
243
  console.error(`\n${pm} install exited with an error — run it yourself: cd ${targetRoot} && ${pm} ${args.join(" ")}`);
244
+ if (pm === "pnpm") {
245
+ // pnpm's default security posture blocks postinstall/preinstall scripts from any
246
+ // dependency it hasn't seen approved before — argon2 (native addon build) and prisma
247
+ // (downloads its query-engine binary) both need theirs to run, so this is the single most
248
+ // common reason a combo install fails under pnpm specifically (ERR_PNPM_IGNORED_BUILDS).
249
+ console.error(
250
+ `If that was "Ignored build scripts": run "pnpm approve-builds" in ${targetRoot} (interactive — lets you pick which dependencies may run install scripts, argon2/prisma included; "pnpm approve-builds --all" approves everything pending without prompting), then re-run "npx @simple-auth-kit/cli update".`,
251
+ );
252
+ }
221
253
  }
222
254
  }
223
255
 
@@ -435,7 +467,7 @@ async function installMerge(comboName: string, combo: ComboEntry, variant: strin
435
467
  const peerDeps = [...new Set([...registry.core.peerDependencies, ...combo.peerDependencies])];
436
468
  console.log(`\nPeer dependencies: ${peerDeps.join(" ")}`);
437
469
  if (result.updated.length) {
438
- installDependencies(targetRoot, peerDeps, flags);
470
+ await installDependencies(targetRoot, peerDeps, flags);
439
471
  } else if (flags["skip-install"] !== true) {
440
472
  console.log(`(nothing changed this run — skipping install)`);
441
473
  }
@@ -507,7 +539,7 @@ async function installScaffold(comboName: string, combo: ComboEntry, variant: st
507
539
  if (result.updated.length) {
508
540
  // Dependencies are already declared in package.json — no specific packages to name, just
509
541
  // "install whatever's there" (installDependencies with an empty list does exactly that).
510
- installDependencies(targetRoot, [], flags);
542
+ await installDependencies(targetRoot, [], flags);
511
543
  } else if (flags["skip-install"] !== true) {
512
544
  console.log(`\n(nothing changed this run — skipping install)`);
513
545
  }