@pikku/cli 0.12.28 → 0.12.30

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 (76) hide show
  1. package/console-app/assets/{index-Ca6xJwNm.js → index-BkiCv5R3.js} +110 -110
  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 +1 -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.gen.d.ts +1 -1
  11. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  12. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  13. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  14. package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
  15. package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
  16. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  17. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  18. package/dist/.pikku/function/pikku-functions-meta.gen.json +118 -118
  19. package/dist/.pikku/function/pikku-functions.gen.js +3 -1
  20. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  21. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  22. package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
  23. package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +1 -1
  24. package/dist/.pikku/http/pikku-http-wirings.gen.js +1 -1
  25. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  26. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  27. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  28. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  29. package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
  30. package/dist/.pikku/pikku-meta-service.gen.js +1 -1
  31. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  32. package/dist/.pikku/pikku-types.gen.d.ts +1 -1
  33. package/dist/.pikku/pikku-types.gen.js +1 -1
  34. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  35. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  36. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  37. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.json +4 -0
  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 +9 -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 -7
  45. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  46. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  47. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  48. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  49. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  50. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  51. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  52. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  53. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  54. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  55. package/dist/.pikku/workflow/meta/allWorkflow.gen.json +8 -2
  56. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  57. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  58. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  59. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  60. package/dist/bin/pikku-bin.mjs +2 -2
  61. package/dist/src/fabric/functions/validate-core.js +65 -0
  62. package/dist/src/functions/commands/dev.js +9 -17
  63. package/dist/src/functions/db/db-codegen.d.ts +1 -0
  64. package/dist/src/functions/db/db-codegen.js +45 -0
  65. package/dist/src/functions/db/db-introspector.d.ts +12 -0
  66. package/dist/src/functions/db/local-db.d.ts +8 -0
  67. package/dist/src/functions/db/local-db.js +17 -0
  68. package/dist/src/functions/db/postgres/postgres-introspector.d.ts +3 -1
  69. package/dist/src/functions/db/postgres/postgres-introspector.js +43 -0
  70. package/dist/src/functions/db/sqlite/sqlite-introspector.d.ts +3 -1
  71. package/dist/src/functions/db/sqlite/sqlite-introspector.js +14 -0
  72. package/dist/src/functions/db/sqlite/sqlite-runtime-node.js +16 -3
  73. package/dist/src/functions/wirings/auth/serialize-auth-gen.js +4 -2
  74. package/dist/src/scaffold/rpc-remote.gen.js +1 -1
  75. package/package.json +2 -2
  76. package/skills/pikku-workflow/SKILL.md +22 -0
@@ -96,24 +96,28 @@
96
96
  "pikkuAIAgent": "pikkuAIAgent",
97
97
  "pikkuPublicAgent": "pikkuPublicAgent",
98
98
  "pikkuAuth": "pikkuAuth",
99
+ "pikkuChannels": "pikkuChannels",
100
+ "pikkuChannelTypes": "pikkuChannelTypes",
101
+ "pikkuChannelsMap": "pikkuChannelsMap",
102
+ "pikkuCommandChannels": "pikkuCommandChannels",
99
103
  "pikkuCLIEntry": "pikkuCLIEntry",
100
104
  "pikkuCLI": "pikkuCLI",
101
105
  "pikkuConsoleFunctions": "pikkuConsoleFunctions",
102
106
  "pikkuNodeTypes": "pikkuNodeTypes",
103
107
  "pikkuNodesMeta": "pikkuNodesMeta",
104
108
  "pikkuCredentials": "pikkuCredentials",
105
- "pikkuGateway": "pikkuGateway",
106
109
  "pikkuFunctionTypesSplit": "pikkuFunctionTypesSplit",
107
110
  "pikkuFunctionTypes": "pikkuFunctionTypes",
108
111
  "pikkuFunctions": "pikkuFunctions",
109
112
  "pikkuServices": "pikkuServices",
110
- "pikkuMCPJSON": "pikkuMCPJSON",
111
- "pikkuMCPTypes": "pikkuMCPTypes",
112
- "pikkuMCP": "pikkuMCP",
113
+ "pikkuGateway": "pikkuGateway",
113
114
  "pikkuHTTPMap": "pikkuHTTPMap",
114
115
  "pikkuCommandHTTP": "pikkuCommandHTTP",
115
116
  "pikkuHTTPTypes": "pikkuHTTPTypes",
