@simple-auth-kit/cli 1.1.0 → 1.3.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.
- package/README.md +32 -11
- package/package.json +1 -1
- package/registry.json +37 -37
- package/simple-auth-kit.ts +75 -38
package/README.md
CHANGED
|
@@ -22,7 +22,8 @@ npx @simple-auth-kit/cli add <combo> [--workspaces] # install a specific combo
|
|
|
22
22
|
npx @simple-auth-kit/cli add # guided picker, same flow as init (no config write)
|
|
23
23
|
npx @simple-auth-kit/cli update [--check] # re-installs whatever add last recorded — no args needed
|
|
24
24
|
npx @simple-auth-kit/cli diff # shows the actual content diff for every file that differs from the registry (api combos only)
|
|
25
|
-
npx @simple-auth-kit/cli #
|
|
25
|
+
npx @simple-auth-kit/cli # bare, in a real terminal: same guided picker as `add`
|
|
26
|
+
npx @simple-auth-kit/cli --help # full command/flag reference + available combos
|
|
26
27
|
```
|
|
27
28
|
|
|
28
29
|
`--into <path>` targets any directory (defaults to the current one). In a real terminal,
|
|
@@ -30,20 +31,40 @@ without `--force` or `--check`, a file that's changed locally since install prom
|
|
|
30
31
|
per file — before it's overwritten; outside a TTY (CI/scripts) it's left alone by default and
|
|
31
32
|
reported at the end, same as `--force` always applying and `--check` never writing anything.
|
|
32
33
|
|
|
33
|
-
**Dependencies are installed for you**, shadcn-`add`-style — no separate `npm install` step
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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.
|
|
37
41
|
|
|
38
42
|
## Available combos
|
|
39
43
|
|
|
40
|
-
|
|
41
|
-
|---|---|---|
|
|
42
|
-
| `api` | `nestjs-prisma`, `nestjs-drizzle`, `express-prisma`, `express-drizzle` | merged into `src/lib/auth` of an existing project (the Prisma/Drizzle config + schema land at the project root instead — see below) |
|
|
43
|
-
| `admin` | `admin-nextjs`, `admin-react` | a whole new standalone app, scaffolded at the target (refuses a non-empty target unless `--force`) |
|
|
44
|
-
| `mobile` | `mobile-expo`, `mobile-bare-rn` | a whole new standalone app, scaffolded at the target (same non-empty-target rule) |
|
|
44
|
+
Add `--workspaces` to any of these for the workspaces variant.
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
**`api`** — merged into `src/lib/auth` of an existing project (the Prisma/Drizzle config +
|
|
47
|
+
schema land at the project root instead — see below):
|
|
48
|
+
```bash
|
|
49
|
+
npx @simple-auth-kit/cli add nestjs-prisma --into .
|
|
50
|
+
npx @simple-auth-kit/cli add nestjs-drizzle --into .
|
|
51
|
+
npx @simple-auth-kit/cli add express-prisma --into .
|
|
52
|
+
npx @simple-auth-kit/cli add express-drizzle --into .
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**`admin`** — a whole new standalone app, scaffolded at the target (refuses a non-empty target
|
|
56
|
+
unless `--force`):
|
|
57
|
+
```bash
|
|
58
|
+
npx @simple-auth-kit/cli add admin-nextjs --into ./admin
|
|
59
|
+
npx @simple-auth-kit/cli add admin-react --into ./admin
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**`mobile`** — a whole new standalone app, scaffolded at the target (same non-empty-target
|
|
63
|
+
rule):
|
|
64
|
+
```bash
|
|
65
|
+
npx @simple-auth-kit/cli add mobile-expo --into ./mobile
|
|
66
|
+
npx @simple-auth-kit/cli add mobile-bare-rn --into ./mobile
|
|
67
|
+
```
|
|
47
68
|
|
|
48
69
|
## Your own database models are safe
|
|
49
70
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simple-auth-kit/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
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": {
|
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)",
|
package/simple-auth-kit.ts
CHANGED
|
@@ -43,7 +43,7 @@ const KIND_LABELS: Record<Kind, string> = { api: "API (backend)", admin: "Admin
|
|
|
43
43
|
const KIND_FRAMEWORK_NOUN: Record<Kind, string> = { api: "API stack", admin: "admin framework", mobile: "mobile framework" };
|
|
44
44
|
|
|
45
45
|
/** Flags that take no value. Everything else consumes the next argv entry. */
|
|
46
|
-
const BOOLEAN_FLAGS = new Set(["workspaces", "force", "check", "config-only", "skip-install"]);
|
|
46
|
+
const BOOLEAN_FLAGS = new Set(["workspaces", "force", "check", "config-only", "skip-install", "help"]);
|
|
47
47
|
|
|
48
48
|
interface SimpleAuthKitConfig {
|
|
49
49
|
path: string;
|
|
@@ -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 —
|
|
187
|
-
*
|
|
188
|
-
* given,
|
|
189
|
-
function
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
if (
|
|
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
|
|
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 =
|
|
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"];
|
|
@@ -435,7 +458,7 @@ async function installMerge(comboName: string, combo: ComboEntry, variant: strin
|
|
|
435
458
|
const peerDeps = [...new Set([...registry.core.peerDependencies, ...combo.peerDependencies])];
|
|
436
459
|
console.log(`\nPeer dependencies: ${peerDeps.join(" ")}`);
|
|
437
460
|
if (result.updated.length) {
|
|
438
|
-
installDependencies(targetRoot, peerDeps, flags);
|
|
461
|
+
await installDependencies(targetRoot, peerDeps, flags);
|
|
439
462
|
} else if (flags["skip-install"] !== true) {
|
|
440
463
|
console.log(`(nothing changed this run — skipping install)`);
|
|
441
464
|
}
|
|
@@ -507,7 +530,7 @@ async function installScaffold(comboName: string, combo: ComboEntry, variant: st
|
|
|
507
530
|
if (result.updated.length) {
|
|
508
531
|
// Dependencies are already declared in package.json — no specific packages to name, just
|
|
509
532
|
// "install whatever's there" (installDependencies with an empty list does exactly that).
|
|
510
|
-
installDependencies(targetRoot, [], flags);
|
|
533
|
+
await installDependencies(targetRoot, [], flags);
|
|
511
534
|
} else if (flags["skip-install"] !== true) {
|
|
512
535
|
console.log(`\n(nothing changed this run — skipping install)`);
|
|
513
536
|
}
|
|
@@ -785,10 +808,47 @@ async function cmdDiff(targetRoot: string) {
|
|
|
785
808
|
}
|
|
786
809
|
}
|
|
787
810
|
|
|
811
|
+
async function printUsage(exitCode: number) {
|
|
812
|
+
const registry = await loadRegistry();
|
|
813
|
+
console.log("Usage: simple-auth-kit <init|add|update|diff> [...]");
|
|
814
|
+
console.log(` init [--config-only] [--kind ...] [--into <path>] (fresh setup: guided "what do you need" picker, like bare "add")`);
|
|
815
|
+
console.log(` --config-only: just write .simple-auth-kit.json and stop, no install`);
|
|
816
|
+
console.log(` add <combo> [--workspaces] [--force] [--skip-install] [--into <path>] [--path <dir>] [--alias <alias>]`);
|
|
817
|
+
console.log(` add [--kind api,admin,mobile] [--framework <name>,...] [--workspaces] [--into <path>] [--name <appName>]`);
|
|
818
|
+
console.log(` (bare "add", or "add" with --kind but no combo, launches a guided prompt for whatever's missing)`);
|
|
819
|
+
console.log(` update [--check] [--force] [--skip-install] [--into <path>] (re-installs whatever combo+variant auth.lock.json already records)`);
|
|
820
|
+
console.log(` --check: report what would change, without writing anything (merge-mode combos only)`);
|
|
821
|
+
console.log(` diff [--into <path>] (shows the actual content diff for every tracked file that differs from the current registry — read-only; merge-mode combos only)`);
|
|
822
|
+
console.log(` In a TTY, without --force or --check: a file changed locally since install prompts to overwrite, per file.`);
|
|
823
|
+
console.log(` --skip-install: don't run the package manager after copying files — the default is to install for you, shadcn-\`add\`-style.`);
|
|
824
|
+
console.log(` --pm <npm|pnpm|yarn|bun>: which package manager to install with — default: detected from a lockfile in the target directory, npm if none found.`);
|
|
825
|
+
console.log(`\nAvailable combos:`);
|
|
826
|
+
for (const kind of ["api", "admin", "mobile"] as Kind[]) {
|
|
827
|
+
const names = combosByKind(registry, kind).map(([name]) => name);
|
|
828
|
+
if (names.length) console.log(` ${KIND_LABELS[kind]}: ${names.join(", ")}`);
|
|
829
|
+
}
|
|
830
|
+
console.log(`\nVariants (choose one at install time):`);
|
|
831
|
+
for (const [name, variant] of Object.entries(registry.variants)) {
|
|
832
|
+
console.log(` ${name}${variant.flag ? ` (${variant.flag})` : " (default)"} — ${variant.description}`);
|
|
833
|
+
}
|
|
834
|
+
process.exitCode = exitCode;
|
|
835
|
+
}
|
|
836
|
+
|
|
788
837
|
async function main() {
|
|
789
838
|
const { command, positional, flags } = parseArgs(process.argv.slice(2));
|
|
790
839
|
const targetRoot = resolve(process.cwd(), flagString(flags.into) ?? ".");
|
|
791
840
|
|
|
841
|
+
if (command === "--help" || command === "-h" || command === "help" || flags.help === true) {
|
|
842
|
+
await printUsage(0);
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
if (!command && isTTY()) {
|
|
846
|
+
// Bare `npx @simple-auth-kit/cli` in a real terminal — go straight to the guided picker
|
|
847
|
+
// (same flow as `add` with no combo) instead of just printing help text.
|
|
848
|
+
await cmdCreate(targetRoot, flags);
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
|
|
792
852
|
switch (command) {
|
|
793
853
|
case "init":
|
|
794
854
|
await cmdInit(targetRoot, flags);
|
|
@@ -809,31 +869,8 @@ async function main() {
|
|
|
809
869
|
case "diff":
|
|
810
870
|
await cmdDiff(targetRoot);
|
|
811
871
|
break;
|
|
812
|
-
default:
|
|
813
|
-
|
|
814
|
-
console.log("Usage: simple-auth-kit <init|add|update|diff> [...]");
|
|
815
|
-
console.log(` init [--config-only] [--kind ...] [--into <path>] (fresh setup: guided "what do you need" picker, like bare "add")`);
|
|
816
|
-
console.log(` --config-only: just write .simple-auth-kit.json and stop, no install`);
|
|
817
|
-
console.log(` add <combo> [--workspaces] [--force] [--skip-install] [--into <path>] [--path <dir>] [--alias <alias>]`);
|
|
818
|
-
console.log(` add [--kind api,admin,mobile] [--framework <name>,...] [--workspaces] [--into <path>] [--name <appName>]`);
|
|
819
|
-
console.log(` (bare "add", or "add" with --kind but no combo, launches a guided prompt for whatever's missing)`);
|
|
820
|
-
console.log(` update [--check] [--force] [--skip-install] [--into <path>] (re-installs whatever combo+variant auth.lock.json already records)`);
|
|
821
|
-
console.log(` --check: report what would change, without writing anything (merge-mode combos only)`);
|
|
822
|
-
console.log(` diff [--into <path>] (shows the actual content diff for every tracked file that differs from the current registry — read-only; merge-mode combos only)`);
|
|
823
|
-
console.log(` In a TTY, without --force or --check: a file changed locally since install prompts to overwrite, per file.`);
|
|
824
|
-
console.log(` --skip-install: don't run the package manager after copying files — the default is to install for you, shadcn-\`add\`-style.`);
|
|
825
|
-
console.log(` --pm <npm|pnpm|yarn|bun>: which package manager to install with — default: detected from a lockfile in the target directory, npm if none found.`);
|
|
826
|
-
console.log(`\nAvailable combos:`);
|
|
827
|
-
for (const kind of ["api", "admin", "mobile"] as Kind[]) {
|
|
828
|
-
const names = combosByKind(registry, kind).map(([name]) => name);
|
|
829
|
-
if (names.length) console.log(` ${KIND_LABELS[kind]}: ${names.join(", ")}`);
|
|
830
|
-
}
|
|
831
|
-
console.log(`\nVariants (choose one at install time):`);
|
|
832
|
-
for (const [name, variant] of Object.entries(registry.variants)) {
|
|
833
|
-
console.log(` ${name}${variant.flag ? ` (${variant.flag})` : " (default)"} — ${variant.description}`);
|
|
834
|
-
}
|
|
835
|
-
process.exitCode = command ? 1 : 0;
|
|
836
|
-
}
|
|
872
|
+
default:
|
|
873
|
+
await printUsage(command ? 1 : 0);
|
|
837
874
|
}
|
|
838
875
|
}
|
|
839
876
|
|