@pikku/cli 0.12.24 → 0.12.26

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 (151) hide show
  1. package/cli.schema.json +1 -1
  2. package/console-app/assets/index-Ba9K10XZ.js +232 -0
  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 +21 -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 +50 -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 +1 -1
  18. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  19. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  20. package/dist/.pikku/function/pikku-functions-meta.gen.json +183 -104
  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 +3 -1
  34. package/dist/.pikku/pikku-services.gen.js +2 -0
  35. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  36. package/dist/.pikku/pikku-types.gen.js +1 -1
  37. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  38. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  39. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  40. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  41. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  42. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  43. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +13 -9
  44. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  45. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  46. package/dist/.pikku/schemas/register.gen.js +17 -5
  47. package/dist/.pikku/schemas/schemas/DbAuditInput.schema.json +1 -0
  48. package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
  49. package/dist/.pikku/schemas/schemas/PikkuEmailsOutput.schema.json +1 -0
  50. package/dist/.pikku/schemas/schemas/PikkuFunctionTypesSplitInput.schema.json +1 -0
  51. package/dist/.pikku/schemas/schemas/PikkuTestsCoverageInput.schema.json +1 -1
  52. package/dist/.pikku/schemas/schemas/PikkuTriggerTypesInput.schema.json +1 -0
  53. package/dist/.pikku/schemas/schemas/WorkspaceValidateInput.schema.json +1 -0
  54. package/dist/.pikku/schemas/schemas/WorkspaceValidateOutput.schema.json +1 -0
  55. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  56. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  57. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  58. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  59. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  60. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  61. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  62. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  63. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  64. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  65. package/dist/.pikku/workflow/meta/allWorkflow.gen.json +5 -5
  66. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  67. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  68. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  69. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  70. package/dist/bin/pikku-bin.mjs +2 -2
  71. package/dist/src/cli.wiring.js +39 -0
  72. package/dist/src/fabric/functions/validate-core.d.ts +20 -0
  73. package/dist/src/fabric/functions/validate-core.js +227 -0
  74. package/dist/src/fabric/functions/validate.function.js +12 -4
  75. package/dist/src/functions/commands/bootstrap.js +2 -2
  76. package/dist/src/functions/commands/console.js +7 -4
  77. package/dist/src/functions/commands/db-audit.d.ts +1 -0
  78. package/dist/src/functions/commands/db-audit.js +67 -0
  79. package/dist/src/functions/commands/db-migrate.js +7 -11
  80. package/dist/src/functions/commands/db-reset.js +12 -12
  81. package/dist/src/functions/commands/db-seed.js +11 -11
  82. package/dist/src/functions/commands/db-shared.d.ts +4 -19
  83. package/dist/src/functions/commands/db-shared.js +53 -17
  84. package/dist/src/functions/commands/dev.js +25 -14
  85. package/dist/src/functions/commands/emails-init.d.ts +5 -0
  86. package/dist/src/functions/commands/emails-init.js +162 -0
  87. package/dist/src/functions/commands/load-user-project.js +12 -3
  88. package/dist/src/functions/commands/new-addon.js +2 -2
  89. package/dist/src/functions/commands/tests-coverage.d.ts +3 -0
  90. package/dist/src/functions/commands/tests-coverage.js +34 -0
  91. package/dist/src/functions/commands/watch.js +7 -4
  92. package/dist/src/functions/commands/workspace-validate.d.ts +33 -0
  93. package/dist/src/functions/commands/workspace-validate.js +9 -0
  94. package/dist/src/functions/db/annotation-parser.d.ts +31 -0
  95. package/dist/src/functions/db/annotation-parser.js +93 -0
  96. package/dist/src/functions/db/coercion-plugin.d.ts +7 -0
  97. package/dist/src/functions/db/coercion-plugin.js +99 -0
  98. package/dist/src/functions/db/db-codegen.d.ts +24 -0
  99. package/dist/src/functions/db/db-codegen.js +276 -0
  100. package/dist/src/functions/db/db-introspector.d.ts +15 -0
  101. package/dist/src/functions/db/db-introspector.js +1 -0
  102. package/dist/src/functions/db/db-migrator.d.ts +32 -0
  103. package/dist/src/functions/db/db-migrator.js +65 -0
  104. package/dist/src/functions/db/local-db.d.ts +27 -33
  105. package/dist/src/functions/db/local-db.js +108 -57
  106. package/dist/src/functions/db/postgres/postgres-introspector.d.ts +10 -0
  107. package/dist/src/functions/db/postgres/postgres-introspector.js +54 -0
  108. package/dist/src/functions/db/postgres/postgres-migrator.d.ts +9 -0
  109. package/dist/src/functions/db/postgres/postgres-migrator.js +32 -0
  110. package/dist/src/functions/db/{seed.d.ts → sqlite/seed.d.ts} +2 -2
  111. package/dist/src/functions/db/sqlite/sqlite-introspector.d.ts +9 -0
  112. package/dist/src/functions/db/sqlite/sqlite-introspector.js +35 -0
  113. package/dist/src/functions/db/sqlite/sqlite-kysely.d.ts +8 -0
  114. package/dist/src/functions/db/sqlite/sqlite-kysely.js +62 -0
  115. package/dist/src/functions/db/sqlite/sqlite-migrator.d.ts +10 -0
  116. package/dist/src/functions/db/sqlite/sqlite-migrator.js +36 -0
  117. package/dist/src/functions/db/sqlite/sqlite-runtime-bun.d.ts +2 -0
  118. package/dist/src/functions/db/sqlite/sqlite-runtime-bun.js +52 -0
  119. package/dist/src/functions/db/sqlite/sqlite-runtime-node.d.ts +2 -0
  120. package/dist/src/functions/db/sqlite/sqlite-runtime-node.js +51 -0
  121. package/dist/src/functions/db/sqlite/sqlite-runtime.d.ts +20 -0
  122. package/dist/src/functions/db/sqlite/sqlite-runtime.js +13 -0
  123. package/dist/src/functions/validate/workspace-validate.d.ts +34 -0
  124. package/dist/src/functions/validate/workspace-validate.js +259 -0
  125. package/dist/src/functions/wirings/ai-agent/serialize-public-agent.js +2 -1
  126. package/dist/src/functions/wirings/cli/pikku-command-cli-types.js +1 -1
  127. package/dist/src/functions/wirings/console/serialize-console-functions.js +4 -4
  128. package/dist/src/functions/wirings/emails/pikku-command-emails.d.ts +6 -0
  129. package/dist/src/functions/wirings/emails/pikku-command-emails.js +172 -0
  130. package/dist/src/functions/wirings/emails/serialize-emails.d.ts +20 -0
  131. package/dist/src/functions/wirings/emails/serialize-emails.js +168 -0
  132. package/dist/src/functions/wirings/functions/pikku-command-function-types-split.d.ts +7 -1
  133. package/dist/src/functions/wirings/functions/pikku-command-function-types-split.js +2 -2
  134. package/dist/src/functions/wirings/functions/serialize-addon-types.js +1 -1
  135. package/dist/src/functions/wirings/triggers/pikku-command-trigger-types.d.ts +7 -1
  136. package/dist/src/functions/wirings/triggers/pikku-command-trigger-types.js +2 -2
  137. package/dist/src/functions/wirings/workflow/pikku-command-workflow.js +1 -1
  138. package/dist/src/functions/workflows/all.workflow.js +12 -7
  139. package/dist/src/scaffold/rpc-remote.gen.js +1 -1
  140. package/dist/src/services.js +2 -0
  141. package/dist/src/utils/pikku-cli-config.js +6 -0
  142. package/dist/tsconfig.tsbuildinfo +1 -1
  143. package/package.json +6 -4
  144. package/skills/pikku-auth-js/SKILL.md +271 -58
  145. package/skills/pikku-testing/SKILL.md +208 -0
  146. package/console-app/assets/index-BDOqBctb.js +0 -232
  147. package/dist/src/functions/db/sql-migrator.d.ts +0 -26
  148. package/dist/src/functions/db/sql-migrator.js +0 -104
  149. package/dist/src/functions/db/sqlite-codegen.d.ts +0 -45
  150. package/dist/src/functions/db/sqlite-codegen.js +0 -294
  151. /package/dist/src/functions/db/{seed.js → sqlite/seed.js} +0 -0
