@pikku/cli 0.12.35 → 0.12.37

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 (100) hide show
  1. package/cli.schema.json +1 -1
  2. package/console-app/assets/{index-BOM3RFeu.js → index-Dxl3JsMK.js} +73 -73
  3. package/console-app/index.html +1 -1
  4. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  5. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  6. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  7. package/dist/.pikku/cli/pikku-cli-channel.js +6 -1
  8. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  9. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  10. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  11. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +6 -0
  12. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  13. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  14. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  15. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  16. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  17. package/dist/.pikku/function/pikku-function-types.gen.d.ts +2 -2
  18. package/dist/.pikku/function/pikku-function-types.gen.js +17 -3
  19. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  20. package/dist/.pikku/function/pikku-functions-meta.gen.json +74 -57
  21. package/dist/.pikku/function/pikku-functions.gen.js +3 -1
  22. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  23. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  24. package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
  25. package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +1 -1
  26. package/dist/.pikku/http/pikku-http-wirings.gen.js +1 -1
  27. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  28. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  29. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  30. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  31. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  32. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  33. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  34. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  35. package/dist/.pikku/pikku-types.gen.js +1 -1
  36. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  37. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  38. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  39. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  40. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  41. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  42. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +3 -2
  43. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  44. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  45. package/dist/.pikku/schemas/register.gen.js +9 -5
  46. package/dist/.pikku/schemas/schemas/DbGenerateInput.schema.json +1 -0
  47. package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  48. package/dist/.pikku/schemas/schemas/PikkuFunctionTypesInput.schema.json +1 -0
  49. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  50. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  51. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  52. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  53. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  54. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  55. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  56. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  57. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  58. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  59. package/dist/.pikku/workflow/meta/allWorkflow.gen.json +22 -4
  60. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  61. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  62. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  63. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  64. package/dist/bin/pikku-bin.mjs +2 -2
  65. package/dist/src/cli.wiring.js +5 -0
  66. package/dist/src/fabric/functions/login.function.d.ts +1 -1
  67. package/dist/src/fabric/functions/login.function.js +1 -1
  68. package/dist/src/fabric/functions/validate.function.js +4 -0
  69. package/dist/src/functions/commands/bootstrap.js +1 -1
  70. package/dist/src/functions/commands/db-generate.d.ts +1 -0
  71. package/dist/src/functions/commands/db-generate.js +45 -0
  72. package/dist/src/functions/commands/db-migrate.js +13 -1
  73. package/dist/src/functions/db/better-auth-schema.d.ts +23 -0
  74. package/dist/src/functions/db/better-auth-schema.js +122 -0
  75. package/dist/src/functions/db/local-db.d.ts +33 -0
  76. package/dist/src/functions/db/local-db.js +125 -1
  77. package/dist/src/functions/db/zod-codegen.js +9 -6
  78. package/dist/src/functions/validate/workspace-validate.js +1 -1
  79. package/dist/src/functions/wirings/auth/pikku-command-auth.js +30 -4
  80. package/dist/src/functions/wirings/auth/serialize-auth-gen.d.ts +33 -1
  81. package/dist/src/functions/wirings/auth/serialize-auth-gen.js +122 -88
  82. package/dist/src/functions/wirings/auth/serialize-auth-meta.d.ts +32 -0
  83. package/dist/src/functions/wirings/auth/serialize-auth-meta.js +23 -0
  84. package/dist/src/functions/wirings/auth/serialize-auth-types.d.ts +27 -0
  85. package/dist/src/functions/wirings/auth/serialize-auth-types.js +58 -0
  86. package/dist/src/functions/wirings/functions/pikku-command-function-types.d.ts +7 -1
  87. package/dist/src/functions/wirings/functions/pikku-command-function-types.js +16 -3
  88. package/dist/src/functions/wirings/functions/pikku-command-services.d.ts +1 -1
  89. package/dist/src/functions/wirings/functions/pikku-command-services.js +9 -2
  90. package/dist/src/functions/wirings/functions/serialize-function-types.js +17 -3
  91. package/dist/src/functions/wirings/functions/serialize-pikku-types-hub.d.ts +1 -1
  92. package/dist/src/functions/wirings/functions/serialize-pikku-types-hub.js +2 -1
  93. package/dist/src/functions/workflows/all.workflow.js +16 -2
  94. package/dist/src/scaffold/rpc-remote.gen.js +1 -1
  95. package/dist/src/services.js +8 -0
  96. package/dist/src/utils/pikku-cli-config.js +12 -0
  97. package/dist/tsconfig.tsbuildinfo +1 -1
  98. package/package.json +6 -5
  99. package/skills/pikku-better-auth/SKILL.md +211 -0
  100. package/skills/pikku-auth-js/SKILL.md +0 -339
