@pikku/cli 0.12.37 → 0.12.39

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 (67) hide show
  1. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  2. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  3. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  4. package/dist/.pikku/cli/pikku-cli-channel.js +1 -1
  5. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  6. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  7. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  8. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  9. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  10. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  11. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  12. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  13. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  14. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  15. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  16. package/dist/.pikku/function/pikku-functions-meta.gen.json +164 -164
  17. package/dist/.pikku/function/pikku-functions.gen.js +1 -1
  18. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  19. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  20. package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
  21. package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +1 -1
  22. package/dist/.pikku/http/pikku-http-wirings.gen.js +1 -1
  23. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  24. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  25. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  26. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  27. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  28. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  29. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  30. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  31. package/dist/.pikku/pikku-types.gen.js +1 -1
  32. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  33. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  34. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  35. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  36. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  37. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  38. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +1 -1
  39. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  40. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  41. package/dist/.pikku/schemas/register.gen.js +7 -7
  42. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  43. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  44. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  45. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  46. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  47. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  48. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  49. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  50. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  51. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  52. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  53. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  54. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  55. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  56. package/dist/bin/pikku-bin.mjs +2 -2
  57. package/dist/src/fabric/functions/validate-core.js +7 -7
  58. package/dist/src/fabric/functions/validate.function.js +36 -28
  59. package/dist/src/fabric/lib/config.d.ts +4 -4
  60. package/dist/src/fabric/lib/config.js +2 -2
  61. package/dist/src/functions/db/better-auth-schema.js +33 -11
  62. package/dist/src/functions/db/db-codegen.js +9 -5
  63. package/dist/src/functions/db/local-db.d.ts +2 -2
  64. package/dist/src/functions/db/local-db.js +123 -6
  65. package/dist/src/functions/db/sqlite/sqlite-runtime-node.js +2 -1
  66. package/dist/src/scaffold/rpc-remote.gen.js +1 -1
  67. package/package.json +1 -1
@@ -3,6 +3,7 @@ import { pathToFileURL } from 'node:url';
3
3
  import { readdirSync, statSync, readFileSync, existsSync } from 'node:fs';
4
4
  import { join, extname, dirname } from 'node:path';
5
5
  import { PIKKU_BETTER_AUTH } from '@pikku/better-auth';
6
+ import { LocalSecretService, LocalVariablesService } from '@pikku/core/services';
6
7
  import { loadUserModule } from '../commands/load-user-project.js';
7
8
  let cachedGetMigrations = null;
8
9
  async function loadGetMigrations() {
@@ -87,24 +88,43 @@ async function loadAuthFactory(sourceFile) {
87
88
  return null;
88
89
  }
89
90
  function schemaServicesStub(kysely, logger) {
90
- const dummy = 'x'.repeat(32);
91
- const fromKeys = (keys) => Object.fromEntries(keys.map((k) => [k, dummy]));
91
+ const variables = new LocalVariablesService();
92
+ const secrets = new LocalSecretService(variables);
92
93
  const base = {
93
94
  kysely,
94
95
  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
- },
96
+ secrets,
97
+ variables,
103
98
  };
104
99
  return new Proxy(base, {
105
100
  get: (target, prop) => typeof prop === 'string' && prop in target ? target[prop] : undefined,
106
101
  });
107
102
  }