@@ -1,65 +1,125 @@
1
- import { DatabaseSync } from 'node:sqlite';
2
1
  import { existsSync, mkdirSync, rmSync } from 'node:fs';
3
2
  import { resolve, isAbsolute, relative, dirname, join } from 'node:path';
4
- import { createNodeSqliteKysely, createCoercionPlugin, } from '@pikku/kysely-node-sqlite';
5
- import { migrate } from './sql-migrator.js';
6
- import { generateSchemaTypes } from './sqlite-codegen.js';
3
+ import { migrate } from './db-migrator.js';
4
+ import { generateSchemaTypes } from './db-codegen.js';
7
5
  import { generateZodTypes } from './zod-codegen.js';
8
- import { seed as runSeed } from './seed.js';
6
+ import { createCoercionPlugin } from './coercion-plugin.js';
7
+ import { SqliteMigrationExecutor } from './sqlite/sqlite-migrator.js';
8
+ import { SqliteIntrospector } from './sqlite/sqlite-introspector.js';
9
+ import { createSqliteKysely } from './sqlite/sqlite-kysely.js';
10
+ import { loadSqliteRuntime } from './sqlite/sqlite-runtime.js';
11
+ import { seed as runSeed } from './sqlite/seed.js';
12
+ import { PostgresMigrationExecutor } from './postgres/postgres-migrator.js';
13
+ import { PostgresIntrospector } from './postgres/postgres-introspector.js';
14
+ // ─── Resolution ───────────────────────────────────────────────────────────────
9
15
  /**
10
- * Resolve a DevDbConfig into absolute paths.
11
- * - dbFile lives under runtimeDir (default: <rootDir>/.pikku-runtime)
12
- * - schema/coercion/zod are generated into outDir/db
13
- * - migrations and seed are authored source under rootDir/db
16
+ * Resolve a UserConfigShape into an absolute-path descriptor.
17
+ * Returns null when neither sqliteDb nor postgresUrl is configured.
14
18
  */