116
117
  "pikkuHTTP": "pikkuHTTP",
118
+ "pikkuMCPJSON": "pikkuMCPJSON",
119
+ "pikkuMCPTypes": "pikkuMCPTypes",
120
+ "pikkuMCP": "pikkuMCP",
117
121
  "pikkuMiddleware": "pikkuMiddleware",
118
122
  "pikkuSecretDefinitionTypes": "pikkuSecretDefinitionTypes",
119
123
  "pikkuVariableDefinitionTypes": "pikkuVariableDefinitionTypes",
@@ -136,9 +140,5 @@
136
140
  "pikkuTriggerTypes": "pikkuTriggerTypes",
137
141
  "pikkuTrigger": "pikkuTrigger",
138
142
  "pikkuVariables": "pikkuVariables",
139
- "pikkuWorkflowRoutes": "pikkuWorkflowRoutes",
140
- "pikkuChannels": "pikkuChannels",
141
- "pikkuChannelTypes": "pikkuChannelTypes",
142
- "pikkuChannelsMap": "pikkuChannelsMap",
143
- "pikkuCommandChannels": "pikkuCommandChannels"
143
+ "pikkuWorkflowRoutes": "pikkuWorkflowRoutes"
144
144
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  /**
5
5
  * Scheduler-specific type definitions for tree-shaking optimization
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  /**
5
5
  * Scheduler-specific type definitions for tree-shaking optimization
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  import { addSchema } from '@pikku/core/schema';
5
5
  import * as PikkuSchemasOutput from './schemas/PikkuSchemasOutput.schema.json' with { type: 'json' };
@@ -192,20 +192,20 @@ import * as PikkuCLIEntryOutput from './schemas/PikkuCLIEntryOutput.schema.json'
192
192
  addSchema('PikkuCLIEntryOutput', PikkuCLIEntryOutput);
193
193
  import * as PikkuCLIOutput from './schemas/PikkuCLIOutput.schema.json' with { type: 'json' };
194
194
  addSchema('PikkuCLIOutput', PikkuCLIOutput);
195
- import * as PikkuGatewayOutput from './schemas/PikkuGatewayOutput.schema.json' with { type: 'json' };
196
- addSchema('PikkuGatewayOutput', PikkuGatewayOutput);
197
195
  import * as PikkuFunctionTypesSplitInput from './schemas/PikkuFunctionTypesSplitInput.schema.json' with { type: 'json' };
198
196
  addSchema('PikkuFunctionTypesSplitInput', PikkuFunctionTypesSplitInput);
199
197
  import * as PikkuFunctionsOutput from './schemas/PikkuFunctionsOutput.schema.json' with { type: 'json' };
200
198
  addSchema('PikkuFunctionsOutput', PikkuFunctionsOutput);
199
+ import * as PikkuGatewayOutput from './schemas/PikkuGatewayOutput.schema.json' with { type: 'json' };
200
+ addSchema('PikkuGatewayOutput', PikkuGatewayOutput);
201
201
  import * as PikkuCommandHTTPOutput from './schemas/PikkuCommandHTTPOutput.schema.json' with { type: 'json' };
202
202
  addSchema('PikkuCommandHTTPOutput', PikkuCommandHTTPOutput);
203
203
  import * as PikkuHTTPOutput from './schemas/PikkuHTTPOutput.schema.json' with { type: 'json' };
204
204
  addSchema('PikkuHTTPOutput', PikkuHTTPOutput);
205
- import * as PikkuMCPOutput from './schemas/PikkuMCPOutput.schema.json' with { type: 'json' };
206
- addSchema('PikkuMCPOutput', PikkuMCPOutput);
207
205
  import * as PikkuMiddlewareOutput from './schemas/PikkuMiddlewareOutput.schema.json' with { type: 'json' };
208
206
  addSchema('PikkuMiddlewareOutput', PikkuMiddlewareOutput);
207
+ import * as PikkuMCPOutput from './schemas/PikkuMCPOutput.schema.json' with { type: 'json' };
208
+ addSchema('PikkuMCPOutput', PikkuMCPOutput);
209
209
  import * as PikkuPackageOutput from './schemas/PikkuPackageOutput.schema.json' with { type: 'json' };
210
210
  addSchema('PikkuPackageOutput', PikkuPackageOutput);
211
211
  import * as PikkuPermissionsOutput from './schemas/PikkuPermissionsOutput.schema.json' with { type: 'json' };
@@ -222,13 +222,13 @@ import * as PikkuTriggerTypesInput from './schemas/PikkuTriggerTypesInput.schema
222
222
  addSchema('PikkuTriggerTypesInput', PikkuTriggerTypesInput);
223
223
  import * as PikkuTriggerOutput from './schemas/PikkuTriggerOutput.schema.json' with { type: 'json' };
224
224
  addSchema('PikkuTriggerOutput', PikkuTriggerOutput);
225
- import * as PikkuWorkflowRoutesOutput from './schemas/PikkuWorkflowRoutesOutput.schema.json' with { type: 'json' };
226
- addSchema('PikkuWorkflowRoutesOutput', PikkuWorkflowRoutesOutput);
227
225
  import * as PikkuPublicRPCOutput from './schemas/PikkuPublicRPCOutput.schema.json' with { type: 'json' };
228
226
  addSchema('PikkuPublicRPCOutput', PikkuPublicRPCOutput);
229
227
  import * as PikkuRemoteRPCOutput from './schemas/PikkuRemoteRPCOutput.schema.json' with { type: 'json' };
230
228
  addSchema('PikkuRemoteRPCOutput', PikkuRemoteRPCOutput);
231
229
  import * as PikkuRPCOutput from './schemas/PikkuRPCOutput.schema.json' with { type: 'json' };
232
230
  addSchema('PikkuRPCOutput', PikkuRPCOutput);
231
+ import * as PikkuWorkflowRoutesOutput from './schemas/PikkuWorkflowRoutesOutput.schema.json' with { type: 'json' };
232
+ addSchema('PikkuWorkflowRoutesOutput', PikkuWorkflowRoutesOutput);
233
233
  import * as PikkuCLIConfig from './schemas/PikkuCLIConfig.schema.json' with { type: 'json' };
234
234
  addSchema('PikkuCLIConfig', PikkuCLIConfig);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  export { wireSecret } from '@pikku/core/secret';
5
5
  export type { CoreSecret, SecretDefinitionMeta, SecretDefinitionsMeta } from '@pikku/core/secret';
@@ -1,4 +1,4 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  export { wireSecret } from '@pikku/core/secret';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  import { TypedSecretService as CoreTypedSecretService } from '@pikku/core/services';
5
5
  import type { SecretService } from '@pikku/core/services';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  import { TypedSecretService as CoreTypedSecretService } from '@pikku/core/services';
5
5
  const CREDENTIALS_META = {};
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  /**
5
5
  * Trigger-specific type definitions for tree-shaking optimization
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  /**
5
5
  * Trigger-specific type definitions for tree-shaking optimization
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  export { wireVariable } from '@pikku/core/variable';
5
5
  export type { CoreVariable, VariableDefinitionMeta, VariableDefinitionsMeta } from '@pikku/core/variable';
@@ -1,4 +1,4 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  export { wireVariable } from '@pikku/core/variable';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  import { TypedVariablesService as CoreTypedVariablesService } from '@pikku/core/services';
5
5
  import type { VariablesService } from '@pikku/core/services';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  import { TypedVariablesService as CoreTypedVariablesService } from '@pikku/core/services';
5
5
  const VARIABLES_META = {};
@@ -225,9 +225,15 @@
225
225
  "Events scaffold": {
226
226
  "nodeId": "Events scaffold",
227
227
  "rpcName": "pikkuEventsScaffold",
228
- "next": "Secret definition types",
228
+ "next": "Emails",
229
229
  "stepHash": "b5a547ac59ef"
230
230
  },
231
+ "Emails": {
232
+ "nodeId": "Emails",
233
+ "rpcName": "pikkuEmails",
234
+ "next": "Secret definition types",
235
+ "stepHash": "bb8361cb96cf"
236
+ },
231
237
  "Secret definition types": {
232
238
  "nodeId": "Secret definition types",
233
239
  "rpcName": "pikkuSecretDefinitionTypes",
@@ -521,5 +527,5 @@
521
527
  "entryNodeIds": [
522
528
  "Bootstrap inspect"
523
529
  ],
524
- "graphHash": "1c85dee2bb48"
530
+ "graphHash": "359c0b7178dc"
525
531
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  import { WorkflowCancelledException } from '@pikku/core/workflow';
5
5
  import { template } from '@pikku/core/workflow';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  import { WorkflowCancelledException } from '@pikku/core/workflow';
5
5
  import { template } from '@pikku/core/workflow';
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  import { pikkuState } from '@pikku/core/internal';
5
5
  import allWorkflowMeta from './meta/allWorkflow.gen.json' with { type: 'json' };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file was generated by @pikku/cli@0.12.28
2
+ * This file was generated by @pikku/cli@0.12.30
3
3
  */
