@hypersonic-js/cli 0.2.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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/dist/bin/hypersonic.d.ts +3 -0
  3. package/dist/bin/hypersonic.d.ts.map +1 -0
  4. package/dist/bin/hypersonic.js +4 -0
  5. package/dist/bin/hypersonic.js.map +1 -0
  6. package/dist/package.json +49 -0
  7. package/dist/src/commands/admin/create-admin.d.ts +79 -0
  8. package/dist/src/commands/admin/create-admin.d.ts.map +1 -0
  9. package/dist/src/commands/admin/create-admin.js +82 -0
  10. package/dist/src/commands/admin/create-admin.js.map +1 -0
  11. package/dist/src/commands/admin/generate-meta.d.ts +14 -0
  12. package/dist/src/commands/admin/generate-meta.d.ts.map +1 -0
  13. package/dist/src/commands/admin/generate-meta.js +39 -0
  14. package/dist/src/commands/admin/generate-meta.js.map +1 -0
  15. package/dist/src/commands/admin/index.d.ts +6 -0
  16. package/dist/src/commands/admin/index.d.ts.map +1 -0
  17. package/dist/src/commands/admin/index.js +15 -0
  18. package/dist/src/commands/admin/index.js.map +1 -0
  19. package/dist/src/commands/admin/scaffold.d.ts +9 -0
  20. package/dist/src/commands/admin/scaffold.d.ts.map +1 -0
  21. package/dist/src/commands/admin/scaffold.js +33 -0
  22. package/dist/src/commands/admin/scaffold.js.map +1 -0
  23. package/dist/src/dmmf/constants.d.ts +4 -0
  24. package/dist/src/dmmf/constants.d.ts.map +1 -0
  25. package/dist/src/dmmf/constants.js +4 -0
  26. package/dist/src/dmmf/constants.js.map +1 -0
  27. package/dist/src/dmmf/fields.d.ts +49 -0
  28. package/dist/src/dmmf/fields.d.ts.map +1 -0
  29. package/dist/src/dmmf/fields.js +103 -0
  30. package/dist/src/dmmf/fields.js.map +1 -0
  31. package/dist/src/dmmf/parser.d.ts +14 -0
  32. package/dist/src/dmmf/parser.d.ts.map +1 -0
  33. package/dist/src/dmmf/parser.js +57 -0
  34. package/dist/src/dmmf/parser.js.map +1 -0
  35. package/dist/src/dmmf/types.d.ts +37 -0
  36. package/dist/src/dmmf/types.d.ts.map +1 -0
  37. package/dist/src/dmmf/types.js +2 -0
  38. package/dist/src/dmmf/types.js.map +1 -0
  39. package/dist/src/index.d.ts +9 -0
  40. package/dist/src/index.d.ts.map +1 -0
  41. package/dist/src/index.js +18 -0
  42. package/dist/src/index.js.map +1 -0
  43. package/dist/src/utils/logger.d.ts +11 -0
  44. package/dist/src/utils/logger.d.ts.map +1 -0
  45. package/dist/src/utils/logger.js +20 -0
  46. package/dist/src/utils/logger.js.map +1 -0
  47. package/dist/src/utils/prompt.d.ts +14 -0
  48. package/dist/src/utils/prompt.d.ts.map +1 -0
  49. package/dist/src/utils/prompt.js +47 -0
  50. package/dist/src/utils/prompt.js.map +1 -0
  51. package/package.json +49 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Joaquim Dalton-Pereira
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=hypersonic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hypersonic.d.ts","sourceRoot":"","sources":["../../bin/hypersonic.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { createProgram } from '../src/index.js';
3
+ createProgram().parse();
4
+ //# sourceMappingURL=hypersonic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hypersonic.js","sourceRoot":"","sources":["../../bin/hypersonic.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE/C,aAAa,EAAE,CAAC,KAAK,EAAE,CAAA"}
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@hypersonic-js/cli",
3
+ "version": "0.2.0",
4
+ "description": "Hypersonic.js CLI — developer tooling for the Hypersonic framework",
5
+ "type": "module",
6
+ "bin": {
7
+ "hypersonic": "./dist/bin/hypersonic.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/hypersonic-js/hypersonic-js"
15
+ },
16
+ "author": "Joaquim Dalton-Pereira",
17
+ "license": "MIT",
18
+ "homepage": "https://hypersonic-js.com",
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "engines": {
23
+ "node": "^24.0.0"
24
+ },
25
+ "scripts": {
26
+ "build": "tsc",
27
+ "test": "vitest run",
28
+ "test:coverage": "vitest run --coverage",
29
+ "type-check": "tsc --noEmit",
30
+ "clean": "rm -rf dist coverage"
31
+ },
32
+ "dependencies": {
33
+ "@hypersonic-js/admin": "workspace:*",
34
+ "commander": "15.0.0",
35
+ "dotenv": "17.4.2",
36
+ "picocolors": "1.1.1",
37
+ "@prisma/get-dmmf": "7.8.0"
38
+ },
39
+ "devDependencies": {
40
+ "@hypersonic-js/core": "workspace:*",
41
+ "@prisma/adapter-pg": "7.8.0",
42
+ "@prisma/client": "7.8.0",
43
+ "@types/node": "25.9.3",
44
+ "@vitest/coverage-v8": "4.1.8",
45
+ "better-auth": "1.6.17",
46
+ "typescript": "6.0.3",
47
+ "vitest": "4.1.8"
48
+ }
49
+ }
@@ -0,0 +1,79 @@
1
+ import type { Command } from 'commander';
2
+ import { type PromptFn } from '../../utils/prompt.js';
3
+ export interface CreateAdminOptions {
4
+ email: string;
5
+ name: string;
6
+ password: string;
7
+ }
8
+ /** Minimal shape of the auth API we need from Better Auth. */
9
+ interface BetterAuthApi {
10
+ createUser(opts: {
11
+ body: {
12
+ email: string;
13
+ name: string;
14
+ password: string;
15
+ role: string;
16
+ };
17
+ }): Promise<{
18
+ user: {
19
+ id: string;
20
+ email: string;
21
+ };
22
+ }>;
23
+ }
24
+ interface BetterAuthInstance {
25
+ api: BetterAuthApi;
26
+ }
27
+ interface LoadedConfig {
28
+ config: {
29
+ database: {
30
+ provider: string;
31
+ };
32
+ };
33
+ env: {
34
+ DATABASE_URL: string;
35
+ BETTER_AUTH_SECRET: string;
36
+ };
37
+ }
38
+ /** Injectable dependency bag — swap out in tests to avoid real I/O. */
39
+ export interface CreateAdminDeps {
40
+ betterAuth(opts: unknown): BetterAuthInstance;
41
+ prismaAdapter(client: unknown, opts: {
42
+ provider: string;
43
+ }): unknown;
44
+ adminPlugin(): unknown;
45
+ /** Prisma v7 requires an adapter — never constructed bare. */
46
+ PrismaClient: new (opts: {
47
+ adapter: unknown;
48
+ }) => {
49
+ $disconnect(): Promise<void>;
50
+ };
51
+ /** Reads hypersonic.config.ts and validates env — throws if either is missing. */
52
+ loadConfig(): Promise<LoadedConfig>;
53
+ /** Creates the driver adapter for the given provider and DATABASE_URL. */
54
+ createDatabaseAdapter(provider: string, databaseUrl: string): Promise<unknown>;
55
+ }
56
+ /**
57
+ * Core logic for creating the first admin user.
58
+ * Accepts an optional `deps` parameter so unit tests can inject mocks
59
+ * without touching the filesystem or a real database.
60
+ *
61
+ * Environment validation and config loading are handled by `loadConfig` —
62
+ * it will throw a descriptive error if DATABASE_URL, BETTER_AUTH_SECRET, or
63
+ * hypersonic.config.ts are missing.
64
+ */
65
+ export declare function runCreateAdmin(opts: CreateAdminOptions, deps?: CreateAdminDeps): Promise<void>;
66
+ /**
67
+ * Registers the `hypersonic admin create-admin` subcommand.
68
+ *
69
+ * Prompts interactively for email, name, and password (password is hidden).
70
+ * Loads the project's .env file automatically before running so
71
+ * DATABASE_URL and BETTER_AUTH_SECRET are available without the caller
72
+ * having to export them manually.
73
+ *
74
+ * Optional `promptFn` and `deps` can be injected for testing without
75
+ * touching stdin or a real database.
76
+ */
77
+ export declare function registerCreateAdmin(adminCommand: Command, promptFn?: PromptFn, deps?: CreateAdminDeps): void;
78
+ export {};
79
+ //# sourceMappingURL=create-admin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-admin.d.ts","sourceRoot":"","sources":["../../../../src/commands/admin/create-admin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,EAA2B,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAK9E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,8DAA8D;AAC9D,UAAU,aAAa;IACrB,UAAU,CAAC,IAAI,EAAE;QACf,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;KACtE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAA;CACrD;AAED,UAAU,kBAAkB;IAC1B,GAAG,EAAE,aAAa,CAAA;CACnB;AAED,UAAU,YAAY;IACpB,MAAM,EAAE;QAAE,QAAQ,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAA;IAC1C,GAAG,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE,CAAA;CAC1D;AAED,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,kBAAkB,CAAA;IAC7C,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAA;IACnE,WAAW,IAAI,OAAO,CAAA;IACtB,8DAA8D;IAC9D,YAAY,EAAE,KAAK,IAAI,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,KAAK;QAAE,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAA;IAClF,kFAAkF;IAClF,UAAU,IAAI,OAAO,CAAC,YAAY,CAAC,CAAA;IACnC,0EAA0E;IAC1E,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAC/E;AAwBD;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,kBAAkB,EACxB,IAAI,CAAC,EAAE,eAAe,GACrB,OAAO,CAAC,IAAI,CAAC,CA2Bf;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,OAAO,EACrB,QAAQ,CAAC,EAAE,QAAQ,EACnB,IAAI,CAAC,EAAE,eAAe,GACrB,IAAI,CAqBN"}
@@ -0,0 +1,82 @@
1
+ import { prompt as defaultPrompt } from '../../utils/prompt.js';
2
+ import { logger } from '../../utils/logger.js';
3
+ // ── Dependency loader ────────────────────────────────────────────────────────
4
+ async function loadDeps() {
5
+ const [ba, pa, pl, pc, core] = await Promise.all([
6
+ import('better-auth'),
7
+ import('better-auth/adapters/prisma'),
8
+ import('better-auth/plugins'),
9
+ import('@prisma/client'),
10
+ import('@hypersonic-js/core'),
11
+ ]);
12
+ return {
13
+ betterAuth: ba.betterAuth,
14
+ prismaAdapter: pa.prismaAdapter,
15
+ adminPlugin: pl.admin,
16
+ PrismaClient: pc.PrismaClient,
17
+ loadConfig: () => core.loadConfig(),
18
+ createDatabaseAdapter: core.createDatabaseAdapter,
19
+ };
20
+ }
21
+ // ── Core logic ───────────────────────────────────────────────────────────────
22
+ /**
23
+ * Core logic for creating the first admin user.
24
+ * Accepts an optional `deps` parameter so unit tests can inject mocks
25
+ * without touching the filesystem or a real database.
26
+ *
27
+ * Environment validation and config loading are handled by `loadConfig` —
28
+ * it will throw a descriptive error if DATABASE_URL, BETTER_AUTH_SECRET, or
29
+ * hypersonic.config.ts are missing.
30
+ */
31
+ export async function runCreateAdmin(opts, deps) {
32
+ const { betterAuth, prismaAdapter, adminPlugin, PrismaClient, loadConfig, createDatabaseAdapter } = deps ?? (await loadDeps());
33
+ const { config, env } = await loadConfig();
34
+ const adapter = await createDatabaseAdapter(config.database.provider, env.DATABASE_URL);
35
+ const prisma = new PrismaClient({ adapter });
36
+ try {
37
+ const auth = betterAuth({
38
+ secret: env.BETTER_AUTH_SECRET,
39
+ database: prismaAdapter(prisma, { provider: config.database.provider }),
40
+ emailAndPassword: { enabled: true },
41
+ plugins: [adminPlugin()],
42
+ });
43
+ logger.info(`Creating admin user ${opts.email}…`);
44
+ await auth.api.createUser({
45
+ body: { email: opts.email, name: opts.name, password: opts.password, role: 'admin' },
46
+ });
47
+ logger.success(`Admin user created: ${opts.email}`);
48
+ }
49
+ finally {
50
+ await prisma.$disconnect();
51
+ }
52
+ }
53
+ // ── Command registration ──────────────────────────────────────────────────────
54
+ /**
55
+ * Registers the `hypersonic admin create-admin` subcommand.
56
+ *
57
+ * Prompts interactively for email, name, and password (password is hidden).
58
+ * Loads the project's .env file automatically before running so
59
+ * DATABASE_URL and BETTER_AUTH_SECRET are available without the caller
60
+ * having to export them manually.
61
+ *
62
+ * Optional `promptFn` and `deps` can be injected for testing without
63
+ * touching stdin or a real database.
64
+ */
65
+ export function registerCreateAdmin(adminCommand, promptFn, deps) {
66
+ const ask = promptFn ?? defaultPrompt;
67
+ adminCommand
68
+ .command('create-admin')
69
+ .description('Create the initial admin user. Run once after your first migration to bootstrap ' +
70
+ 'admin access. Subsequent admins can be promoted through the dashboard.')
71
+ .action(async () => {
72
+ // Load .env from the project root so DATABASE_URL and BETTER_AUTH_SECRET
73
+ // are available when the CLI is run from the project directory.
74
+ const { config } = await import('dotenv');
75
+ config();
76
+ const email = await ask('Email: ');
77
+ const name = await ask('Name: ');
78
+ const password = await ask('Password: ', true);
79
+ await runCreateAdmin({ email, name, password }, deps);
80
+ });
81
+ }
82
+ //# sourceMappingURL=create-admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-admin.js","sourceRoot":"","sources":["../../../../src/commands/admin/create-admin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { Command } from 'commander';
2
+ export interface GenerateMetaDeps {
3
+ getDMMF: (opts: {
4
+ datamodel: string;
5
+ }) => unknown;
6
+ readFile: (path: string) => string;
7
+ writeFile: (path: string, content: string) => void;
8
+ }
9
+ export declare function runGenerateMeta(opts: {
10
+ schema: string;
11
+ output: string;
12
+ }, deps?: GenerateMetaDeps): Promise<void>;
13
+ export declare function registerGenerateMeta(adminCmd: Command, deps?: GenerateMetaDeps): void;
14
+ //# sourceMappingURL=generate-meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-meta.d.ts","sourceRoot":"","sources":["../../../../src/commands/admin/generate-meta.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKxC,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAA;IACjD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IAClC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACnD;AAWD,wBAAsB,eAAe,CACnC,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACxC,IAAI,CAAC,EAAE,gBAAgB,GACtB,OAAO,CAAC,IAAI,CAAC,CAMf;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAmBrF"}
@@ -0,0 +1,39 @@
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { parseDmmf } from '../../dmmf/parser.js';
4
+ import { logger } from '../../utils/logger.js';
5
+ async function loadDeps() {
6
+ const { getDMMF } = await import('@prisma/get-dmmf');
7
+ return {
8
+ getDMMF,
9
+ readFile: (p) => readFileSync(p, 'utf-8'),
10
+ writeFile: (p, c) => writeFileSync(p, c),
11
+ };
12
+ }
13
+ export async function runGenerateMeta(opts, deps) {
14
+ const { getDMMF, readFile, writeFile } = deps ?? (await loadDeps());
15
+ const schema = readFile(resolve(opts.schema));
16
+ const dmmf = await getDMMF({ datamodel: schema });
17
+ const models = parseDmmf(dmmf);
18
+ writeFile(resolve(opts.output), JSON.stringify(models, null, 2));
19
+ }
20
+ export function registerGenerateMeta(adminCmd, deps) {
21
+ adminCmd
22
+ .command('generate-meta')
23
+ .description('Generate prisma/admin-meta.json from your Prisma schema. ' +
24
+ 'Commit this file to your repo — it is the static metadata the admin dashboard reads at runtime.')
25
+ .option('--schema <path>', 'Path to Prisma schema file', 'prisma/schema.prisma')
26
+ .option('--output <path>', 'Output path for the generated meta file', 'prisma/admin-meta.json')
27
+ .action(async (options) => {
28
+ try {
29
+ logger.info(`Reading schema from ${options.schema}…`);
30
+ await runGenerateMeta(options, deps);
31
+ logger.success(`Admin meta written to ${options.output}`);
32
+ }
33
+ catch (err) {
34
+ logger.error(err instanceof Error ? err.message : String(err));
35
+ process.exit(1);
36
+ }
37
+ });
38
+ }
39
+ //# sourceMappingURL=generate-meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-meta.js","sourceRoot":"","sources":["../../../../src/commands/admin/generate-meta.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAS9C,KAAK,UAAU,QAAQ;IACrB,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAA;IACpD,OAAO;QACL,OAAO;QACP,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC;QACzC,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC;KACzC,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAwC,EACxC,IAAuB;IAEvB,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,IAAI,CAAC,MAAM,QAAQ,EAAE,CAAC,CAAA;IACnE,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IAC7C,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAA;IACjD,MAAM,MAAM,GAAG,SAAS,CAAC,IAAoB,CAAC,CAAA;IAC9C,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;AAClE,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAiB,EAAE,IAAuB;IAC7E,QAAQ;SACL,OAAO,CAAC,eAAe,CAAC;SACxB,WAAW,CACV,2DAA2D;QACzD,iGAAiG,CACpG;SACA,MAAM,CAAC,iBAAiB,EAAE,4BAA4B,EAAE,sBAAsB,CAAC;SAC/E,MAAM,CAAC,iBAAiB,EAAE,yCAAyC,EAAE,wBAAwB,CAAC;SAC9F,MAAM,CAAC,KAAK,EAAE,OAA2C,EAAE,EAAE;QAC5D,IAAI,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,uBAAuB,OAAO,CAAC,MAAM,GAAG,CAAC,CAAA;YACrD,MAAM,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;YACpC,MAAM,CAAC,OAAO,CAAC,yBAAyB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;QAC3D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;YAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;IACH,CAAC,CAAC,CAAA;AACN,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Command } from 'commander';
2
+ /**
3
+ * Registers the `hypersonic admin` command group and all its subcommands.
4
+ */
5
+ export declare function registerAdminCommands(program: Command): void;
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/admin/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKxC;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAQ5D"}
@@ -0,0 +1,15 @@
1
+ import { registerAdminScaffold } from './scaffold.js';
2
+ import { registerCreateAdmin } from './create-admin.js';
3
+ import { registerGenerateMeta } from './generate-meta.js';
4
+ /**
5
+ * Registers the `hypersonic admin` command group and all its subcommands.
6
+ */
7
+ export function registerAdminCommands(program) {
8
+ const admin = program
9
+ .command('admin')
10
+ .description('Admin dashboard commands');
11
+ registerAdminScaffold(admin);
12
+ registerCreateAdmin(admin);
13
+ registerGenerateMeta(admin);
14
+ }
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/commands/admin/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAEzD;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,MAAM,KAAK,GAAG,OAAO;SAClB,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,0BAA0B,CAAC,CAAA;IAE1C,qBAAqB,CAAC,KAAK,CAAC,CAAA;IAC5B,mBAAmB,CAAC,KAAK,CAAC,CAAA;IAC1B,oBAAoB,CAAC,KAAK,CAAC,CAAA;AAC7B,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Command } from 'commander';
2
+ /**
3
+ * Registers the `hypersonic admin scaffold` subcommand.
4
+ *
5
+ * Usage:
6
+ * hypersonic admin scaffold [--target-dir <dir>] [--force]
7
+ */
8
+ export declare function registerAdminScaffold(adminCommand: Command): void;
9
+ //# sourceMappingURL=scaffold.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../../src/commands/admin/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAIxC;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,OAAO,GAAG,IAAI,CAiCjE"}
@@ -0,0 +1,33 @@
1
+ import { scaffoldAdmin } from '@hypersonic-js/admin';
2
+ import { logger } from '../../utils/logger.js';
3
+ /**
4
+ * Registers the `hypersonic admin scaffold` subcommand.
5
+ *
6
+ * Usage:
7
+ * hypersonic admin scaffold [--target-dir <dir>] [--force]
8
+ */
9
+ export function registerAdminScaffold(adminCommand) {
10
+ adminCommand
11
+ .command('scaffold')
12
+ .description('Scaffold the three generic admin page components (Dashboard, ModelIndex, ModelForm) ' +
13
+ 'into your project. These components are schema-driven and never need to be regenerated.')
14
+ .option('--target-dir <dir>', 'Directory to scaffold admin pages into', 'resources/js/Pages')
15
+ .option('-f, --force', 'Overwrite existing files', false)
16
+ .action(async (opts) => {
17
+ logger.info(`Scaffolding admin pages into ${opts.targetDir}/Admin/…`);
18
+ const result = await scaffoldAdmin({
19
+ targetDir: opts.targetDir,
20
+ force: opts.force,
21
+ });
22
+ for (const file of result.written) {
23
+ logger.success(`Written ${opts.targetDir}/Admin/${file}`);
24
+ }
25
+ for (const file of result.skipped) {
26
+ logger.warn(`Skipped ${opts.targetDir}/Admin/${file} (already exists — use --force to overwrite)`);
27
+ }
28
+ if (result.written.length === 0 && result.skipped.length === 0) {
29
+ logger.warn('No files written. This is unexpected — please file a bug.');
30
+ }
31
+ });
32
+ }
33
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../../../src/commands/admin/scaffold.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAE9C;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,YAAqB;IACzD,YAAY;SACT,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CACV,sFAAsF;QACpF,yFAAyF,CAC5F;SACA,MAAM,CACL,oBAAoB,EACpB,wCAAwC,EACxC,oBAAoB,CACrB;SACA,MAAM,CAAC,aAAa,EAAE,0BAA0B,EAAE,KAAK,CAAC;SACxD,MAAM,CAAC,KAAK,EAAE,IAA2C,EAAE,EAAE;QAC5D,MAAM,CAAC,IAAI,CAAC,gCAAgC,IAAI,CAAC,SAAS,UAAU,CAAC,CAAA;QAErE,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC;YACjC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC,CAAA;QAEF,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YAClC,MAAM,CAAC,OAAO,CAAC,YAAY,IAAI,CAAC,SAAS,UAAU,IAAI,EAAE,CAAC,CAAA;QAC5D,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YAClC,MAAM,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,SAAS,UAAU,IAAI,8CAA8C,CAAC,CAAA;QACrG,CAAC;QAED,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/D,MAAM,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAA;QAC1E,CAAC;IACH,CAAC,CAAC,CAAA;AACN,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare const DISPLAY_FIELD_CANDIDATES: readonly ["name", "title", "email", "label", "slug"];
2
+ export declare const AUTO_MANAGED_FIELD_NAMES: readonly ["createdAt", "updatedAt"];
3
+ export declare const LARGE_TEXT_FIELD_NAMES: readonly ["body", "content", "description", "text", "html", "markdown"];
4
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/dmmf/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wBAAwB,sDAAuD,CAAA;AAC5F,eAAO,MAAM,wBAAwB,qCAAsC,CAAA;AAC3E,eAAO,MAAM,sBAAsB,yEAA0E,CAAA"}
@@ -0,0 +1,4 @@
1
+ export const DISPLAY_FIELD_CANDIDATES = ['name', 'title', 'email', 'label', 'slug'];
2
+ export const AUTO_MANAGED_FIELD_NAMES = ['createdAt', 'updatedAt'];
3
+ export const LARGE_TEXT_FIELD_NAMES = ['body', 'content', 'description', 'text', 'html', 'markdown'];
4
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/dmmf/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAU,CAAA;AAC5F,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,WAAW,EAAE,WAAW,CAAU,CAAA;AAC3E,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAU,CAAA"}
@@ -0,0 +1,49 @@
1
+ import type { AdminFieldMeta, AdminFieldKind } from '@hypersonic-js/admin';
2
+ import type { DmmfField, DmmfEnum } from './types.js';
3
+ /** Maps a DMMF field kind to our simplified AdminFieldKind. */
4
+ export declare function classifyField(field: DmmfField): AdminFieldKind;
5
+ /**
6
+ * Returns true for fields that are auto-managed by Prisma or the database
7
+ * and should therefore be excluded from admin create/edit forms.
8
+ * Covers:
9
+ * - field.isUpdatedAt — @updatedAt fields managed by the Prisma client
10
+ * - AUTO_MANAGED_FIELD_NAMES — convention-based names (createdAt / updatedAt)
11
+ * for schemas where Prisma does not set the flags
12
+ * (e.g. Better Auth-managed timestamps)
13
+ *
14
+ * NOTE: We intentionally do NOT check field.isReadOnly here. Prisma sets
15
+ * isReadOnly on FK scalars as well as on auto-managed columns. FK scalars
16
+ * must remain editable in the admin form; auto-managed fields are caught by
17
+ * the isUpdatedAt flag and the name-convention list above.
18
+ */
19
+ export declare function isAutoManagedField(field: DmmfField): boolean;
20
+ /** Chooses the most human-readable field name for list view labels. */
21
+ export declare function getDisplayField(fields: AdminFieldMeta[]): string;
22
+ /** Returns the subset of fields suitable for table list columns. Capped at 6. */
23
+ export declare function getListFields(fields: AdminFieldMeta[]): AdminFieldMeta[];
24
+ /**
25
+ * Returns the subset of fields to show in create/edit forms.
26
+ *
27
+ * Inclusion rules:
28
+ * - Relation fields are excluded (they have no direct column).
29
+ * - Auto-managed read-only fields (timestamps, @updatedAt) are excluded.
30
+ * - FK scalar fields are INCLUDED even though isReadOnly is true — the admin
31
+ * needs to supply the FK value; the router renders them as a <select>.
32
+ * - Id fields with a default (e.g. autoincrement) are excluded.
33
+ */
34
+ export declare function getFormFields(fields: AdminFieldMeta[]): AdminFieldMeta[];
35
+ /**
36
+ * Maps a raw DMMF field to an AdminFieldMeta, resolving enum values.
37
+ *
38
+ * @param field The DMMF field to map.
39
+ * @param enums All enum definitions from the DMMF document.
40
+ * @param fkToModel Map of FK scalar field name → related model name, built by
41
+ * parseDmmf from relation fields' relationFromFields.
42
+ * e.g. new Map([['userId', 'User'], ['authorId', 'Author']])
43
+ * @param fkToSlug Map of FK scalar field name → related model urlSlug, built
44
+ * by parseDmmf. Always use this — never derive the slug
45
+ * client-side from the model name.
46
+ * e.g. new Map([['userId', 'user'], ['userProfileId', 'userprofile']])
47
+ */
48
+ export declare function mapField(field: DmmfField, enums: DmmfEnum[], fkToModel?: ReadonlyMap<string, string>, fkToSlug?: ReadonlyMap<string, string>): AdminFieldMeta;
49
+ //# sourceMappingURL=fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../../src/dmmf/fields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1E,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAMrD,+DAA+D;AAC/D,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,GAAG,cAAc,CAI9D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAG5D;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,CAOhE;AAED,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAKxE;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAOxE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,QAAQ,EAAE,EACjB,SAAS,GAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAa,EAClD,QAAQ,GAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAa,GAChD,cAAc,CAqBhB"}
@@ -0,0 +1,103 @@
1
+ const DISPLAY_FIELD_CANDIDATES = ['name', 'title', 'email', 'label', 'slug'];
2
+ const AUTO_MANAGED_FIELD_NAMES = ['createdAt', 'updatedAt'];
3
+ const LARGE_TEXT_FIELD_NAMES = ['body', 'content', 'description', 'text', 'html', 'markdown'];
4
+ /** Maps a DMMF field kind to our simplified AdminFieldKind. */
5
+ export function classifyField(field) {
6
+ if (field.kind === 'object')
7
+ return 'relation';
8
+ if (field.kind === 'enum')
9
+ return 'enum';
10
+ return 'scalar';
11
+ }
12
+ /**
13
+ * Returns true for fields that are auto-managed by Prisma or the database
14
+ * and should therefore be excluded from admin create/edit forms.
15
+ * Covers:
16
+ * - field.isUpdatedAt — @updatedAt fields managed by the Prisma client
17
+ * - AUTO_MANAGED_FIELD_NAMES — convention-based names (createdAt / updatedAt)
18
+ * for schemas where Prisma does not set the flags
19
+ * (e.g. Better Auth-managed timestamps)
20
+ *
21
+ * NOTE: We intentionally do NOT check field.isReadOnly here. Prisma sets
22
+ * isReadOnly on FK scalars as well as on auto-managed columns. FK scalars
23
+ * must remain editable in the admin form; auto-managed fields are caught by
24
+ * the isUpdatedAt flag and the name-convention list above.
25
+ */
26
+ export function isAutoManagedField(field) {
27
+ if (field.isUpdatedAt)
28
+ return true;
29
+ return AUTO_MANAGED_FIELD_NAMES.includes(field.name);
30
+ }
31
+ /** Chooses the most human-readable field name for list view labels. */
32
+ export function getDisplayField(fields) {
33
+ for (const candidate of DISPLAY_FIELD_CANDIDATES) {
34
+ const found = fields.find((f) => f.name === candidate && f.kind === 'scalar');
35
+ if (found !== undefined)
36
+ return found.name;
37
+ }
38
+ const idField = fields.find((f) => f.isId);
39
+ return idField?.name ?? 'id';
40
+ }
41
+ /** Returns the subset of fields suitable for table list columns. Capped at 6. */
42
+ export function getListFields(fields) {
43
+ return fields
44
+ .filter((f) => f.kind === 'scalar' && !f.isList)
45
+ .filter((f) => !LARGE_TEXT_FIELD_NAMES.includes(f.name))
46
+ .slice(0, 6);
47
+ }
48
+ /**
49
+ * Returns the subset of fields to show in create/edit forms.
50
+ *
51
+ * Inclusion rules:
52
+ * - Relation fields are excluded (they have no direct column).
53
+ * - Auto-managed read-only fields (timestamps, @updatedAt) are excluded.
54
+ * - FK scalar fields are INCLUDED even though isReadOnly is true — the admin
55
+ * needs to supply the FK value; the router renders them as a <select>.
56
+ * - Id fields with a default (e.g. autoincrement) are excluded.
57
+ */
58
+ export function getFormFields(fields) {
59
+ return fields.filter((f) => {
60
+ if (f.kind === 'relation')
61
+ return false;
62
+ if (f.isReadOnly && !f.isForeignKey)
63
+ return false;
64
+ if (f.isId && f.hasDefault)
65
+ return false;
66
+ return true;
67
+ });
68
+ }
69
+ /**
70
+ * Maps a raw DMMF field to an AdminFieldMeta, resolving enum values.
71
+ *
72
+ * @param field The DMMF field to map.
73
+ * @param enums All enum definitions from the DMMF document.
74
+ * @param fkToModel Map of FK scalar field name → related model name, built by
75
+ * parseDmmf from relation fields' relationFromFields.
76
+ * e.g. new Map([['userId', 'User'], ['authorId', 'Author']])
77
+ * @param fkToSlug Map of FK scalar field name → related model urlSlug, built
78
+ * by parseDmmf. Always use this — never derive the slug
79
+ * client-side from the model name.
80
+ * e.g. new Map([['userId', 'user'], ['userProfileId', 'userprofile']])
81
+ */
82
+ export function mapField(field, enums, fkToModel = new Map(), fkToSlug = new Map()) {
83
+ const kind = classifyField(field);
84
+ const enumDef = kind === 'enum' ? enums.find((e) => e.name === field.type) : undefined;
85
+ const isForeignKey = field.kind === 'scalar' && fkToModel.has(field.name);
86
+ return {
87
+ name: field.name,
88
+ prismaType: field.type,
89
+ kind,
90
+ isRequired: field.isRequired,
91
+ isId: field.isId,
92
+ isUnique: field.isUnique,
93
+ hasDefault: field.hasDefaultValue,
94
+ isReadOnly: field.isReadOnly || isAutoManagedField(field),
95
+ isForeignKey,
96
+ relatedModelName: isForeignKey ? fkToModel.get(field.name) : undefined,
97
+ relatedModelSlug: isForeignKey ? fkToSlug.get(field.name) : undefined,
98
+ isList: field.isList,
99
+ relationTo: field.kind === 'object' ? field.type : undefined,
100
+ enumValues: enumDef?.values.map((v) => v.name),
101
+ };
102
+ }
103
+ //# sourceMappingURL=fields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fields.js","sourceRoot":"","sources":["../../../src/dmmf/fields.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { AdminModelMeta } from '@hypersonic-js/admin';
2
+ import type { DmmfDocument } from './types.js';
3
+ /**
4
+ * Converts a model name to a plural display name.
5
+ * Handles the most common English pluralisation rules.
6
+ */
7
+ export declare function toDisplayName(name: string): string;
8
+ /**
9
+ * Maps a full Prisma DMMF document into AdminModelMeta for all models.
10
+ * No filtering is applied — filtering by showAuthModels / hiddenModels
11
+ * happens at runtime in mountAdmin.
12
+ */
13
+ export declare function parseDmmf(dmmf: DmmfDocument): AdminModelMeta[];
14
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/dmmf/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAG9C;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,YAAY,GAAG,cAAc,EAAE,CAyD9D"}
@@ -0,0 +1,57 @@
1
+ import { mapField, getDisplayField, getListFields, getFormFields } from './fields.js';
2
+ /**
3
+ * Converts a model name to a plural display name.
4
+ * Handles the most common English pluralisation rules.
5
+ */
6
+ export function toDisplayName(name) {
7
+ if (name.endsWith('s'))
8
+ return name;
9
+ if (name.endsWith('y') && !/[aeiou]y$/i.test(name)) {
10
+ return name.slice(0, -1) + 'ies';
11
+ }
12
+ return name + 's';
13
+ }
14
+ /**
15
+ * Maps a full Prisma DMMF document into AdminModelMeta for all models.
16
+ * No filtering is applied — filtering by showAuthModels / hiddenModels
17
+ * happens at runtime in mountAdmin.
18
+ */
19
+ export function parseDmmf(dmmf) {
20
+ // Pre-build model name → urlSlug for all models so FK fields can carry the
21
+ // correct slug without any client-side derivation. A model named
22
+ // "UserProfile" gets slug "userprofile", not "userProfile".
23
+ const modelToSlug = new Map(dmmf.datamodel.models.map((m) => [m.name, m.name.toLowerCase()]));
24
+ return dmmf.datamodel.models.map((model) => {
25
+ // Build a map of FK scalar name → related model name.
26
+ // Each relation (object) field lists its FK scalars in relationFromFields
27
+ // and its type is the related model name.
28
+ // e.g. `user User @relation(fields: [userId], …)` → { 'userId' → 'User' }
29
+ const fkToModel = new Map(model.fields
30
+ .filter((f) => f.kind === 'object')
31
+ .flatMap((f) => (f.relationFromFields ?? []).map((scalar) => [scalar, f.type])));
32
+ // Build a map of FK scalar name → related model urlSlug.
33
+ // Used by ModelForm to construct the correct /related-options/:slug URL
34
+ // for load-more pagination instead of deriving it client-side.
35
+ const fkToSlug = new Map(model.fields
36
+ .filter((f) => f.kind === 'object')
37
+ .flatMap((f) => (f.relationFromFields ?? []).map((scalar) => [scalar, modelToSlug.get(f.type) ?? f.type.toLowerCase()])));
38
+ const fields = model.fields.map((f) => mapField(f, dmmf.datamodel.enums, fkToModel, fkToSlug));
39
+ const idField = fields.find((f) => f.isId) ?? fields[0];
40
+ if (idField === undefined) {
41
+ throw new Error(`Admin: model "${model.name}" has no fields — cannot build admin meta.`);
42
+ }
43
+ const idType = idField.prismaType === 'Int' || idField.prismaType === 'Float' ? 'number' : 'string';
44
+ return {
45
+ name: model.name,
46
+ urlSlug: model.name.toLowerCase(),
47
+ displayName: toDisplayName(model.name),
48
+ idField: idField.name,
49
+ idType,
50
+ displayField: getDisplayField(fields),
51
+ fields,
52
+ listFields: getListFields(fields),
53
+ formFields: getFormFields(fields),
54
+ };
55
+ });
56
+ }
57
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../../src/dmmf/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ export interface DmmfEnumValue {
2
+ name: string;
3
+ dbName: string | null;
4
+ }
5
+ export interface DmmfEnum {
6
+ name: string;
7
+ values: DmmfEnumValue[];
8
+ dbName: string | null;
9
+ }
10
+ export interface DmmfField {
11
+ name: string;
12
+ type: string;
13
+ kind: 'scalar' | 'object' | 'enum' | 'unsupported';
14
+ isRequired: boolean;
15
+ isUnique: boolean;
16
+ isId: boolean;
17
+ isList: boolean;
18
+ hasDefaultValue: boolean;
19
+ isReadOnly: boolean;
20
+ isGenerated?: boolean;
21
+ isUpdatedAt: boolean;
22
+ relationName?: string | null;
23
+ relationFromFields?: string[];
24
+ relationToFields?: string[];
25
+ }
26
+ export interface DmmfModel {
27
+ name: string;
28
+ fields: DmmfField[];
29
+ dbName: string | null;
30
+ }
31
+ export interface DmmfDocument {
32
+ datamodel: {
33
+ models: DmmfModel[];
34
+ enums: DmmfEnum[];
35
+ };
36
+ }
37
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/dmmf/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,aAAa,EAAE,CAAA;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,aAAa,CAAA;IAClD,UAAU,EAAE,OAAO,CAAA;IACnB,QAAQ,EAAE,OAAO,CAAA;IACjB,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,EAAE,OAAO,CAAA;IACf,eAAe,EAAE,OAAO,CAAA;IACxB,UAAU,EAAE,OAAO,CAAA;IACnB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,WAAW,EAAE,OAAO,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;CAC5B;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,SAAS,EAAE,CAAA;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE;QACT,MAAM,EAAE,SAAS,EAAE,CAAA;QACnB,KAAK,EAAE,QAAQ,EAAE,CAAA;KAClB,CAAA;CACF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/dmmf/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ import { Command } from 'commander';
2
+ export declare const CLI_VERSION: string;
3
+ /**
4
+ * Builds and returns the configured Commander program.
5
+ * Exported separately from parse() so the program is testable
6
+ * without actually invoking process.argv.
7
+ */
8
+ export declare function createProgram(): Command;
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAInC,eAAO,MAAM,WAAW,EAAE,MAAoB,CAAA;AAE9C;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,OAAO,CASvC"}
@@ -0,0 +1,18 @@
1
+ import { Command } from 'commander';
2
+ import { registerAdminCommands } from './commands/admin/index.js';
3
+ import pkg from '../package.json' with { type: 'json' };
4
+ export const CLI_VERSION = pkg.version;
5
+ /**
6
+ * Builds and returns the configured Commander program.
7
+ * Exported separately from parse() so the program is testable
8
+ * without actually invoking process.argv.
9
+ */
10
+ export function createProgram() {
11
+ const program = new Command()
12
+ .name('hypersonic')
13
+ .description('Hypersonic.js framework CLI')
14
+ .version(CLI_VERSION, '-v, --version', 'Print the CLI version');
15
+ registerAdminCommands(program);
16
+ return program;
17
+ }
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAA;AACjE,OAAO,GAAG,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAA;AAEvD,MAAM,CAAC,MAAM,WAAW,GAAW,GAAG,CAAC,OAAO,CAAA;AAE9C;;;;GAIG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;SAC1B,IAAI,CAAC,YAAY,CAAC;SAClB,WAAW,CAAC,6BAA6B,CAAC;SAC1C,OAAO,CAAC,WAAW,EAAE,eAAe,EAAE,uBAAuB,CAAC,CAAA;IAEjE,qBAAqB,CAAC,OAAO,CAAC,CAAA;IAE9B,OAAO,OAAO,CAAA;AAChB,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Minimal coloured console logger for CLI output.
3
+ * Writes info/success/warn to stdout, error to stderr.
4
+ */
5
+ export declare const logger: {
6
+ info: (msg: string) => void;
7
+ success: (msg: string) => void;
8
+ warn: (msg: string) => void;
9
+ error: (msg: string) => void;
10
+ };
11
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/utils/logger.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,MAAM;gBACL,MAAM,KAAG,IAAI;mBAGV,MAAM,KAAG,IAAI;gBAGhB,MAAM,KAAG,IAAI;iBAGZ,MAAM,KAAG,IAAI;CAG3B,CAAA"}
@@ -0,0 +1,20 @@
1
+ import pc from 'picocolors';
2
+ /**
3
+ * Minimal coloured console logger for CLI output.
4
+ * Writes info/success/warn to stdout, error to stderr.
5
+ */
6
+ export const logger = {
7
+ info: (msg) => {
8
+ process.stdout.write(pc.blue(' info ') + msg + '\n');
9
+ },
10
+ success: (msg) => {
11
+ process.stdout.write(pc.green(' ✓ ') + msg + '\n');
12
+ },
13
+ warn: (msg) => {
14
+ process.stdout.write(pc.yellow(' warn ') + msg + '\n');
15
+ },
16
+ error: (msg) => {
17
+ process.stderr.write(pc.red(' ✗ ') + msg + '\n');
18
+ },
19
+ };
20
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AAE3B;;;GAGG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,EAAE,CAAC,GAAW,EAAQ,EAAE;QAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,CAAA;IACvD,CAAC;IACD,OAAO,EAAE,CAAC,GAAW,EAAQ,EAAE;QAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,CAAA;IACrD,CAAC;IACD,IAAI,EAAE,CAAC,GAAW,EAAQ,EAAE;QAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,CAAA;IACzD,CAAC;IACD,KAAK,EAAE,CAAC,GAAW,EAAQ,EAAE;QAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,CAAA;IACnD,CAAC;CACF,CAAA"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Function signature for asking the user a single question.
3
+ * `hidden` suppresses echo, suitable for passwords.
4
+ */
5
+ export type PromptFn = (question: string, hidden?: boolean) => Promise<string>;
6
+ /**
7
+ * Asks the user a question via stdin/stdout.
8
+ *
9
+ * When `hidden` is true the typed characters are not echoed — use this for
10
+ * passwords. Requires stdin to be a TTY; if it isn't (e.g. in a pipe) the
11
+ * input is still read but cannot be hidden.
12
+ */
13
+ export declare function prompt(question: string, hidden?: boolean): Promise<string>;
14
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../src/utils/prompt.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;AAE9E;;;;;;GAMG;AACH,wBAAsB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAa9E"}
@@ -0,0 +1,47 @@
1
+ import { createInterface } from 'readline';
2
+ /**
3
+ * Asks the user a question via stdin/stdout.
4
+ *
5
+ * When `hidden` is true the typed characters are not echoed — use this for
6
+ * passwords. Requires stdin to be a TTY; if it isn't (e.g. in a pipe) the
7
+ * input is still read but cannot be hidden.
8
+ */
9
+ export async function prompt(question, hidden = false) {
10
+ if (hidden && process.stdin.isTTY) {
11
+ return promptHidden(question);
12
+ }
13
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
14
+ return new Promise((resolve) => {
15
+ rl.question(question, (answer) => {
16
+ rl.close();
17
+ resolve(answer);
18
+ });
19
+ });
20
+ }
21
+ function promptHidden(question) {
22
+ return new Promise((resolve) => {
23
+ process.stdout.write(question);
24
+ let value = '';
25
+ const onData = (chunk) => {
26
+ const char = chunk.toString('utf8');
27
+ if (char === '\r' || char === '\n' || char === '\u0003' /* Ctrl-C */) {
28
+ process.stdin.setRawMode(false);
29
+ process.stdin.pause();
30
+ process.stdin.removeListener('data', onData);
31
+ process.stdout.write('\n');
32
+ resolve(value);
33
+ }
34
+ else if (char === '\u007F' /* backspace */) {
35
+ value = value.slice(0, -1);
36
+ }
37
+ else {
38
+ value += char;
39
+ }
40
+ };
41
+ process.stdin.setRawMode(true);
42
+ process.stdin.resume();
43
+ process.stdin.setEncoding('utf8');
44
+ process.stdin.on('data', onData);
45
+ });
46
+ }
47
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../../src/utils/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAQ1C;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,QAAgB,EAAE,MAAM,GAAG,KAAK;IAC3D,IAAI,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QAClC,OAAO,YAAY,CAAC,QAAQ,CAAC,CAAA;IAC/B,CAAC;IAED,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IAE5E,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QACrC,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;YAC/B,EAAE,CAAC,KAAK,EAAE,CAAA;YACV,OAAO,CAAC,MAAM,CAAC,CAAA;QACjB,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,QAAgB;IACpC,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QACrC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;QAE9B,IAAI,KAAK,GAAG,EAAE,CAAA;QAEd,MAAM,MAAM,GAAG,CAAC,KAAa,EAAQ,EAAE;YACrC,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;YAEnC,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,QAAQ,CAAC,YAAY,EAAE,CAAC;gBACrE,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;gBAC/B,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;gBACrB,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;gBAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBAC1B,OAAO,CAAC,KAAK,CAAC,CAAA;YAChB,CAAC;iBAAM,IAAI,IAAI,KAAK,QAAQ,CAAC,eAAe,EAAE,CAAC;gBAC7C,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;YAC5B,CAAC;iBAAM,CAAC;gBACN,KAAK,IAAI,IAAI,CAAA;YACf,CAAC;QACH,CAAC,CAAA;QAED,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QAC9B,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAA;QACtB,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;QACjC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,CAAC,CAAC,CAAA;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@hypersonic-js/cli",
3
+ "version": "0.2.0",
4
+ "description": "Hypersonic.js CLI — developer tooling for the Hypersonic framework",
5
+ "type": "module",
6
+ "bin": {
7
+ "hypersonic": "./dist/bin/hypersonic.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/hypersonic-js/hypersonic-js"
15
+ },
16
+ "author": "Joaquim Dalton-Pereira",
17
+ "license": "MIT",
18
+ "homepage": "https://hypersonic-js.com",
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "engines": {
23
+ "node": "^24.0.0"
24
+ },
25
+ "dependencies": {
26
+ "commander": "15.0.0",
27
+ "dotenv": "17.4.2",
28
+ "picocolors": "1.1.1",
29
+ "@prisma/get-dmmf": "7.8.0",
30
+ "@hypersonic-js/admin": "0.2.0"
31
+ },
32
+ "devDependencies": {
33
+ "@prisma/adapter-pg": "7.8.0",
34
+ "@prisma/client": "7.8.0",
35
+ "@types/node": "25.9.3",
36
+ "@vitest/coverage-v8": "4.1.8",
37
+ "better-auth": "1.6.17",
38
+ "typescript": "6.0.3",
39
+ "vitest": "4.1.8",
40
+ "@hypersonic-js/core": "0.2.0"
41
+ },
42
+ "scripts": {
43
+ "build": "tsc",
44
+ "test": "vitest run",
45
+ "test:coverage": "vitest run --coverage",
46
+ "type-check": "tsc --noEmit",
47
+ "clean": "rm -rf dist coverage"
48
+ }
49
+ }