103
+ function findUserConfigFactoryFile(rootDir, srcDirectories) {
104
+ for (const srcDir of srcDirectories) {
105
+ for (const name of ['config.ts', 'config.js']) {
106
+ const candidate = join(rootDir, srcDir, name);
107
+ if (existsSync(candidate))
108
+ return candidate;
109
+ }
110
+ }
111
+ for (const name of ['config.ts', 'config.js']) {
112
+ const candidate = join(rootDir, name);
113
+ if (existsSync(candidate))
114
+ return candidate;
115
+ }
116
+ return null;
117
+ }
118
+ async function loadAuthConfig(opts) {
119
+ const configFactoryFile = findUserConfigFactoryFile(opts.rootDir, opts.srcDirectories);
120
+ if (!configFactoryFile)
121
+ return undefined;
122
+ const configModule = await loadUserModule(configFactoryFile);
123
+ const userCreateConfig = configModule.createConfig;
124
+ if (typeof userCreateConfig !== 'function')
125
+ return undefined;
126
+ return userCreateConfig(new LocalVariablesService());
127
+ }
108
128
  export async function loadAuthOptions(opts) {
109
129
  const sourceFile = findAuthSourceFile(opts.rootDir, opts.srcDirectories);
110
130
  if (!sourceFile)
@@ -112,7 +132,9 @@ export async function loadAuthOptions(opts) {
112
132
  const factory = await loadAuthFactory(sourceFile);
113
133
  if (!factory)
114
134
  return null;
115
- const instance = await factory(schemaServicesStub(opts.kysely, opts.logger));
135
+ const services = schemaServicesStub(opts.kysely, opts.logger);
136
+ services.config = await loadAuthConfig(opts);
137
+ const instance = await factory(services);
116
138
  const options = instance.options;
117
139
  return options ?? null;
118
140
  }
@@ -28,6 +28,12 @@ function snakeToPascal(name) {
28
28
  .map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
29
29
  .join('');
30
30
  }
31
+ function tableToInterfaceName(name) {
32
+ return name
33
+ .split('.')
34
+ .map((part) => snakeToPascal(part))
35
+ .join('');
36
+ }
31
37
  function snakeToCamel(name) {
32
38
  return name.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
33
39
  }
@@ -110,9 +116,7 @@ function columnTypeExpression(col, annotation, classification) {
110
116
  return wrap(nullable ? 'Uuid | null' : 'Uuid');
111
117
  }
112
118
  if (annotation?.tsType) {
113
- const base = nullable
114
- ? `${annotation.tsType} | null`
115
- : annotation.tsType;
119
+ const base = nullable ? `${annotation.tsType} | null` : annotation.tsType;
116
120
  return wrap(base);
117
121
  }
118
122
  if (annotation?.kind === 'json') {
@@ -146,7 +150,7 @@ function bareTableName(name) {
146
150
  return dot >= 0 ? name.slice(dot + 1) : name;
147
151
  }
148
152
  function emitInterface(table, camelCase, explicitAnnotations, dialect, enumByName, formatHints, warnings) {
149
- const ifaceName = snakeToPascal(table.name);
153
+ const ifaceName = tableToInterfaceName(table.name);
150
154
  const bare = bareTableName(table.name);
151
155
  const tableCols = explicitAnnotations[bare] ?? {};
152
156
  const fields = table.columns
@@ -352,7 +356,7 @@ export async function generateSchemaTypes(introspector, options) {
352
356
  const safe = /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableKey)
353
357
  ? tableKey
354
358
  : JSON.stringify(tableKey);
355
- return ` ${safe}: ${snakeToPascal(t.name)}`;
359
+ return ` ${safe}: ${tableToInterfaceName(t.name)}`;
356
360
  })
357
361
  .join('\n');
358
362
  const schemaBody = [
@@ -1,4 +1,4 @@
1
- import type { Kysely } from 'kysely';
1
+ import { Kysely } from 'kysely';
2
2
  import { type MigrateResult } from './db-migrator.js';
3
3
  import { type CodegenResult } from './db-codegen.js';
4
4
  import { type ZodCodegenResult } from './zod-codegen.js';
@@ -58,7 +58,7 @@ export interface DesiredAuthSchema {
58
58
  tables: SchemaMap;
59
59
  sql: string;
60
60
  }
61
- export declare function desiredAuthSchema(rootDir: string, srcDirectories: string[], logger: {
61
+ export declare function desiredAuthSchema(resolved: ResolvedDb, rootDir: string, srcDirectories: string[], logger: {
62
62
  error: (msg: string) => void;
63
63
  }): Promise<DesiredAuthSchema | null>;
64
64
  export declare function introspectSchema(resolved: ResolvedDb): Promise<SchemaMap>;
@@ -3,6 +3,7 @@ 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
+ import { CamelCasePlugin, CompiledQuery, Kysely, PostgresDialect } from 'kysely';
6
7
  import { migrate } from './db-migrator.js';
7
8
  import { loadAuthOptions, getAuthMigrations } from './better-auth-schema.js';
8
9
  import { generateSchemaTypes } from './db-codegen.js';
@@ -307,8 +308,19 @@ async function introspectorToMap(intro) {
307
308
  function diffSchemas(desired, actual) {
308
309
  const missingTables = [];
309
310
  const missingColumns = [];
311
+ const findSchemaQualifiedMatch = (table) => {
312
+ if (table.includes('.'))
313
+ return undefined;
314
+ const matches = [...actual.entries()].filter(([actualTable]) => {
315
+ const parts = actualTable.split('.');
316
+ return parts.length === 2 && parts[1] === table;
317
+ });
318
+ if (matches.length !== 1)
319
+ return undefined;
320
+ return matches[0][1];
321
+ };
310
322
  for (const [table, cols] of desired) {
311
- const actualCols = actual.get(table);
323
+ const actualCols = actual.get(table) ?? findSchemaQualifiedMatch(table);
312
324
  if (!actualCols) {
313
325
  missingTables.push(table);
314
326
  continue;
@@ -319,14 +331,114 @@ function diffSchemas(desired, actual) {
319
331
  }
320
332
  return { missingTables, missingColumns };
321
333
  }
322
- export async function desiredAuthSchema(rootDir, srcDirectories, logger) {
334
+ function isPostgresAuthDatabase(options) {
335
+ return options.database?.type === 'postgres';
336
+ }
337
+ function createScratchPostgresSchemaName() {
338
+ const random = Math.random().toString(36).slice(2, 10);
339
+ return `pikku_auth_${Date.now().toString(36)}_${random}`;
340
+ }
341
+ async function postgresSchemaToMap(connectionString, schema) {
342
+ const { Client } = await import('pg');
343
+ const client = new Client({ connectionString });
344
+ await client.connect();
345
+ try {
346
+ const tablesResult = await client.query(`SELECT table_name
347
+ FROM information_schema.tables
348
+ WHERE table_schema = $1
349
+ AND table_type = 'BASE TABLE'
350
+ ORDER BY table_name`, [schema]);
351
+ const map = new Map();
352
+ for (const { table_name } of tablesResult.rows) {
353
+ const columnsResult = await client.query(`SELECT column_name
354
+ FROM information_schema.columns
355
+ WHERE table_schema = $1
356
+ AND table_name = $2
357
+ ORDER BY ordinal_position`, [schema, table_name]);
358
+ map.set(table_name, new Set(columnsResult.rows.map((c) => c.column_name)));
359
+ }
360
+ return map;
361
+ }
362
+ finally {
363
+ await client.end();
364
+ }
365
+ }
366
+ async function desiredPostgresAuthSchema(resolved, rootDir, srcDirectories, logger) {
367
+ const { Pool } = await import('pg');
368
+ const schema = createScratchPostgresSchemaName();
369
+ const pool = new Pool({
370
+ connectionString: resolved.connectionString,
371
+ max: 1,
372
+ });
373
+ try {
374
+ const admin = await pool.connect();
375
+ try {
376
+ await admin.query(`CREATE SCHEMA "${schema}"`);
377
+ await admin.query(`SET search_path TO "${schema}"`);
378
+ }
379
+ finally {
380
+ admin.release();
381
+ }
382
+ const kysely = new Kysely({
383
+ dialect: new PostgresDialect({
384
+ pool,
385
+ onReserveConnection: async (connection) => {
386
+ await connection.executeQuery(CompiledQuery.raw(`SET search_path TO "${schema}"`));
387
+ },
388
+ }),
389
+ plugins: [new CamelCasePlugin()],
390
+ }).withSchema(schema);
391
+ try {
392
+ const options = await loadAuthOptions({
393
+ rootDir,
394
+ srcDirectories,
395
+ kysely,
396
+ logger,
397
+ });
398
+ if (!options)
399
+ return null;
400
+ const { runMigrations, compileMigrations } = await getAuthMigrations(options);
401
+ await runMigrations();
402
+ const tables = await postgresSchemaToMap(resolved.connectionString, schema);
403
+ const sql = await compileMigrations();
404
+ return { tables, sql };
405
+ }
406
+ finally {
407
+ await kysely.destroy();
408
+ }
409
+ }
410
+ finally {
411
+ const cleanup = new Pool({
412
+ connectionString: resolved.connectionString,
413
+ max: 1,
414
+ });
415
+ try {
416
+ await cleanup.query(`DROP SCHEMA IF EXISTS "${schema}" CASCADE`);
417
+ }
418
+ finally {
419
+ await cleanup.end();
420
+ }
421
+ }
422
+ }
423
+ export async function desiredAuthSchema(resolved, rootDir, srcDirectories, logger) {
323
424
  const runtime = await loadSqliteRuntime();
324
425
  const db = runtime.open(':memory:');
325
426
  try {
326
427
  const kysely = createSqliteKysely({ db, camelCase: true });
327
- const options = await loadAuthOptions({ rootDir, srcDirectories, kysely, logger });
428
+ const options = await loadAuthOptions({
429
+ rootDir,
430
+ srcDirectories,
431
+ kysely,
432
+ logger,
433
+ });
328
434
  if (!options)
329
435
  return null;
436
+ if (isPostgresAuthDatabase(options)) {
437
+ if (resolved.dialect !== 'postgres') {
438
+ throw new Error('Better Auth database.type is postgres, but the resolved app database is not postgres.');
439
+ }
440
+ return desiredPostgresAuthSchema(resolved, rootDir, srcDirectories, logger);
441
+ }
330
442
  const { runMigrations, compileMigrations } = await getAuthMigrations(options);
331
443
  await runMigrations();
332
444
  const tables = await introspectorToMap(new SqliteIntrospector(db));
@@ -369,9 +481,14 @@ async function coveredSqliteSchema(migrationsDir) {
369
481
  }
370
482
  }
371
483
  export async function computeAuthDrift(resolved, rootDir, srcDirectories, logger) {
372
- const desired = await desiredAuthSchema(rootDir, srcDirectories, logger);
484
+ const desired = await desiredAuthSchema(resolved, rootDir, srcDirectories, logger);
373
485
  if (!desired) {
374
- return { hasAuth: false, inSync: true, missingTables: [], missingColumns: [] };
486
+ return {
487
+ hasAuth: false,
488
+ inSync: true,
489
+ missingTables: [],
490
+ missingColumns: [],
491
+ };
375
492
  }
376
493
  const actual = await introspectSchema(resolved);
377
494
  const { missingTables, missingColumns } = diffSchemas(desired.tables, actual);
@@ -401,7 +518,7 @@ function nextMigrationFile(migrationsDir, label) {
401
518
  export async function generateAuthMigration(resolved, rootDir, srcDirectories, logger) {
402
519
  if (resolved.dialect !== 'sqlite')
403
520
  return { status: 'unsupported-dialect' };
404
- const desired = await desiredAuthSchema(rootDir, srcDirectories, logger);
521
+ const desired = await desiredAuthSchema(resolved, rootDir, srcDirectories, logger);
405
522
  if (!desired)
406
523
  return { status: 'no-auth' };
407
524
  const covered = await coveredSqliteSchema(resolved.migrationsDir);
@@ -15,7 +15,8 @@ class NodeSqliteStatement {
15
15
  upper.startsWith('WITH') ||
16
16
  upper.startsWith('PRAGMA') ||
17
17
  upper.startsWith('EXPLAIN') ||
18
- upper.startsWith('VALUES');
18
+ upper.startsWith('VALUES') ||
19
+ /\bRETURNING\b/.test(upper);
19
20
  }
20
21
  }
21
22
  all(...parameters) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.37
2
+ * This file was generated by @pikku/cli@0.12.39
3
3
  */
4
4
  /**
5
5
  * Auto-generated remote internal RPC queue worker and HTTP endpoint
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/cli",
3
- "version": "0.12.37",
3
+ "version": "0.12.39",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "BUSL-1.1",
6
6
  "imports": {