4
4
  import { addWorkflow } from '@pikku/core/workflow';
5
5
  import './pikku-workflow-wirings-meta.gen.js';
@@ -11,8 +11,8 @@ async function checkForUpdate() {
11
11
  })
12
12
  if (!res.ok) return
13
13
  const { version: latest } = await res.json()
14
- if (latest !== '0.12.28') {
15
- process.stderr.write(`\n Update available 0.12.28 → ${latest}\n brew upgrade pikku or npm install -g @pikku/cli\n\n`)
14
+ if (latest !== '0.12.30') {
15
+ process.stderr.write(`\n Update available 0.12.30 → ${latest}\n brew upgrade pikku or npm install -g @pikku/cli\n\n`)
16
16
  }
17
17
  } catch {}
18
18
  }
@@ -1,6 +1,7 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { readdir } from 'node:fs/promises';
3
3
  import { join } from 'node:path';
4
+ import { readFile } from 'node:fs/promises';
4
5
  import { z } from 'zod';
5
6
  import { added, changed, dim, removed } from '../lib/output.js';
6
7
  import { WorkspaceValidateOutput, readJsonSafe, readTextSafe, runWorkspaceValidate, } from '../../functions/validate/workspace-validate.js';
@@ -31,6 +32,53 @@ export async function runFabricValidate(startDir = process.cwd()) {
31
32
  const info = (id, message, path, fixHint) => {
32
33
  findings.push({ id, severity: 'info', message, path, fixHint });
33
34
  };
35
+ // These dirs are generated/runtime artefacts; committing them bloats PRs and causes stale issues
36
+ const gitignorePath = join(root, '.gitignore');
37
+ const REQUIRED_GITIGNORE_ENTRIES = [
38
+ {
39
+ name: '.pikku',
40
+ patterns: ['.pikku', '.pikku/', '/.pikku', '/.pikku/'],
41
+ reason: 'generated codegen artifacts will pollute commits and PRs',
42
+ },
43
+ {
44
+ name: '.pikku-runtime',
45
+ patterns: [
46
+ '.pikku-runtime',
47
+ '.pikku-runtime/',
48
+ '/.pikku-runtime',
49
+ '/.pikku-runtime/',
50
+ ],
51
+ reason: 'runtime state directory should not be committed',
52
+ },
53
+ {
54
+ name: '.opencode',
55
+ patterns: ['.opencode', '.opencode/', '/.opencode', '/.opencode/'],
56
+ reason: 'OpenCode session data should not be committed',
57
+ },
58
+ {
59
+ name: '*.gen.ts',
60
+ patterns: ['*.gen.ts', '**/*.gen.ts'],
61
+ reason: 'generated TypeScript files (by pikku and TanStack Start) should not be committed — they are regenerated at build time',
62
+ },
63
+ {
64
+ name: '*.gen.js',
65
+ patterns: ['*.gen.js', '**/*.gen.js'],
66
+ reason: 'generated JavaScript files (by pikku and TanStack Start) should not be committed — they are regenerated at build time',
67
+ },
68
+ ];
69
+ try {
70
+ const gitignoreText = await readFile(gitignorePath, 'utf8');
71
+ const lines = gitignoreText.split('\n').map((l) => l.trim());
72
+ for (const entry of REQUIRED_GITIGNORE_ENTRIES) {
73
+ const covered = lines.some((l) => entry.patterns.includes(l));
74
+ if (!covered) {
75
+ w(`${entry.name.replace(/[^a-z0-9]/gi, '-')}-not-gitignored`, `${entry.name} is not listed in .gitignore — ${entry.reason}`, gitignorePath, `Add "${entry.name}" to .gitignore`);
76
+ }
77
+ }
78
+ }
79
+ catch {
80
+ w('gitignore-missing', '.gitignore not found at project root — generated artifacts (.pikku, .pikku-runtime, .opencode) may be committed accidentally', gitignorePath, 'Create .gitignore and add ".pikku", ".pikku-runtime", and ".opencode" to it');
81
+ }
34
82
  const fabricConfigPath = join(root, 'fabric.config.json');
35
83
  const fabricConfig = await readJsonSafe(fabricConfigPath);
36
84
  if (!fabricConfig) {
@@ -69,6 +117,12 @@ export async function runFabricValidate(startDir = process.cwd()) {
69
117
  if (fnAllDeps['@pikku/kysely-postgres']) {
70
118
  e('fn-pkg-postgres-dep', '@pikku/kysely-postgres is in packages/functions dependencies — Fabric uses SQLite/libSQL (Turso), not PostgreSQL', fnPkgPath, 'Remove @pikku/kysely-postgres and use @pikku/kysely-sqlite with LibsqlWebDialect instead');
71
119
  }
120
+ if (!fnPkg.dependencies?.['@pikku/schema-cfworker']) {
121
+ e('missing-schema-cfworker', '@pikku/schema-cfworker is not in dependencies — every Cloudflare worker entry requires it', fnPkgPath, 'Run `yarn add @pikku/schema-cfworker` in packages/functions — must be in dependencies, not devDependencies');
122
+ }
123
+ if (!fnPkg.dependencies?.['@pikku/kysely']) {
124
+ e('missing-pikku-kysely', '@pikku/kysely is not in dependencies — every Cloudflare worker entry requires it (KyselySecretService)', fnPkgPath, 'Run `yarn add @pikku/kysely` in packages/functions — must be in dependencies, not devDependencies');
125
+ }
72
126
  }
73
127
  const servicesPath = join(fnDir, 'src', 'services.ts');
74
128
  const servicesText = await readTextSafe(servicesPath);
@@ -90,6 +144,17 @@ export async function runFabricValidate(startDir = process.cwd()) {
90
144
  e('missing-kysely-sqlite', 'services.ts imports @pikku/kysely-sqlite but it is not in root package.json', rootPkgPath, 'Add "@pikku/kysely-sqlite": "file:./vendor/pikku-kysely-sqlite.tgz" to dependencies');
91
145
  }
92
146
  }
147
+ // agent units — require explicit deps rather than CI injection
148
+ const agentMetaPath = join(fnDir, '.pikku', 'agent', 'pikku-agent-wirings-meta.gen.json');
149
+ const agentMeta = await readJsonSafe(agentMetaPath);
150
+ if (agentMeta && Object.keys(agentMeta.agentsMeta ?? {}).length > 0) {
151
+ if (!fnPkg?.dependencies?.['@pikku/ai-vercel']) {
152
+ e('missing-ai-vercel', 'Project declares agent units but @pikku/ai-vercel is not in dependencies', fnPkgPath, 'Run `yarn add @pikku/ai-vercel` in packages/functions — must be in dependencies, not devDependencies');
153
+ }
154
+ if (!fnPkg?.dependencies?.['@ai-sdk/openai-compatible']) {
155
+ e('missing-ai-sdk-openai-compatible', 'Project declares agent units but @ai-sdk/openai-compatible is not in dependencies', fnPkgPath, 'Run `yarn add @ai-sdk/openai-compatible` in packages/functions — must be in dependencies, not devDependencies');
156
+ }
157
+ }
93
158
  // db/sqlite/ — presence, numbering and SQL dialect
94
159
  const migrationsDir = join(fnDir, 'db', 'sqlite');
95
160
  if (!existsSync(migrationsDir)) {
@@ -1,4 +1,3 @@
1
- import { existsSync } from 'fs';
2
1
  import { join, resolve } from 'path';
3
2
  import { pikkuSessionlessFunc } from '#pikku';
4
3
  import chokidar from 'chokidar';
@@ -14,11 +13,11 @@ import { pikkuWebsocketHandler } from '@pikku/ws';
14
13
  import { PikkuNodeHTTPServer } from '@pikku/node-http-server';
15
14
  import { WebSocketServer } from 'ws';
16
15
  import { InMemorySchedulerService } from '@pikku/schedule';
17
- import { resolveDb, createKysely } from '../db/local-db.js';
16
+ import { resolveDb, createKysely, parseDatabaseUrl, } from '../db/local-db.js';
18
17
  import { loadUserBootstrap, loadUserModule } from './load-user-project.js';
19
18
  export const dev = pikkuSessionlessFunc({
20
19
  remote: true,
21
- func: async ({ logger, config, getInspectorState }, { port, watch, hmr }, { rpc }) => {
20
+ func: async ({ logger, config, getInspectorState, variables }, { port, watch, hmr }, { rpc }) => {
22
21
  const resolvedPort = parseInt(port || '3000', 10);
23
22
  const hostname = 'localhost';
24
23
  const enableWatch = watch !== false;
@@ -40,8 +39,6 @@ export const dev = pikkuSessionlessFunc({
40
39
  const commandWorkflowRegistrations = new Map(pikkuState(null, 'workflows', 'registrations'));
41
40
  const workflowService = new InMemoryWorkflowService();
42
41
  const pikkuDir = resolve(config.rootDir, config.outDir);
43
- const bootstrapExists = existsSync(resolve(pikkuDir, 'pikku-bootstrap.gen.ts')) ||
44
- existsSync(resolve(pikkuDir, 'pikku-bootstrap.gen.js'));
45
42
  const runAll = async () => {
46
43
  await workflowService.runToCompletion('allWorkflow', {}, rpc);
47
44
  };
@@ -97,9 +94,6 @@ export const dev = pikkuSessionlessFunc({
97
94
  pikkuState(null, 'workflows', 'registrations', previousWorkflowRegistrations);
98
95
  }
99
96
  };
100
- if (bootstrapExists) {
101
- await loadUserBootstrap(pikkuDir);
102
- }
103
97
  await runAllWithCommandState();
104
98
  const inspectorState = await getInspectorState(true);
105
99
  const { pikkuConfigFactory, singletonServicesFactory } = inspectorState.filesAndMethods;
@@ -107,21 +101,19 @@ export const dev = pikkuSessionlessFunc({
107
101
  logger.error('createConfig and createSingletonServices must be defined in your project');
108
102
  return;
109
103
  }
110
- if (!bootstrapExists) {
111
- await loadUserBootstrap(pikkuDir);
112
- }
104
+ await loadUserBootstrap(pikkuDir);
113
105
  workflowService.rewireQueueWorkers();
114
106
  const configModule = await loadUserModule(pikkuConfigFactory.file);
115
107
  const servicesModule = await loadUserModule(singletonServicesFactory.file);
116
108
  const userCreateConfig = configModule[pikkuConfigFactory.variable];
117
109
  const userCreateSingletonServices = servicesModule[singletonServicesFactory.variable];
118
110
  const userConfig = await userCreateConfig();
119
- const resolvedDb = resolveDb(userConfig, config.rootDir, config.outDir, config.runtimeDir);
120
- const resolvedLocalDb = resolvedDb?.dialect === 'sqlite'
121
- ? resolvedDb
122
- : userConfig.sqliteDb
123
- ? resolveDb({ sqliteDb: userConfig.sqliteDb }, config.rootDir, config.outDir, config.runtimeDir)
124
- : undefined;
111
+ const envDatabaseUrl = await variables.get('DATABASE_URL');
112
+ const effectiveDbConfig = envDatabaseUrl
113
+ ? parseDatabaseUrl(envDatabaseUrl)
114
+ : userConfig;
115
+ const resolvedDb = resolveDb(effectiveDbConfig, config.rootDir, config.outDir, config.runtimeDir);
116
+ const resolvedLocalDb = resolvedDb?.dialect === 'sqlite' ? resolvedDb : undefined;
125
117
  const kysely = resolvedLocalDb
126
118
  ? await createKysely(resolvedLocalDb)
127
119
  : undefined;
@@ -4,6 +4,7 @@ export interface CodegenOptions {
4
4
  coercionFile: string;
5
5
  manifestFile?: string;
6
6
  classificationMapFile?: string;
7
+ schemaJsonFile?: string;
7
8
  camelCase?: boolean;
8
9
  rootDir?: string;
9
10
  migrationsDir?: string;
@@ -308,11 +308,43 @@ export async function generateSchemaTypes(introspector, options) {
308
308
  const classificationMapBody = options.classificationMapFile
309
309
  ? emitClassificationMap(tables)
310
310
  : null;
311
+ // ── pikku-db-schema.gen.json ─────────────────────────────────────────────────
312
+ let schemaJsonBody = null;
313
+ if (options.schemaJsonFile) {
314
+ const [fkResults, enums] = await Promise.all([
315
+ Promise.all(tableNames.map((name) => introspector.getForeignKeys(name))),
316
+ introspector.listEnums(),
317
+ ]);
318
+ const fkMap = new Map(tableNames.map((name, i) => [name, fkResults[i]]));
319
+ const jsonTables = tables.map((t) => ({
320
+ name: t.name,
321
+ columns: t.columns.map((c) => {
322
+ const fk = fkMap.get(t.name)?.find((f) => f.column === c.name);
323
+ return {
324
+ name: c.name,
325
+ type: c.type,
326
+ nullable: !c.notNull && !c.pk,
327
+ isPrimaryKey: c.pk,
328
+ ...(fk
329
+ ? {
330
+ foreignKey: {
331
+ table: fk.foreignTable,
332
+ column: fk.foreignColumn,
333
+ },
334
+ }
335
+ : {}),
336
+ };
337
+ }),
338
+ }));
339
+ schemaJsonBody =
340
+ JSON.stringify({ tables: jsonTables, enums }, null, 2) + '\n';
341
+ }
311
342
  // ── write files ───────────────────────────────────────────────────────────────
312
343
  let existingSchema = null;
313
344
  let existingCoercion = null;
314
345
  let existingManifest = null;
315
346
  let existingClassificationMap = null;
347
+ let existingSchemaJson = null;
316
348
  try {
317
349
  existingSchema = readFileSync(options.outFile, 'utf8');
318
350
  }
@@ -341,11 +373,20 @@ export async function generateSchemaTypes(introspector, options) {
341
373
  /* ok */
342
374
  }
343
375
  }
376
+ if (options.schemaJsonFile) {
377
+ try {
378
+ existingSchemaJson = readFileSync(options.schemaJsonFile, 'utf8');
379
+ }
380
+ catch {
381
+ /* ok */
382
+ }
383
+ }
344
384
  const schemaChanged = existingSchema !== schemaBody;
345
385
  const coercionChanged = existingCoercion !== coercionBody;
346
386
  const manifestChanged = manifestBody !== null && existingManifest !== manifestBody;
347
387
  const classificationMapChanged = classificationMapBody !== null &&
348
388
  existingClassificationMap !== classificationMapBody;
389
+ const schemaJsonChanged = schemaJsonBody !== null && existingSchemaJson !== schemaJsonBody;
349
390
  if (schemaChanged) {
350
391
  mkdirSync(dirname(options.outFile), { recursive: true });
351
392
  writeFileSync(options.outFile, schemaBody, 'utf8');
@@ -364,6 +405,10 @@ export async function generateSchemaTypes(introspector, options) {
364
405
  mkdirSync(dirname(options.classificationMapFile), { recursive: true });
365
406
  writeFileSync(options.classificationMapFile, classificationMapBody, 'utf8');
366
407
  }
408
+ if (schemaJsonChanged && options.schemaJsonFile && schemaJsonBody) {
409
+ mkdirSync(dirname(options.schemaJsonFile), { recursive: true });
410
+ writeFileSync(options.schemaJsonFile, schemaJsonBody, 'utf8');
411
+ }
367
412
  return {
368
413
  outFile: options.outFile,
369
414
  coercionFile: options.coercionFile,
@@ -8,8 +8,20 @@ export interface ColumnInfo {
8
8
  /** True for virtual or stored generated columns — these are read-only and never inserted. */
9
9
  generated?: boolean;
10
10
  }
11
+ export interface ForeignKeyInfo {
12
+ column: string;
13
+ foreignTable: string;
14
+ foreignColumn: string;
15
+ }
16
+ export interface EnumInfo {
17
+ name: string;
18
+ schema: string;
19
+ values: string[];
20
+ }
11
21
  export interface DbIntrospector {
12
22
  listTables(): Promise<string[]>;
13
23
  getColumns(table: string): Promise<ColumnInfo[]>;
24
+ getForeignKeys(table: string): Promise<ForeignKeyInfo[]>;
25
+ listEnums(): Promise<EnumInfo[]>;
14
26
  close(): Promise<void>;
15
27
  }
@@ -11,6 +11,7 @@ interface ResolvedDbBase {
11
11
  coercionFile: string;
12
12
  manifestFile: string;
13
13
  classificationMapFile: string;
14
+ schemaJsonFile: string;
14
15
  classificationsFile: string;
15
16
  classificationsGenJsonFile: string;
16
17
  zodFile: string;
@@ -27,6 +28,13 @@ export interface ResolvedPostgresDb extends ResolvedDbBase {
27
28
  connectionString: string;
28
29
  }
29
30
  export type ResolvedDb = ResolvedSqliteDb | ResolvedPostgresDb;
31
+ /**
32
+ * Parse a DATABASE_URL string into the subset of UserConfigShape that resolveDb understands.
33
+ * - postgres(ql):// → { postgresUrl }
34
+ * - libsql:// or http(s):// → {} (remote, not handled by the CLI layer)
35
+ * - anything else → { sqliteDb } (local file path)
36
+ */
37
+ export declare function parseDatabaseUrl(url: string): Pick<UserConfigShape, 'sqliteDb' | 'postgresUrl'>;
30
38
  /**
31
39
  * Resolve a UserConfigShape into an absolute-path descriptor.
32
40
  * Returns null when neither sqliteDb nor postgresUrl is configured.
@@ -15,6 +15,19 @@ import { seed as runSeed } from './sqlite/seed.js';
15
15
  import { PostgresMigrationExecutor } from './postgres/postgres-migrator.js';
16
16
  import { PostgresIntrospector } from './postgres/postgres-introspector.js';
17
17
  // ─── Resolution ───────────────────────────────────────────────────────────────
18
+ /**
19
+ * Parse a DATABASE_URL string into the subset of UserConfigShape that resolveDb understands.
20
+ * - postgres(ql):// → { postgresUrl }
21
+ * - libsql:// or http(s):// → {} (remote, not handled by the CLI layer)
22
+ * - anything else → { sqliteDb } (local file path)
23
+ */
24
+ export function parseDatabaseUrl(url) {
25
+ if (/^postgres(ql)?:\/\//.test(url))
26
+ return { postgresUrl: url };
27
+ if (/^(libsql|https?):\/\//.test(url))
28
+ return {};
29
+ return { sqliteDb: url };
30
+ }
18
31
  /**
19
32
  * Resolve a UserConfigShape into an absolute-path descriptor.
20
33
  * Returns null when neither sqliteDb nor postgresUrl is configured.
@@ -27,6 +40,7 @@ export function resolveDb(userConfig, rootDir, outDir, runtimeDir) {
27
40
  coercionFile: join(outDir, 'db', 'coercion.gen.ts'),
28
41
  manifestFile: join(outDir, 'db', 'classification.gen.ts'),
29
42
  classificationMapFile: join(outDir, 'db', 'classification-map.gen.d.ts'),
43
+ schemaJsonFile: join(outDir, 'db', 'pikku-db-schema.gen.json'),
30
44
  classificationsFile: join(rootDir, 'db', 'annotations.ts'),
31
45
  classificationsGenJsonFile: join(outDir, 'db', 'annotations.gen.json'),
32
46
  zodFile: join(outDir, 'db', 'zod.gen.ts'),
@@ -82,6 +96,7 @@ export async function migrateAndCodegen(resolved) {
82
96
  coercionFile: resolved.coercionFile,
83
97
  manifestFile: resolved.manifestFile,
84
98
  classificationMapFile: resolved.classificationMapFile,
99
+ schemaJsonFile: resolved.schemaJsonFile,
85
100
  camelCase: resolved.camelCase,
86
101
  rootDir: resolved.rootDir,
87
102
  migrationsDir: resolved.migrationsDir,
@@ -107,7 +122,9 @@ export async function migrateAndCodegen(resolved) {
107
122
  coercionFile: resolved.coercionFile,
108
123
  manifestFile: resolved.manifestFile,
109
124
  classificationMapFile: resolved.classificationMapFile,
125
+ schemaJsonFile: resolved.schemaJsonFile,
110
126
  camelCase: resolved.camelCase,
127
+ rootDir: resolved.rootDir,
111
128
  migrationsDir: resolved.migrationsDir,
112
129
  });
113
130
  }
@@ -1,4 +1,4 @@
1
- import type { DbIntrospector, ColumnInfo } from '../db-introspector.js';
1
+ import type { DbIntrospector, ColumnInfo, ForeignKeyInfo, EnumInfo } from '../db-introspector.js';
2
2
  interface QueryClient {
3
3
  query<T = unknown>(sql: string, params?: unknown[]): Promise<{
4
4
  rows: T[];
@@ -11,6 +11,8 @@ export declare class PostgresIntrospector implements DbIntrospector {
11
11
  connect(): Promise<void>;
12
12
  listTables(): Promise<string[]>;
13
13
  getColumns(table: string): Promise<ColumnInfo[]>;
14
+ getForeignKeys(table: string): Promise<ForeignKeyInfo[]>;
15
+ listEnums(): Promise<EnumInfo[]>;
14
16
  close(): Promise<void>;
15
17
  }
16
18
  export {};