@@ -0,0 +1,122 @@
1
+ import { createRequire } from 'node:module';
2
+ import { pathToFileURL } from 'node:url';
3
+ import { readdirSync, statSync, readFileSync, existsSync } from 'node:fs';
4
+ import { join, extname, dirname } from 'node:path';
5
+ import { PIKKU_BETTER_AUTH } from '@pikku/better-auth';
6
+ import { loadUserModule } from '../commands/load-user-project.js';
7
+ let cachedGetMigrations = null;
8
+ async function loadGetMigrations() {
9
+ if (cachedGetMigrations)
10
+ return cachedGetMigrations;
11
+ const require = createRequire(import.meta.url);
12
+ const mainEntry = require.resolve('better-auth');
13
+ let root = dirname(mainEntry);
14
+ while (!existsSync(join(root, 'package.json'))) {
15
+ const parent = dirname(root);
16
+ if (parent === root) {
17
+ throw new Error('Could not locate the better-auth package root');
18
+ }
19
+ root = parent;
20
+ }
21
+ const modUrl = pathToFileURL(join(root, 'dist/db/get-migration.mjs')).href;
22
+ const mod = await import(modUrl);
23
+ cachedGetMigrations = mod.getMigrations;
24
+ return cachedGetMigrations;
25
+ }
26
+ const SKIP_DIRS = new Set([
27
+ 'node_modules',
28
+ '.pikku',
29
+ '.git',
30
+ 'dist',
31
+ '.pikku-runtime',
32
+ ]);
33
+ function findAuthSourceFile(rootDir, srcDirectories) {
34
+ const walk = (dir) => {
35
+ let entries;
36
+ try {
37
+ entries = readdirSync(dir);
38
+ }
39
+ catch {
40
+ return null;
41
+ }
42
+ for (const entry of entries) {
43
+ if (SKIP_DIRS.has(entry))
44
+ continue;
45
+ const full = join(dir, entry);
46
+ let st;
47
+ try {
48
+ st = statSync(full);
49
+ }
50
+ catch {
51
+ continue;
52
+ }
53
+ if (st.isDirectory()) {
54
+ const found = walk(full);
55
+ if (found)
56
+ return found;
57
+ continue;
58
+ }
59
+ if (extname(full) !== '.ts')
60
+ continue;
61
+ let src;
62
+ try {
63
+ src = readFileSync(full, 'utf8');
64
+ }
65
+ catch {
66
+ continue;
67
+ }
68
+ if (/\bpikkuBetterAuth\s*\(/.test(src))
69
+ return full;
70
+ }
71
+ return null;
72
+ };
73
+ for (const srcDir of srcDirectories) {
74
+ const found = walk(join(rootDir, srcDir));
75
+ if (found)
76
+ return found;
77
+ }
78
+ return walk(rootDir);
79
+ }
80
+ async function loadAuthFactory(sourceFile) {
81
+ const mod = await loadUserModule(sourceFile);
82
+ for (const value of Object.values(mod)) {
83
+ if (typeof value === 'function' && value[PIKKU_BETTER_AUTH]) {
84
+ return value;
85
+ }
86
+ }
87
+ return null;
88
+ }
89
+ function schemaServicesStub(kysely, logger) {
90
+ const dummy = 'x'.repeat(32);
91
+ const fromKeys = (keys) => Object.fromEntries(keys.map((k) => [k, dummy]));
92
+ const base = {
93
+ kysely,
94
+ logger,
95
+ secrets: {
96
+ getSecret: async () => dummy,
97
+ getSecrets: async (keys) => fromKeys(keys),
98
+ },
99
+ variables: {
100
+ getVariable: async () => dummy,
101
+ getVariables: async (keys) => fromKeys(keys),
102
+ },
103
+ };
104
+ return new Proxy(base, {
105
+ get: (target, prop) => typeof prop === 'string' && prop in target ? target[prop] : undefined,
106
+ });
107
+ }
108
+ export async function loadAuthOptions(opts) {
109
+ const sourceFile = findAuthSourceFile(opts.rootDir, opts.srcDirectories);
110
+ if (!sourceFile)
111
+ return null;
112
+ const factory = await loadAuthFactory(sourceFile);
113
+ if (!factory)
114
+ return null;
115
+ const instance = await factory(schemaServicesStub(opts.kysely, opts.logger));
116
+ const options = instance.options;
117
+ return options ?? null;
118
+ }
119
+ export async function getAuthMigrations(authOptions) {
120
+ const getMigrations = await loadGetMigrations();
121
+ return getMigrations(authOptions);
122
+ }
@@ -53,4 +53,37 @@ export declare function migrateAndCodegen(resolved: ResolvedDb): Promise<Migrate
53
53
  export declare function seed(resolved: ResolvedSqliteDb): Promise<SeedResult>;
54
54
  export declare function reset(resolved: ResolvedSqliteDb, rootDir: string): void;
55
55
  export declare function createKysely<DB>(resolved: ResolvedSqliteDb): Promise<Kysely<DB>>;
56
+ type SchemaMap = Map<string, Set<string>>;
57
+ export interface DesiredAuthSchema {
58
+ tables: SchemaMap;
59
+ sql: string;
60
+ }
61
+ export declare function desiredAuthSchema(rootDir: string, srcDirectories: string[], logger: {
62
+ error: (msg: string) => void;
63
+ }): Promise<DesiredAuthSchema | null>;
64
+ export declare function introspectSchema(resolved: ResolvedDb): Promise<SchemaMap>;
65
+ export interface AuthDriftResult {
66
+ hasAuth: boolean;
67
+ inSync: boolean;
68
+ missingTables: string[];
69
+ missingColumns: {
70
+ table: string;
71
+ columns: string[];
72
+ }[];
73
+ }
74
+ export declare function computeAuthDrift(resolved: ResolvedDb, rootDir: string, srcDirectories: string[], logger: {
75
+ error: (msg: string) => void;
76
+ }): Promise<AuthDriftResult>;
77
+ export interface GenerateAuthResult {
78
+ status: 'no-auth' | 'up-to-date' | 'written' | 'incremental-unsupported' | 'unsupported-dialect';
79
+ file?: string;
80
+ missingTables?: string[];
81
+ missingColumns?: {
82
+ table: string;
83
+ columns: string[];
84
+ }[];
85
+ }
86
+ export declare function generateAuthMigration(resolved: ResolvedDb, rootDir: string, srcDirectories: string[], logger: {
87
+ error: (msg: string) => void;
88
+ }): Promise<GenerateAuthResult>;
56
89
  export {};
@@ -1,9 +1,10 @@
1
- import { existsSync, mkdirSync, rmSync, writeFileSync, readFileSync, } from 'node:fs';
1
+ import { existsSync, mkdirSync, rmSync, writeFileSync, readFileSync, readdirSync, } from 'node:fs';
2
2
  import { resolve, isAbsolute, relative, dirname, join } from 'node:path';
3
3
  import { createRequire } from 'node:module';
4
4
  import { runInNewContext } from 'node:vm';
5
5
  import { transformSync } from 'esbuild';
6
6
  import { migrate } from './db-migrator.js';
7
+ import { loadAuthOptions, getAuthMigrations } from './better-auth-schema.js';
7
8
  import { generateSchemaTypes } from './db-codegen.js';
8
9
  import { generateZodTypes } from './zod-codegen.js';
9
10
  import { createCoercionPlugin } from './coercion-plugin.js';
@@ -295,3 +296,126 @@ export async function createKysely(resolved) {
295
296
  plugins: coercionMap ? [createCoercionPlugin({ map: coercionMap })] : [],
296
297
  });