15
- export function resolveLocalDb(config, rootDir, outDir, runtimeDir) {
16
- if (!config)
17
- return null;
18
- const file = config === true ? undefined : config.file;
19
- const resolvedRuntimeDir = runtimeDir ?? join(rootDir, '.pikku-runtime');
20
- return {
21
- dbFile: file
22
- ? resolveAgainst(rootDir, file)
23
- : join(resolvedRuntimeDir, 'dev.db'),
24
- runtimeDir: resolvedRuntimeDir,
25
- migrationsDir: resolveAgainst(rootDir, 'db/migrations'),
26
- seedFile: resolveAgainst(rootDir, 'db/seed.sql'),
19
+ export function resolveDb(userConfig, rootDir, outDir, runtimeDir) {
20
+ const base = (sub) => ({
21
+ migrationsDir: resolveAgainst(rootDir, sub),
27
22
  schemaFile: join(outDir, 'db', 'schema.d.ts'),
28
23
  coercionFile: join(outDir, 'db', 'coercion.gen.ts'),
24
+ manifestFile: join(outDir, 'db', 'classification.gen.ts'),
29
25
  zodFile: join(outDir, 'db', 'zod.gen.ts'),
30
26
  camelCase: true,
31
- };
27
+ });
28
+ if (userConfig.postgresUrl && userConfig.sqliteDb) {
29
+ throw new Error('Both postgresUrl and sqliteDb are set. Configure exactly one database dialect.');
30
+ }
31
+ if (userConfig.postgresUrl) {
32
+ return {
33
+ dialect: 'postgres',
34
+ connectionString: userConfig.postgresUrl,
35
+ ...base('db/postgres'),
36
+ };
37
+ }
38
+ if (userConfig.sqliteDb) {
39
+ const resolvedRuntimeDir = runtimeDir
40
+ ? resolveAgainst(rootDir, runtimeDir)
41
+ : join(rootDir, '.pikku-runtime');
42
+ return {
43
+ dialect: 'sqlite',
44
+ dbFile: resolveAgainst(rootDir, userConfig.sqliteDb),
45
+ runtimeDir: resolvedRuntimeDir,
46
+ seedFile: resolveAgainst(rootDir, 'db/seed.sql'),
47
+ ...base('db/sqlite'),
48
+ };
49
+ }
50
+ return null;
51
+ }
52
+ /** @deprecated Use resolveDb(userConfig, ...) instead. */
53
+ export function resolveLocalDb(sqliteDb, rootDir, outDir, runtimeDir) {
54
+ if (!sqliteDb)
55
+ return null;
56
+ const result = resolveDb({ sqliteDb }, rootDir, outDir, runtimeDir);
57
+ return result;
32
58
  }
33
59
  function resolveAgainst(root, p) {
34
60
  return isAbsolute(p) ? p : resolve(root, p);
35
61
  }
36
- /**
37
- * Run the migrate routine (open tracking-table → drift-check → apply →
38
- * codegen close). Used by both `pikku db migrate` and `pikku dev` boot.
39
- */
40
- export function migrateAndCodegen(resolved) {
41
- mkdirSync(dirname(resolved.dbFile), { recursive: true });
42
- const db = new DatabaseSync(resolved.dbFile);
62
+ export async function migrateAndCodegen(resolved) {
63
+ if (resolved.dialect === 'sqlite') {
64
+ mkdirSync(dirname(resolved.dbFile), { recursive: true });
65
+ const runtime = await loadSqliteRuntime();
66
+ const db = runtime.open(resolved.dbFile);
67
+ try {
68
+ const executor = new SqliteMigrationExecutor(db);
69
+ const migrateResult = await migrate(executor, resolved.migrationsDir);
70
+ const introspector = new SqliteIntrospector(db);
71
+ const codegenResult = await generateSchemaTypes(introspector, {
72
+ outFile: resolved.schemaFile,
73
+ coercionFile: resolved.coercionFile,
74
+ manifestFile: resolved.manifestFile,
75
+ camelCase: resolved.camelCase,
76
+ migrationsDir: resolved.migrationsDir,
77
+ });
78
+ const zodResult = generateZodTypes({
79
+ schemaFile: resolved.schemaFile,
80
+ outFile: resolved.zodFile,
81
+ });
82
+ return { migrate: migrateResult, codegen: codegenResult, zod: zodResult };
83
+ }
84
+ finally {
85
+ db.close();
86
+ }
87
+ }
88
+ // Postgres
89
+ const introspector = new PostgresIntrospector(resolved.connectionString);
90
+ await introspector.connect();
43
91
  try {
44
- const migrateResult = migrate(db, resolved.migrationsDir);
45
- const codegenResult = generateSchemaTypes(db, {
46
- outFile: resolved.schemaFile,
47
- coercionFile: resolved.coercionFile,
48
- camelCase: resolved.camelCase,
49
- migrationsDir: resolved.migrationsDir,
50
- });
51
- const zodResult = generateZodTypes({
52
- schemaFile: resolved.schemaFile,
53
- outFile: resolved.zodFile,
54
- });
55
- return { migrate: migrateResult, codegen: codegenResult, zod: zodResult };
92
+ const { Client } = await import('pg');
93
+ const client = new Client({ connectionString: resolved.connectionString });
94
+ await client.connect();
95
+ try {
96
+ const executor = new PostgresMigrationExecutor(client);
97
+ const migrateResult = await migrate(executor, resolved.migrationsDir);
98
+ const codegenResult = await generateSchemaTypes(introspector, {
99
+ outFile: resolved.schemaFile,
100
+ coercionFile: resolved.coercionFile,
101
+ manifestFile: resolved.manifestFile,
102
+ camelCase: resolved.camelCase,
103
+ migrationsDir: resolved.migrationsDir,
104
+ });
105
+ const zodResult = generateZodTypes({
106
+ schemaFile: resolved.schemaFile,
107
+ outFile: resolved.zodFile,
108
+ });
109
+ return { migrate: migrateResult, codegen: codegenResult, zod: zodResult };
110
+ }
111
+ finally {
112
+ await client.end();
113
+ }
56
114
  }
57
115
  finally {
58
- db.close();
116
+ await introspector.close();
59
117
  }
60
118
  }
61
- export function seed(resolved) {
62
- const db = new DatabaseSync(resolved.dbFile);
119
+ // ─── SQLite-only operations ───────────────────────────────────────────────────
120
+ export async function seed(resolved) {
121
+ const runtime = await loadSqliteRuntime();
122
+ const db = runtime.open(resolved.dbFile);
63
123
  try {
64
124
  return runSeed(db, resolved.seedFile);
65
125
  }
@@ -67,30 +127,21 @@ export function seed(resolved) {
67
127
  db.close();
68
128
  }
69
129
  }
70
- /**
71
- * Delete the dev DB file. Refuses if NODE_ENV is 'production' or the
72
- * resolved file lives outside the project root (defensive against
73
- * misconfigured absolute paths).
74
- */
75
130
  export function reset(resolved, rootDir) {
76
131
  if (process.env.NODE_ENV === 'production') {
77
132
  throw new Error(`pikku db reset refused: NODE_ENV=production. This command only runs in dev.`);
78
133
  }
79
134
  const rel = relative(resolved.runtimeDir, resolved.dbFile);
80
135
  if (rel.startsWith('..') || isAbsolute(rel)) {
81
- throw new Error(`pikku db reset refused: resolved DB file (${resolved.dbFile}) is outside the runtime directory (${resolved.runtimeDir}). Override dev.db.file or set runtimeDir correctly.`);
136
+ throw new Error(`pikku db reset refused: resolved DB file (${resolved.dbFile}) is outside the runtime directory (${resolved.runtimeDir}). Override sqliteDb or set runtimeDir correctly.`);
82
137
  }
83
138
  if (existsSync(resolved.dbFile)) {
84
139
  rmSync(resolved.dbFile);
85
140
  }
86
141
  }
87
- /**
88
- * Construct the user-facing Kysely instance for the dev DB. Used by
89
- * `pikku dev` to populate inMemoryServices.kysely.
90
- * Wires the coercion plugin when db/coercion.gen.ts exists.
91
- */
92
142
  export async function createKysely(resolved) {
93
143
  mkdirSync(dirname(resolved.dbFile), { recursive: true });
144
+ const runtime = await loadSqliteRuntime();
94
145
  let coercionMap;
95
146
  try {
96
147
  const mod = await import(resolved.coercionFile);
@@ -99,8 +150,8 @@ export async function createKysely(resolved) {
99
150
  catch {
100
151
  // coercion.gen.ts not yet generated — run `pikku db migrate` first
101
152
  }
102
- return createNodeSqliteKysely({
103
- filename: resolved.dbFile,
153
+ return createSqliteKysely({
154
+ db: runtime.open(resolved.dbFile),
104
155
  camelCase: resolved.camelCase,
105
156
  plugins: coercionMap ? [createCoercionPlugin({ map: coercionMap })] : [],
106
157
  });
@@ -0,0 +1,10 @@
1
+ import { Client } from 'pg';
2
+ import type { DbIntrospector, ColumnInfo } from '../db-introspector.js';
3
+ export declare class PostgresIntrospector implements DbIntrospector {
4
+ private client;
5
+ constructor(connectionStringOrClient: string | Client);
6
+ connect(): Promise<void>;
7
+ listTables(): Promise<string[]>;
8
+ getColumns(table: string): Promise<ColumnInfo[]>;
9
+ close(): Promise<void>;
10
+ }
@@ -0,0 +1,54 @@
1
+ import { Client } from 'pg';
2
+ export class PostgresIntrospector {
3
+ client;
4
+ constructor(connectionStringOrClient) {
5
+ this.client =
6
+ typeof connectionStringOrClient === 'string'
7
+ ? new Client({ connectionString: connectionStringOrClient })
8
+ : connectionStringOrClient;
9
+ }
10
+ async connect() {
11
+ await this.client.connect();
12
+ }
13
+ async listTables() {
14
+ const result = await this.client.query(`SELECT table_name
15
+ FROM information_schema.tables
16
+ WHERE table_schema = 'public' AND table_type = 'BASE TABLE'
17
+ ORDER BY table_name`);
18
+ return result.rows.map((r) => r.table_name);
19
+ }
20
+ async getColumns(table) {
21
+ const result = await this.client.query(`SELECT
22
+ c.column_name,
23
+ c.data_type,
24
+ c.is_nullable,
25
+ c.column_default,
26
+ c.is_generated,
27
+ EXISTS(
28
+ SELECT 1
29
+ FROM information_schema.table_constraints tc
30
+ JOIN information_schema.key_column_usage kcu
31
+ ON tc.constraint_name = kcu.constraint_name
32
+ AND tc.table_schema = kcu.table_schema
33
+ AND tc.table_name = kcu.table_name
34
+ WHERE tc.constraint_type = 'PRIMARY KEY'
35
+ AND tc.table_schema = 'public'
36
+ AND tc.table_name = $1
37
+ AND kcu.column_name = c.column_name
38
+ ) AS is_pk
39
+ FROM information_schema.columns c
40
+ WHERE c.table_schema = 'public' AND c.table_name = $1
41
+ ORDER BY c.ordinal_position`, [table]);
42
+ return result.rows.map((r) => ({
43
+ name: r.column_name,
44
+ type: r.data_type,
45
+ notNull: r.is_nullable === 'NO',
46
+ pk: Boolean(r.is_pk),
47
+ defaultValue: r.column_default,
48
+ generated: r.is_generated === 'ALWAYS',
49
+ }));
50
+ }
51
+ async close() {
52
+ await this.client.end();
53
+ }
54
+ }
@@ -0,0 +1,9 @@
1
+ import type { Client } from 'pg';
2
+ import type { MigrationExecutor, AppliedMigration } from '../db-migrator.js';
3
+ export declare class PostgresMigrationExecutor implements MigrationExecutor {
4
+ private readonly client;
5
+ constructor(client: Client);
6
+ ensureTrackingTable(): Promise<void>;
7
+ getApplied(): Promise<AppliedMigration[]>;
8
+ runMigration(sql: string, name: string, hash: string): Promise<void>;
9
+ }
@@ -0,0 +1,32 @@
1
+ const TRACKING_TABLE = 'sql_migrations';
2
+ export class PostgresMigrationExecutor {
3
+ client;
4
+ constructor(client) {
5
+ this.client = client;
6
+ }
7
+ async ensureTrackingTable() {
8
+ await this.client.query(`
9
+ CREATE TABLE IF NOT EXISTS ${TRACKING_TABLE} (
10
+ name TEXT PRIMARY KEY,
11
+ hash TEXT NOT NULL,
12
+ applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
13
+ )
14
+ `);
15
+ }
16
+ async getApplied() {
17
+ const { rows } = await this.client.query(`SELECT name, hash, applied_at FROM ${TRACKING_TABLE} ORDER BY name`);
18
+ return rows;
19
+ }
20
+ async runMigration(sql, name, hash) {
21
+ await this.client.query('BEGIN');
22
+ try {
23
+ await this.client.query(sql);
24
+ await this.client.query(`INSERT INTO ${TRACKING_TABLE} (name, hash) VALUES ($1, $2)`, [name, hash]);
25
+ await this.client.query('COMMIT');
26
+ }
27
+ catch (err) {
28
+ await this.client.query('ROLLBACK');
29
+ throw err;
30
+ }
31
+ }
32
+ }
@@ -1,4 +1,4 @@
1
- import type { DatabaseSync } from 'node:sqlite';
1
+ import type { SyncSqliteDatabase } from './sqlite-runtime.js';
2
2
  export interface SeedResult {
3
3
  applied: boolean;
4
4
  bytes: number;
@@ -8,4 +8,4 @@ export interface SeedResult {
8
8
  * (e.g. `INSERT OR IGNORE`, upserts). Returns `applied: false` if the file
9
9
  * doesn't exist; throws on SQL errors.
10
10
  */
11
- export declare function seed(db: DatabaseSync, seedFile: string): SeedResult;
11
+ export declare function seed(db: SyncSqliteDatabase, seedFile: string): SeedResult;
@@ -0,0 +1,9 @@
1
+ import type { DbIntrospector, ColumnInfo } from '../db-introspector.js';
2
+ import type { SyncSqliteDatabase } from './sqlite-runtime.js';
3
+ export declare class SqliteIntrospector implements DbIntrospector {
4
+ private readonly db;
5
+ constructor(db: SyncSqliteDatabase);
6
+ listTables(): Promise<string[]>;
7
+ getColumns(table: string): Promise<ColumnInfo[]>;
8
+ close(): Promise<void>;
9
+ }
@@ -0,0 +1,35 @@
1
+ const SKIP_TABLES = new Set(['sqlite_sequence', 'sql_migrations']);
2
+ export class SqliteIntrospector {
3
+ db;
4
+ constructor(db) {
5
+ this.db = db;
6
+ }
7
+ async listTables() {
8
+ const rows = this.db
9
+ .prepare(`SELECT name FROM sqlite_master
10
+ WHERE type = 'table'
11
+ AND name NOT LIKE 'sqlite\\_%' ESCAPE '\\'
12
+ ORDER BY name`)
13
+ .all();
14
+ return rows.map((r) => r.name).filter((n) => !SKIP_TABLES.has(n));
15
+ }
16
+ async getColumns(table) {
17
+ const escaped = `"${table.replace(/"/g, '""')}"`;
18
+ const rows = this.db
19
+ .prepare(`PRAGMA table_xinfo(${escaped})`)
20
+ .all();
21
+ return rows
22
+ .filter((c) => c.hidden !== 1)
23
+ .map((c) => ({
24
+ name: c.name,
25
+ type: c.type,
26
+ notNull: Boolean(c.notnull),
27
+ pk: c.pk > 0,
28
+ defaultValue: c.dflt_value != null ? String(c.dflt_value) : null,
29
+ generated: c.hidden === 2 || c.hidden === 3,
30
+ }));
31
+ }
32
+ async close() {
33
+ this.db.close();
34
+ }
35
+ }
@@ -0,0 +1,8 @@
1
+ import { Kysely, type KyselyPlugin } from 'kysely';
2
+ import type { SyncSqliteDatabase } from './sqlite-runtime.js';
3
+ export interface CreateSqliteKyselyOptions {
4
+ db: SyncSqliteDatabase;
5
+ camelCase?: boolean;
6
+ plugins?: KyselyPlugin[];
7
+ }
8
+ export declare function createSqliteKysely<DB>(options: CreateSqliteKyselyOptions): Kysely<DB>;
@@ -0,0 +1,62 @@
1
+ import { Kysely, SqliteDialect, CamelCasePlugin, } from 'kysely';
2
+ function coerce(v) {
3
+ if (v === null || v === undefined)
4
+ return null;
5
+ if (typeof v === 'boolean')
6
+ return v ? 1 : 0;
7
+ if (v instanceof Date)
8
+ return v.toISOString();
9
+ if (v instanceof Uint8Array)
10
+ return v;
11
+ if (typeof v === 'object')
12
+ return JSON.stringify(v);
13
+ return v;
14
+ }
15
+ class RuntimeSqliteStatement {
16
+ stmt;
17
+ reader;
18
+ constructor(stmt) {
19
+ this.stmt = stmt;
20
+ this.reader = Boolean(stmt.reader);
21
+ }
22
+ all(parameters) {
23
+ return this.stmt.all(...parameters.map(coerce));
24
+ }
25
+ *iterate(parameters) {
26
+ for (const row of this.stmt.iterate(...parameters.map(coerce))) {
27
+ yield row;
28
+ }
29
+ }
30
+ run(parameters) {
31
+ const result = this.stmt.run(...parameters.map(coerce));
32
+ return {
33
+ changes: result.changes,
34
+ lastInsertRowid: result.lastInsertRowid,
35
+ };
36
+ }
37
+ }
38
+ class RuntimeSqliteDatabase {
39
+ db;
40
+ constructor(db) {
41
+ this.db = db;
42
+ }
43
+ prepare(sql) {
44
+ return new RuntimeSqliteStatement(this.db.prepare(sql));
45
+ }
46
+ close() {
47
+ this.db.close();
48
+ }
49
+ }
50
+ export function createSqliteKysely(options) {
51
+ const plugins = [];
52
+ if (options.camelCase ?? true)
53
+ plugins.push(new CamelCasePlugin());
54
+ if (options.plugins)
55
+ plugins.push(...options.plugins);
56
+ return new Kysely({
57
+ dialect: new SqliteDialect({
58
+ database: new RuntimeSqliteDatabase(options.db),
59
+ }),
60
+ plugins,
61
+ });
62
+ }
@@ -0,0 +1,10 @@
1
+ import type { MigrationExecutor, AppliedMigration } from '../db-migrator.js';
2
+ import type { SyncSqliteDatabase } from './sqlite-runtime.js';
3
+ export declare class SqliteMigrationExecutor implements MigrationExecutor {
4
+ private readonly db;
5
+ constructor(db: SyncSqliteDatabase);
6
+ ensureTrackingTable(): Promise<void>;
7
+ getApplied(): Promise<AppliedMigration[]>;
8
+ runMigration(sql: string, name: string, hash: string): Promise<void>;
9
+ }
10
+ export declare function dropTrackingTable(db: SyncSqliteDatabase): void;
@@ -0,0 +1,36 @@
1
+ const TRACKING_TABLE = 'sql_migrations';
2
+ export class SqliteMigrationExecutor {
3
+ db;
4
+ constructor(db) {
5
+ this.db = db;
6
+ }
7
+ async ensureTrackingTable() {
8
+ this.db.exec(`CREATE TABLE IF NOT EXISTS ${TRACKING_TABLE} (
9
+ name TEXT PRIMARY KEY,
10
+ hash TEXT NOT NULL,
11
+ applied_at TEXT NOT NULL DEFAULT (datetime('now'))
12
+ )`);
13
+ }
14
+ async getApplied() {
15
+ return this.db
16
+ .prepare(`SELECT name, hash, applied_at FROM ${TRACKING_TABLE} ORDER BY name`)
17
+ .all();
18
+ }
19
+ async runMigration(sql, name, hash) {
20
+ this.db.exec('BEGIN');
21
+ try {
22
+ this.db.exec(sql);
23
+ this.db
24
+ .prepare(`INSERT INTO ${TRACKING_TABLE} (name, hash) VALUES (?, ?)`)
25
+ .run(name, hash);
26
+ this.db.exec('COMMIT');
27
+ }
28
+ catch (err) {
29
+ this.db.exec('ROLLBACK');
30
+ throw err;
31
+ }
32
+ }
33
+ }
34
+ export function dropTrackingTable(db) {
35
+ db.exec(`DROP TABLE IF EXISTS ${TRACKING_TABLE}`);
36
+ }
@@ -0,0 +1,2 @@
1
+ import type { SqliteRuntime } from './sqlite-runtime.js';
2
+ export declare const bunSqliteRuntime: SqliteRuntime;
@@ -0,0 +1,52 @@
1
+ import { Database } from 'bun:sqlite';
2
+ class BunSqliteStatement {
3
+ stmt;
4
+ reader;
5
+ constructor(stmt, reader) {
6
+ this.stmt = stmt;
7
+ this.reader = reader;
8
+ }
9
+ all(...parameters) {
10
+ return this.stmt.all(...parameters);
11
+ }
12
+ get(...parameters) {
13
+ return this.stmt.get(...parameters) ?? null;
14
+ }
15
+ iterate(...parameters) {
16
+ return this.stmt.iterate(...parameters);
17
+ }
18
+ run(...parameters) {
19
+ const result = this.stmt.run(...parameters);
20
+ return {
21
+ changes: result.changes,
22
+ lastInsertRowid: result.lastInsertRowid,
23
+ };
24
+ }
25
+ }
26
+ class BunSqliteDatabase {
27
+ db;
28
+ constructor(db) {
29
+ this.db = db;
30
+ }
31
+ exec(sql) {
32
+ this.db.exec(sql);
33
+ }
34
+ prepare(sql) {
35
+ return new BunSqliteStatement(this.db.prepare(sql), isReaderSql(sql));
36
+ }
37
+ close() {
38
+ this.db.close();
39
+ }
40
+ }
41
+ function isReaderSql(sql) {
42
+ const normalized = sql.trimStart().toUpperCase();
43
+ return (normalized.startsWith('SELECT') ||
44
+ normalized.startsWith('WITH') ||
45
+ normalized.startsWith('PRAGMA') ||
46
+ normalized.startsWith('EXPLAIN'));
47
+ }
48
+ export const bunSqliteRuntime = {
49
+ open(filename) {
50
+ return new BunSqliteDatabase(new Database(filename));
51
+ },
52
+ };
@@ -0,0 +1,2 @@
1
+ import type { SqliteRuntime } from './sqlite-runtime.js';
2
+ export declare function createNodeSqliteRuntime(): Promise<SqliteRuntime>;
@@ -0,0 +1,51 @@
1
+ class NodeSqliteStatement {
2
+ stmt;
3
+ reader;
4
+ constructor(stmt) {
5
+ this.stmt = stmt;
6
+ this.reader = Boolean(stmt.reader);
7
+ }
8
+ all(...parameters) {
9
+ return this.stmt.all(...parameters);
10
+ }
11
+ get(...parameters) {
12
+ return this.stmt.get(...parameters) ?? null;
13
+ }
14
+ iterate(...parameters) {
15
+ return this.stmt.iterate(...parameters);
16
+ }
17
+ run(...parameters) {
18
+ const result = this.stmt.run(...parameters);
19
+ return {
20
+ changes: result.changes,
21
+ lastInsertRowid: result.lastInsertRowid,
22
+ };
23
+ }
24
+ }
25
+ class NodeSqliteDatabase {
26
+ db;
27
+ constructor(db) {
28
+ this.db = db;
29
+ }
30
+ exec(sql) {
31
+ this.db.exec(sql);
32
+ }
33
+ prepare(sql) {
34
+ return new NodeSqliteStatement(this.db.prepare(sql));
35
+ }
36
+ close() {
37
+ this.db.close();
38
+ }
39
+ }
40
+ async function importNodeSqlite() {
41
+ const dynamicImport = new Function('return import("node:sqlite")');
42
+ return dynamicImport();
43
+ }
44
+ export async function createNodeSqliteRuntime() {
45
+ const { DatabaseSync } = await importNodeSqlite();
46
+ return {
47
+ open(filename) {
48
+ return new NodeSqliteDatabase(new DatabaseSync(filename));
49
+ },
50
+ };
51
+ }
@@ -0,0 +1,20 @@
1
+ export interface SyncSqliteChanges {
2
+ changes: number | bigint;
3
+ lastInsertRowid: number | bigint;
4
+ }
5
+ export interface SyncSqliteStatement {
6
+ reader: boolean;
7
+ all(...parameters: unknown[]): unknown[];
8
+ get(...parameters: unknown[]): unknown | null;
9
+ iterate(...parameters: unknown[]): IterableIterator<unknown>;
10
+ run(...parameters: unknown[]): SyncSqliteChanges;
11
+ }
12
+ export interface SyncSqliteDatabase {
13
+ exec(sql: string): void;
14
+ prepare(sql: string): SyncSqliteStatement;
15
+ close(): void;
16
+ }
17
+ export interface SqliteRuntime {
18
+ open(filename: string): SyncSqliteDatabase;
19
+ }
20
+ export declare function loadSqliteRuntime(): Promise<SqliteRuntime>;
@@ -0,0 +1,13 @@
1
+ let runtimePromise;
2
+ export async function loadSqliteRuntime() {
3
+ runtimePromise ??= (async () => {
4
+ const isBunRuntime = typeof globalThis.Bun !== 'undefined';
5
+ if (isBunRuntime) {
6
+ const { bunSqliteRuntime } = await import('./sqlite-runtime-bun.js');
7
+ return bunSqliteRuntime;
8
+ }
9
+ const { createNodeSqliteRuntime } = await import('./sqlite-runtime-node.js');
10
+ return createNodeSqliteRuntime();
11
+ })();
12
+ return runtimePromise;
13
+ }