@pikku/cli 0.12.25 → 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 (115) hide show
  1. package/console-app/assets/{index-D4DgafuS.js → index-Ba9K10XZ.js} +4 -4
  2. package/console-app/index.html +1 -1
  3. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  4. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  5. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  6. package/dist/.pikku/cli/pikku-cli-channel.js +6 -1
  7. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  8. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  9. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  10. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +9 -0
  11. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  12. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  13. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  14. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  15. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  16. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  17. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  18. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  19. package/dist/.pikku/function/pikku-functions-meta.gen.json +116 -101
  20. package/dist/.pikku/function/pikku-functions.gen.js +3 -1
  21. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  22. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  23. package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
  24. package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +1 -1
  25. package/dist/.pikku/http/pikku-http-wirings.gen.js +1 -1
  26. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  27. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  28. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  29. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  30. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  31. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  32. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  33. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  34. package/dist/.pikku/pikku-types.gen.js +1 -1
  35. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  36. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  37. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  38. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  39. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  40. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  41. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +10 -9
  42. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  43. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  44. package/dist/.pikku/schemas/register.gen.js +7 -5
  45. package/dist/.pikku/schemas/schemas/DbAuditInput.schema.json +1 -0
  46. package/dist/.pikku/schemas/schemas/PikkuTestsCoverageInput.schema.json +1 -1
  47. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  48. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  49. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  50. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  51. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  52. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  53. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  54. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  55. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  56. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  57. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  58. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  59. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  60. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  61. package/dist/bin/pikku-bin.mjs +2 -2
  62. package/dist/src/cli.wiring.js +8 -0
  63. package/dist/src/fabric/functions/validate.function.js +1 -1
  64. package/dist/src/functions/commands/db-audit.d.ts +1 -0
  65. package/dist/src/functions/commands/db-audit.js +67 -0
  66. package/dist/src/functions/commands/db-migrate.js +5 -8
  67. package/dist/src/functions/commands/db-reset.js +9 -8
  68. package/dist/src/functions/commands/db-seed.js +9 -8
  69. package/dist/src/functions/commands/db-shared.d.ts +2 -4
  70. package/dist/src/functions/commands/db-shared.js +15 -5
  71. package/dist/src/functions/commands/dev.js +14 -8
  72. package/dist/src/functions/commands/new-addon.js +2 -2
  73. package/dist/src/functions/commands/tests-coverage.d.ts +3 -0
  74. package/dist/src/functions/commands/tests-coverage.js +34 -0
  75. package/dist/src/functions/db/annotation-parser.d.ts +31 -0
  76. package/dist/src/functions/db/annotation-parser.js +93 -0
  77. package/dist/src/functions/db/db-codegen.d.ts +24 -0
  78. package/dist/src/functions/db/db-codegen.js +276 -0
  79. package/dist/src/functions/db/db-introspector.d.ts +15 -0
  80. package/dist/src/functions/db/db-introspector.js +1 -0
  81. package/dist/src/functions/db/db-migrator.d.ts +32 -0
  82. package/dist/src/functions/db/db-migrator.js +65 -0
  83. package/dist/src/functions/db/local-db.d.ts +27 -33
  84. package/dist/src/functions/db/local-db.js +100 -53
  85. package/dist/src/functions/db/postgres/postgres-introspector.d.ts +10 -0
  86. package/dist/src/functions/db/postgres/postgres-introspector.js +54 -0
  87. package/dist/src/functions/db/postgres/postgres-migrator.d.ts +9 -0
  88. package/dist/src/functions/db/postgres/postgres-migrator.js +32 -0
  89. package/dist/src/functions/db/sqlite/sqlite-introspector.d.ts +9 -0
  90. package/dist/src/functions/db/sqlite/sqlite-introspector.js +35 -0
  91. package/dist/src/functions/db/sqlite/sqlite-migrator.d.ts +10 -0
  92. package/dist/src/functions/db/sqlite/sqlite-migrator.js +36 -0
  93. package/dist/src/functions/validate/workspace-validate.js +3 -2
  94. package/dist/src/functions/wirings/ai-agent/serialize-public-agent.js +2 -1
  95. package/dist/src/functions/wirings/console/serialize-console-functions.js +4 -4
  96. package/dist/src/functions/wirings/functions/serialize-addon-types.js +1 -1
  97. package/dist/src/scaffold/rpc-remote.gen.js +1 -1
  98. package/dist/src/services.js +2 -0
  99. package/dist/tsconfig.tsbuildinfo +1 -1
  100. package/package.json +5 -3
  101. package/skills/pikku-testing/SKILL.md +208 -0
  102. package/dist/src/functions/db/sql-migrator.d.ts +0 -26
  103. package/dist/src/functions/db/sql-migrator.js +0 -104
  104. package/dist/src/functions/db/sqlite-codegen.d.ts +0 -45
  105. package/dist/src/functions/db/sqlite-codegen.js +0 -294
  106. /package/dist/src/functions/db/{seed.d.ts → sqlite/seed.d.ts} +0 -0
  107. /package/dist/src/functions/db/{seed.js → sqlite/seed.js} +0 -0
  108. /package/dist/src/functions/db/{sqlite-kysely.d.ts → sqlite/sqlite-kysely.d.ts} +0 -0
  109. /package/dist/src/functions/db/{sqlite-kysely.js → sqlite/sqlite-kysely.js} +0 -0
  110. /package/dist/src/functions/db/{sqlite-runtime-bun.d.ts → sqlite/sqlite-runtime-bun.d.ts} +0 -0
  111. /package/dist/src/functions/db/{sqlite-runtime-bun.js → sqlite/sqlite-runtime-bun.js} +0 -0
  112. /package/dist/src/functions/db/{sqlite-runtime-node.d.ts → sqlite/sqlite-runtime-node.d.ts} +0 -0
  113. /package/dist/src/functions/db/{sqlite-runtime-node.js → sqlite/sqlite-runtime-node.js} +0 -0
  114. /package/dist/src/functions/db/{sqlite-runtime.d.ts → sqlite/sqlite-runtime.d.ts} +0 -0
  115. /package/dist/src/functions/db/{sqlite-runtime.js → sqlite/sqlite-runtime.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/cli",
3
- "version": "0.12.25",
3
+ "version": "0.12.26",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "BUSL-1.1",
6
6
  "imports": {
@@ -26,10 +26,10 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@openapi-contrib/json-schema-to-openapi-schema": "^4.3.1",
29
- "@pikku/core": "^0.12.24",
29
+ "@pikku/core": "^0.12.25",
30
30
  "@pikku/deploy-cloudflare": "^0.12.3",
31
31
  "@pikku/fetch": "^0.12.2",
32
- "@pikku/inspector": "^0.12.12",
32
+ "@pikku/inspector": "^0.12.13",
33
33
  "@pikku/kysely": "^0.12.12",
34
34
  "@pikku/kysely-node-sqlite": "^0.12.1",
35
35
  "@pikku/node-http-server": "^0.12.1",
@@ -44,6 +44,7 @@
44
44
  "open": "^10.2.0",
45
45
  "openapi-types": "^12.1.3",
46
46
  "path-to-regexp": "^8.3.0",
47
+ "pg": "^8.13.0",
47
48
  "tinyglobby": "^0.2.12",
48
49
  "ts-json-schema-generator": "^2.5.0",
49
50
  "tsx": "^4.21.0",
@@ -55,6 +56,7 @@
55
56
  },
56
57
  "devDependencies": {
57
58
  "@types/node": "^24.11.0",
59
+ "@types/pg": "^8.11.0",
58
60
  "@types/ws": "^8"
59
61
  },
60
62
  "files": [
@@ -27,6 +27,132 @@ pikku info functions --verbose # See existing functions and their middleware/p
27
27
  pikku info middleware --verbose # See middleware applied
28
28
  ```
29
29
 
30
+ ## Personas and Named Data — Never Inline JSON
31
+
32
+ **Never put JSON, inline tables, or raw values inside `.feature` files.** Feature files are for human-readable scenarios. All test data belongs in typed maps that step definitions look up by name.
33
+
34
+ `@pikku/cucumber` exports `PersonaData<T>` for this purpose — a typed map that throws a clear error when a name is missing.
35
+
36
+ ### Personas
37
+
38
+ A **persona** is a named user: their login credentials plus the session they hold after authenticating. Define all personas in one file:
39
+
40
+ ```ts
41
+ // tests/tests/support/personas.ts
42
+ import { PersonaData } from '@pikku/cucumber'
43
+
44
+ export const logins = new PersonaData({
45
+ yasser: { email: 'yasser@example.com', password: 'hunter2' },
46
+ guest: { email: 'guest@example.com', password: 'guest123' },
47
+ })
48
+ ```
49
+
50
+ A persona step logs in and stores the session in the world so every subsequent call by that persona carries it automatically:
51
+
52
+ ```ts
53
+ // tests/tests/support/steps/auth.steps.ts
54
+ import { Given } from '@cucumber/cucumber'
55
+ import { logins } from '../personas.js'
56
+
57
+ Given('{string} logs in', async function (name: string) {
58
+ await this.call(name, 'auth:login', logins.get(name))
59
+ const { token } = this.lastResult as { token: string }
60
+ this.setSession(name, { token })
61
+ })
62
+ ```
63
+
64
+ ### Named Domain Data
65
+
66
+ Use a separate `PersonaData` map for each domain concept. Name entries after real-world meaning, not technical fields:
67
+
68
+ ```ts
69
+ // tests/tests/support/data/cards.ts
70
+ import { PersonaData } from '@pikku/cucumber'
71
+
72
+ export const cards = new PersonaData({
73
+ 'writing a blog post': { title: 'Writing a blog post', columnId: 'backlog' },
74
+ 'fix the login bug': { title: 'Fix the login bug', columnId: 'in-progress' },
75
+ })
76
+ ```
77
+
78
+ Steps resolve the name and make the call — the feature file never sees raw data:
79
+
80
+ ```ts
81
+ // tests/tests/support/steps/card.steps.ts
82
+ import { When, Then } from '@cucumber/cucumber'
83
+ import assert from 'node:assert/strict'
84
+ import { cards } from '../data/cards.js'
85
+
86
+ When('{string} creates a card for {string}', async function (persona: string, cardName: string) {
87
+ await this.call(persona, 'kanban:createCard', cards.get(cardName))
88
+ })
89
+
90
+ When('{string} gets the card {string}', async function (persona: string, cardName: string) {
91
+ const { title } = cards.get(cardName)
92
+ await this.call(persona, 'kanban:getCard', { title })
93
+ })
94
+
95
+ // "the newly created card" — checks the live result against the data map entry
96
+ // AND any server-assigned fields (id, createdAt) are present
97
+ Then('the result is the newly created card {string}', function (cardName: string) {
98
+ const expected = cards.get(cardName)
99
+ const result = this.lastResult as typeof expected & { id: string; createdAt: string }
100
+ assert.equal(result.title, expected.title)
101
+ assert.equal(result.columnId, expected.columnId)
102
+ assert.ok(result.id, 'expected server-assigned id')
103
+ assert.ok(result.createdAt, 'expected server-assigned createdAt')
104
+ })
105
+ ```
106
+
107
+ The feature file reads naturally:
108
+
109
+ ```gherkin
110
+ Feature: Card management
111
+
112
+ Scenario: Create and retrieve a card
113
+ Given 'yasser' logs in
114
+ When 'yasser' creates a card for 'writing a blog post'
115
+ And 'yasser' gets the card 'writing a blog post'
116
+ Then the result is the newly created card 'writing a blog post'
117
+ ```
118
+
119
+ ### File layout
120
+
121
+ ```
122
+ tests/tests/support/
123
+ personas.ts ← logins PersonaData (one per project)
124
+ data/
125
+ cards.ts ← cards PersonaData
126
+ users.ts ← users PersonaData
127
+ steps/
128
+ auth.steps.ts ← login / logout steps
129
+ card.steps.ts ← card CRUD steps
130
+ ```
131
+
132
+ Keep one `PersonaData` instance per domain concept. Steps import only what they need — no cross-domain coupling.
133
+
134
+ ## Coverage-Driven Test Writing
135
+
136
+ When asked to improve or fill test coverage, start with the AI prompt from the coverage command:
137
+
138
+ ```bash
139
+ # Run tests and emit an AI-ready prompt listing every uncovered/partial function
140
+ pikku tests coverage --ai-out coverage-prompt.md
141
+
142
+ # Or skip re-running if you already have fresh coverage data
143
+ pikku tests coverage --no-run --ai-out coverage-prompt.md
144
+
145
+ # Pipe directly to stdout (e.g. to paste into a chat)
146
+ pikku tests coverage --ai-out -
147
+ ```
148
+
149
+ The prompt lists each function that needs work with its status (`uncovered`/`partial`), coverage ratio, missed line numbers, and source file path. Use it as your starting point:
150
+
151
+ 1. Read the prompt to know which functions need Gherkin scenarios.
152
+ 2. Run `pikku meta functions list` or `pikku meta context` to get input/output schemas for those functions.
153
+ 3. Write `.feature` files under `tests/tests/features/` — one feature per domain, one scenario per case.
154
+ 4. Re-run `pikku tests coverage` to confirm coverage improved.
155
+
30
156
  See `pikku-concepts` for the core mental model.
31
157
 
32
158
  ## Test Runner Setup
@@ -426,3 +552,85 @@ describe('createTodo', () => {
426
552
  })
427
553
  })
428
554
  ```
555
+
556
+ ## Anti-Patterns
557
+
558
+ ### Inline data in feature files
559
+
560
+ **Wrong** — raw values and JSON in `.feature` files make scenarios brittle and unreadable:
561
+
562
+ ```gherkin
563
+ When I call 'kanban:createCard' with {"title": "My card", "columnId": "backlog"}
564
+ And I call 'kanban:getCard' with {"title": "My card"}
565
+ Then the result title is "My card"
566
+ ```
567
+
568
+ **Right** — named references resolved by step definitions:
569
+
570
+ ```gherkin
571
+ When 'yasser' creates a card for 'writing a blog post'
572
+ And 'yasser' gets the card 'writing a blog post'
573
+ Then the result is the newly created card 'writing a blog post'
574
+ ```
575
+
576
+ ### Feature-coupled step definitions
577
+
578
+ Steps tied to one feature can't be reused and cause duplication. Organise by **domain concept**, not by feature:
579
+
580
+ ```
581
+ Wrong: Right:
582
+ steps/
583
+ edit_work_experience.ts → steps/
584
+ edit_languages.ts → auth.steps.ts
585
+ edit_education.ts → profile.steps.ts
586
+ card.steps.ts
587
+ ```
588
+
589
+ Name step files after the domain they cover. A login step belongs in `auth.steps.ts` regardless of which feature needs it.
590
+
591
+ ### Conjunction steps
592
+
593
+ Don't combine multiple actions into a single step — it makes reuse impossible:
594
+
595
+ ```gherkin
596
+ # Wrong — two actions in one step
597
+ Given 'yasser' is logged in and has created a card
598
+ ```
599
+
600
+ ```gherkin
601
+ # Right — atomic steps, composable via And
602
+ Given 'yasser' logs in
603
+ And 'yasser' creates a card for 'writing a blog post'
604
+ ```
605
+
606
+ Use `And` / `But` for a reason: each step should do exactly one thing.
607
+
608
+ ### Asserting in When steps
609
+
610
+ `When` steps perform actions; `Then` steps assert outcomes. Mixing them hides intent:
611
+
612
+ ```gherkin
613
+ # Wrong
614
+ When 'yasser' creates a card and the title is 'writing a blog post'
615
+
616
+ # Right
617
+ When 'yasser' creates a card for 'writing a blog post'
618
+ Then the call succeeds
619
+ ```
620
+
621
+ ### Hard-coding persona data in step definitions
622
+
623
+ Credentials and test inputs embedded in step code can't be reused across scenarios and break when data changes:
624
+
625
+ ```ts
626
+ // Wrong
627
+ Given('{string} logs in', async function (name: string) {
628
+ await this.call(name, 'auth:login', { email: 'yasser@example.com', password: 'hunter2' })
629
+ })
630
+
631
+ // Right — look up from PersonaData
632
+ Given('{string} logs in', async function (name: string) {
633
+ await this.call(name, 'auth:login', logins.get(name))
634
+ this.setSession(name, (this.lastResult as { token: string }))
635
+ })
636
+ ```
@@ -1,26 +0,0 @@
1
- import type { SyncSqliteDatabase } from './sqlite-runtime.js';
2
- export declare class MigrationDriftError extends Error {
3
- readonly file: string;
4
- readonly recordedHash: string;
5
- readonly currentHash: string | null;
6
- readonly appliedAt: string;
7
- constructor(file: string, recordedHash: string, currentHash: string | null, appliedAt: string);
8
- }
9
- export interface MigrateResult {
10
- applied: string[];
11
- skipped: string[];
12
- }
13
- /**
14
- * Apply pending migrations from <migrationsDir>/*.sql against the open db.
15
- * Hashes raw bytes on apply; subsequent runs re-hash and bail with
16
- * MigrationDriftError if any applied file has changed on disk.
17
- *
18
- * The same bytes that get hashed are the bytes passed to db.exec — no
19
- * splitting, trimming, or normalization. See docs/dev-builtin-sqlite.md.
20
- */
21
- export declare function migrate(db: SyncSqliteDatabase, migrationsDir: string): MigrateResult;
22
- /**
23
- * Wipe the tracking table. Used by `pikku db reset` after the DB file is
24
- * removed (calling this on its own does NOT drop user tables).
25
- */
26
- export declare function dropTrackingTable(db: SyncSqliteDatabase): void;
@@ -1,104 +0,0 @@
1
- import { createHash } from 'node:crypto';
2
- import { readFileSync, readdirSync } from 'node:fs';
3
- import { join } from 'node:path';
4
- const TRACKING_TABLE = 'sql_migrations';
5
- export class MigrationDriftError extends Error {
6
- file;
7
- recordedHash;
8
- currentHash;
9
- appliedAt;
10
- constructor(file, recordedHash, currentHash, appliedAt) {
11
- const onDisk = currentHash === null
12
- ? 'file missing on disk'
13
- : `sha256:${currentHash.slice(0, 8)}…`;
14
- super(`[PKU-DB-DRIFT] db/migrations/${file}\n\n` +
15
- `Migration content has changed since it was applied.\n` +
16
- ` recorded: sha256:${recordedHash.slice(0, 8)}… applied ${appliedAt}\n` +
17
- ` on disk: ${onDisk}\n\n` +
18
- `If this edit was intentional, run \`pikku db reset\` to rebuild the dev\n` +
19
- `database from scratch. Production migrations are immutable.`);
20
- this.file = file;
21
- this.recordedHash = recordedHash;
22
- this.currentHash = currentHash;
23
- this.appliedAt = appliedAt;
24
- this.name = 'MigrationDriftError';
25
- }
26
- }
27
- function sha256(bytes) {
28
- return createHash('sha256').update(bytes).digest('hex');
29
- }
30
- function ensureTrackingTable(db) {
31
- db.exec(`CREATE TABLE IF NOT EXISTS ${TRACKING_TABLE} (
32
- name TEXT PRIMARY KEY,
33
- hash TEXT NOT NULL,
34
- applied_at TEXT NOT NULL DEFAULT (datetime('now'))
35
- )`);
36
- }
37
- function listMigrationFiles(migrationsDir) {
38
- return readdirSync(migrationsDir)
39
- .filter((f) => f.endsWith('.sql'))
40
- .sort();
41
- }
42
- function readApplied(db) {
43
- return db
44
- .prepare(`SELECT name, hash, applied_at FROM ${TRACKING_TABLE} ORDER BY name`)
45
- .all();
46
- }
47
- function checkDrift(applied, migrationsDir) {
48
- for (const row of applied) {
49
- let currentHash = null;
50
- try {
51
- currentHash = sha256(readFileSync(join(migrationsDir, row.name)));
52
- }
53
- catch {
54
- currentHash = null;
55
- }
56
- if (currentHash !== row.hash) {
57
- throw new MigrationDriftError(row.name, row.hash, currentHash, row.applied_at);
58
- }
59
- }
60
- }
61
- /**
62
- * Apply pending migrations from <migrationsDir>/*.sql against the open db.
63
- * Hashes raw bytes on apply; subsequent runs re-hash and bail with
64
- * MigrationDriftError if any applied file has changed on disk.
65
- *
66
- * The same bytes that get hashed are the bytes passed to db.exec — no
67
- * splitting, trimming, or normalization. See docs/dev-builtin-sqlite.md.
68
- */
69
- export function migrate(db, migrationsDir) {
70
- ensureTrackingTable(db);
71
- const applied = readApplied(db);
72
- checkDrift(applied, migrationsDir);
73
- const appliedSet = new Set(applied.map((r) => r.name));
74
- const files = listMigrationFiles(migrationsDir);
75
- const result = { applied: [], skipped: [] };
76
- const insertStmt = db.prepare(`INSERT INTO ${TRACKING_TABLE} (name, hash) VALUES (?, ?)`);
77
- for (const name of files) {
78
- if (appliedSet.has(name)) {
79
- result.skipped.push(name);
80
- continue;
81
- }
82
- const raw = readFileSync(join(migrationsDir, name));
83
- const hash = sha256(raw);
84
- db.exec('BEGIN');
85
- try {
86
- db.exec(raw.toString('utf8'));
87
- insertStmt.run(name, hash);
88
- db.exec('COMMIT');
89
- }
90
- catch (err) {
91
- db.exec('ROLLBACK');
92
- throw err;
93
- }
94
- result.applied.push(name);
95
- }
96
- return result;
97
- }
98
- /**
99
- * Wipe the tracking table. Used by `pikku db reset` after the DB file is
100
- * removed (calling this on its own does NOT drop user tables).
101
- */
102
- export function dropTrackingTable(db) {
103
- db.exec(`DROP TABLE IF EXISTS ${TRACKING_TABLE}`);
104
- }
@@ -1,45 +0,0 @@
1
- import type { ColumnKind } from './coercion-plugin.js';
2
- import type { SyncSqliteDatabase } from './sqlite-runtime.js';
3
- /** Internal annotation: column kind plus an optional TS type string (for @json). */
4
- interface ColAnnotation {
5
- kind: ColumnKind;
6
- /** TypeScript type string, e.g. `string[]` or `Record<string, number>`. Only set for @json. */
7
- tsType?: string;
8
- }
9
- /** Internal annotation map used during codegen. */
10
- type AnnotationMap = Record<string, Record<string, ColAnnotation>>;
11
- /**
12
- * Parse `-- @bool | @date | @json [TypescriptType]` inline annotations from
13
- * migration SQL files. The TypeScript type is optional and only meaningful for
14
- * `@json` — it controls the generated TypeScript type (e.g. `string[]`,
15
- * `Record<string, number>`).
16
- *
17
- * Returns an AnnotationMap: { table_name: { col_name: ColAnnotation } }.
18
- */
19
- export declare function parseAnnotations(migrationsDir: string): AnnotationMap;
20
- export interface CodegenOptions {
21
- outFile: string;
22
- coercionFile: string;
23
- camelCase?: boolean;
24
- migrationsDir?: string;
25
- }
26
- export interface CodegenResult {
27
- outFile: string;
28
- coercionFile: string;
29
- written: boolean;
30
- coercionWritten: boolean;
31
- tables: string[];
32
- }
33
- /**
34
- * Introspect the open SQLite database and emit a Kysely DB type to outFile
35
- * plus a CoercionMap to coercionFile.
36
- *
37
- * Columns are annotated via:
38
- * 1. Naming conventions (_at/_on → date; is_/has_/can_ → bool)
39
- * 2. Inline SQL comments: `col_name TYPE ... -- @bool|@date|@json [TsType]`
40
- * For @json, an optional TypeScript type string controls the generated type.
41
- *
42
- * Returns `written: false` if the on-disk file already matches.
43
- */
44
- export declare function generateSchemaTypes(db: SyncSqliteDatabase, options: CodegenOptions): CodegenResult;
45
- export {};