297
298
  }
299
+ async function introspectorToMap(intro) {
300
+ const map = new Map();
301
+ for (const table of await intro.listTables()) {
302
+ const cols = await intro.getColumns(table);
303
+ map.set(table, new Set(cols.map((c) => c.name)));
304
+ }
305
+ return map;
306
+ }
307
+ function diffSchemas(desired, actual) {
308
+ const missingTables = [];
309
+ const missingColumns = [];
310
+ for (const [table, cols] of desired) {
311
+ const actualCols = actual.get(table);
312
+ if (!actualCols) {
313
+ missingTables.push(table);
314
+ continue;
315
+ }
316
+ const missing = [...cols].filter((c) => !actualCols.has(c));
317
+ if (missing.length)
318
+ missingColumns.push({ table, columns: missing });
319
+ }
320
+ return { missingTables, missingColumns };
321
+ }
322
+ export async function desiredAuthSchema(rootDir, srcDirectories, logger) {
323
+ const runtime = await loadSqliteRuntime();
324
+ const db = runtime.open(':memory:');
325
+ try {
326
+ const kysely = createSqliteKysely({ db, camelCase: true });
327
+ const options = await loadAuthOptions({ rootDir, srcDirectories, kysely, logger });
328
+ if (!options)
329
+ return null;
330
+ const { runMigrations, compileMigrations } = await getAuthMigrations(options);
331
+ await runMigrations();
332
+ const tables = await introspectorToMap(new SqliteIntrospector(db));
333
+ const sql = await compileMigrations();
334
+ return { tables, sql };
335
+ }
336
+ finally {
337
+ db.close();
338
+ }
339
+ }
340
+ export async function introspectSchema(resolved) {
341
+ if (resolved.dialect === 'sqlite') {
342
+ const runtime = await loadSqliteRuntime();
343
+ const db = runtime.open(resolved.dbFile);
344
+ try {
345
+ return await introspectorToMap(new SqliteIntrospector(db));
346
+ }
347
+ finally {
348
+ db.close();
349
+ }
350
+ }
351
+ const intro = new PostgresIntrospector(resolved.connectionString);
352
+ await intro.connect();
353
+ try {
354
+ return await introspectorToMap(intro);
355
+ }
356
+ finally {
357
+ await intro.close();
358
+ }
359
+ }
360
+ async function coveredSqliteSchema(migrationsDir) {
361
+ const runtime = await loadSqliteRuntime();
362
+ const db = runtime.open(':memory:');
363
+ try {
364
+ await migrate(new SqliteMigrationExecutor(db), migrationsDir);
365
+ return await introspectorToMap(new SqliteIntrospector(db));
366
+ }
367
+ finally {
368
+ db.close();
369
+ }
370
+ }
371
+ export async function computeAuthDrift(resolved, rootDir, srcDirectories, logger) {
372
+ const desired = await desiredAuthSchema(rootDir, srcDirectories, logger);
373
+ if (!desired) {
374
+ return { hasAuth: false, inSync: true, missingTables: [], missingColumns: [] };
375
+ }
376
+ const actual = await introspectSchema(resolved);
377
+ const { missingTables, missingColumns } = diffSchemas(desired.tables, actual);
378
+ return {
379
+ hasAuth: true,
380
+ inSync: missingTables.length === 0 && missingColumns.length === 0,
381
+ missingTables,
382
+ missingColumns,
383
+ };
384
+ }
385
+ function nextMigrationFile(migrationsDir, label) {
386
+ mkdirSync(migrationsDir, { recursive: true });
387
+ let max = 0;
388
+ try {
389
+ for (const file of readdirSync(migrationsDir)) {
390
+ const m = /^(\d+)/.exec(file);
391
+ if (m)
392
+ max = Math.max(max, parseInt(m[1], 10));
393
+ }
394
+ }
395
+ catch {
396
+ max = 0;
397
+ }
398
+ const num = String(max + 1).padStart(4, '0');
399
+ return join(migrationsDir, `${num}-${label}.sql`);
400
+ }
401
+ export async function generateAuthMigration(resolved, rootDir, srcDirectories, logger) {
402
+ if (resolved.dialect !== 'sqlite')
403
+ return { status: 'unsupported-dialect' };
404
+ const desired = await desiredAuthSchema(rootDir, srcDirectories, logger);
405
+ if (!desired)
406
+ return { status: 'no-auth' };
407
+ const covered = await coveredSqliteSchema(resolved.migrationsDir);
408
+ const { missingTables, missingColumns } = diffSchemas(desired.tables, covered);
409
+ if (missingTables.length === 0 && missingColumns.length === 0) {
410
+ return { status: 'up-to-date' };
411
+ }
412
+ const coveredHasAnyAuthTable = [...desired.tables.keys()].some((t) => covered.has(t));
413
+ if (coveredHasAnyAuthTable) {
414
+ return { status: 'incremental-unsupported', missingTables, missingColumns };
415
+ }
416
+ const file = nextMigrationFile(resolved.migrationsDir, 'better-auth');
417
+ const header = '-- Generated by `pikku db generate` from pikkuBetterAuth (Better Auth).\n' +
418
+ '-- Re-run the command after changing the auth config.\n\n';
419
+ writeFileSync(file, header + desired.sql + '\n', 'utf8');
420
+ return { status: 'written', file, missingTables };
421
+ }
@@ -57,12 +57,15 @@ function parseTables(src) {
57
57
  const tables = [];
58
58
  for (const match of src.matchAll(INTERFACE_RE)) {
59
59
  const name = match[1];
60
- if (name === 'DB')
60
+ if (!name || name === 'DB')
61
61
  continue;
62
- const body = match[2];
62
+ const body = match[2] ?? '';
63
63
  const fields = [];
64
64
  for (const field of body.matchAll(FIELD_RE)) {
65
- fields.push({ name: field[1], type: field[2].trim() });
65
+ const fieldName = field[1];
66
+ if (!fieldName)
67
+ continue;
68
+ fields.push({ name: fieldName, type: (field[2] ?? '').trim() });
66
69
  }
67
70
  tables.push({ name, fields });
68
71
  }
@@ -119,7 +122,7 @@ function zodForType(tsType, format) {
119
122
  // Peel a single `Generated<…>` wrapper. For public bool/date columns this
120
123
  // wraps a `ColumnType<…>`, so the unwrapped inner is handled below.
121
124
  const generatedMatch = inner.match(/^Generated<(.+)>$/);
122
- if (generatedMatch) {
125
+ if (generatedMatch?.[1]) {
123
126
  generated = true;
124
127
  inner = generatedMatch[1].trim();
125
128
  }
@@ -146,7 +149,7 @@ function scalarSchema(tsType, format) {
146
149
  let inner = tsType.trim();
147
150
  // Defensive: a Select arg may itself be `Generated<…>` in older schemas.
148
151
  const generatedMatch = inner.match(/^Generated<(.+)>$/);
149
- if (generatedMatch) {
152
+ if (generatedMatch?.[1]) {
150
153
  inner = generatedMatch[1].trim();
151
154
  }
152
155
  const nullable = inner.endsWith(' | null');
@@ -155,7 +158,7 @@ function scalarSchema(tsType, format) {
155
158
  }
156
159
  // Unwrap a classification brand: `Private<T>` / `Pii<T>` / `Secret<T>` → T.
157
160
  const brandMatch = inner.match(/^(?:Private|Pii|Secret)<(.+)>$/);
158
- if (brandMatch) {
161
+ if (brandMatch?.[1]) {
159
162
  inner = brandMatch[1].trim();
160
163
  }
161
164
  let schema;
@@ -61,7 +61,7 @@ async function hasAuthSessionMiddleware(fnDir) {
61
61
  const meta = await readJsonSafe(metaPath);
62
62
  if (!meta?.instances)
63
63
  return false;
64
- return Object.values(meta.instances).some((instance) => instance.definitionId === 'authJsSession');
64
+ return Object.values(meta.instances).some((instance) => instance.definitionId === 'betterAuthSession');
65
65
  }
66
66
  function migrationCreatesTable(sql, tableName) {
67
67
  const escapedTable = tableName.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
@@ -1,17 +1,43 @@
1
+ import { join, dirname } from 'node:path';
1
2
  import { pikkuSessionlessFunc } from '#pikku';
2
3
  import { writeFileInDir } from '../../../utils/file-writer.js';
3
4
  import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
4
5
  import { serializeAuthGen } from './serialize-auth-gen.js';
6
+ import { serializeAuthTypes } from './serialize-auth-types.js';
7
+ import { serializeAuthMeta } from './serialize-auth-meta.js';
5
8
  export const pikkuAuth = pikkuSessionlessFunc({
6
9
  func: async ({ logger, config, getInspectorState }) => {
7
- const { authFile } = config;
10
+ const { authFile, authTypesFile, authMetaJsonFile, functionTypesFile, typesDeclarationFile, secretsFile: secretsServiceFile, variablesFile, packageMappings, } = config;
8
11
  if (!authFile)
9
12
  return;
10
13
  const state = await getInspectorState();
11
- if (state.auth.providers.length === 0)
14
+ // Only generate when the project declares auth via `pikkuBetterAuth`. Gating on
15
+ // the definition (not provider count) means credentials-only auth — which
16
+ // has no OAuth providers — still generates its /auth/* wiring.
17
+ if (!state.auth.definition)
12
18
  return;
13
- const content = serializeAuthGen(state.auth.providers);
14
- await writeFileInDir(logger, authFile, content);
19
+ const { wiring, secrets } = serializeAuthGen(state.auth.definition, state.auth.providers, authFile, typesDeclarationFile, packageMappings ?? {});
20
+ // The secrets file sits alongside authFile so re-inspection rediscovers it.
21
+ // It is kept separate from the wiring file because the CLI forbids Zod
22
+ // schemas and HTTP wiring (wireHTTPRoutes) in the same file (PKU490).
23
+ const secretsFile = join(dirname(authFile), 'auth-secrets.gen.ts');
24
+ await writeFileInDir(logger, authFile, wiring);
25
+ await writeFileInDir(logger, secretsFile, secrets);
26
+ // Static metadata of the enabled providers/plugins for the console SSO page,
27
+ // following the `*-meta.gen.json` convention. Read at runtime by the console
28
+ // getAuthProviders function instead of a runtime registry.
29
+ if (authMetaJsonFile) {
30
+ const meta = serializeAuthMeta(state.auth.definition, state.auth.providers);
31
+ await writeFileInDir(logger, authMetaJsonFile, JSON.stringify(meta, null, 2));
32
+ }
33
+ // Generate the typed pikkuBetterAuth re-export consumed by `import { pikkuBetterAuth } from '#pikku'`.
34
+ if (authTypesFile &&
35
+ functionTypesFile &&
36
+ secretsServiceFile &&
37
+ variablesFile) {
38
+ const authTypes = serializeAuthTypes(authTypesFile, functionTypesFile, secretsServiceFile, variablesFile, packageMappings ?? {});
39
+ await writeFileInDir(logger, authTypesFile, authTypes);
40
+ }
15
41
  },
16
42
  middleware: [
17
43
  logCommandInfoAndTime({
@@ -1 +1,33 @@
1
- export declare const serializeAuthGen: (providers: string[]) => string;
1
+ import type { AuthDefinition } from '@pikku/inspector';
2
+ /**
3
+ * The two files generated from a `pikkuBetterAuth` export.
4
+ *
5
+ * They are split because the CLI's schema/wiring-separation rule (PKU490)
6
+ * forbids a file from declaring Zod schemas AND `wireHTTPRoutes` together —
7
+ * schema files are imported at runtime, which would fire HTTP wiring
8
+ * side-effects without a server context. `wireSecret`/`wireVariable` are NOT
9
+ * HTTP wiring, so schemas may sit alongside them; only the route wiring must be
10
+ * separated out.
11
+ */
12
+ export interface AuthGenOutput {
13
+ /** The HTTP wiring file (authFile): handler + catch-all routes + session middleware. */
14
+ wiring: string;
15
+ /** The secrets file: Zod schemas + wireSecret/wireVariable. */
16
+ secrets: string;
17
+ }
18
+ /**
19
+ * Generates the `auth.gen.ts` (HTTP wiring) and `auth-secrets.gen.ts` (schemas +
20
+ * secret/variable wiring) files from a `pikkuBetterAuth((services) => betterAuth(...))`
21
+ * export.
22
+ *
23
+ * The wiring file side-effect imports the user's auth file (so `pikkuBetterAuth`
24
+ * registers its factory), builds ONE shared sessionless handler that reads the
25
+ * resolved `services.auth` and delegates to better-auth's fetch handler, wires a
26
+ * catch-all `${basePath}{/*splat}` route per method to it, and registers the
27
+ * better-auth session-bridge middleware globally. Provider/plugin metadata for
28
+ * the console is emitted separately as `auth-meta.gen.json` (see
29
+ * serializeAuthMeta). Because this is normal, statically inspectable HTTP
30
+ * wiring, the routes flow through inspection into the deploy manifest (one
31
+ * worker for all auth routes).
32
+ */
33
+ export declare const serializeAuthGen: (definition: AuthDefinition, providers: string[], authFile: string, typesDeclarationFile: string, packageMappings: Record<string, string>) => AuthGenOutput;