@pikku/cli 0.12.110 → 0.12.111

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 (158) hide show
  1. package/CHANGELOG.md +120 -0
  2. package/dist/.pikku/addon/index.d.ts +1 -1
  3. package/dist/.pikku/addon/index.js +1 -1
  4. package/dist/.pikku/addon/pikku-addon-types.gen.d.ts +1 -1
  5. package/dist/.pikku/addon/pikku-addon-types.gen.js +1 -1
  6. package/dist/.pikku/agent/index.d.ts +1 -1
  7. package/dist/.pikku/agent/index.js +1 -1
  8. package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
  9. package/dist/.pikku/agent/pikku-model-aliases.gen.js +1 -1
  10. package/dist/.pikku/auth/index.d.ts +1 -1
  11. package/dist/.pikku/auth/index.js +1 -1
  12. package/dist/.pikku/auth/pikku-auth-types.gen.d.ts +9 -2
  13. package/dist/.pikku/auth/pikku-auth-types.gen.js +1 -1
  14. package/dist/.pikku/channel/index.d.ts +1 -1
  15. package/dist/.pikku/channel/index.js +1 -1
  16. package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
  17. package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
  18. package/dist/.pikku/cli/index.d.ts +1 -1
  19. package/dist/.pikku/cli/index.js +1 -1
  20. package/dist/.pikku/cli/pikku-cli-channel.js +1 -1
  21. package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
  22. package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
  23. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
  24. package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
  25. package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
  26. package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
  27. package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
  28. package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
  29. package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
  30. package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
  31. package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
  32. package/dist/.pikku/error/index.d.ts +1 -1
  33. package/dist/.pikku/error/index.js +1 -1
  34. package/dist/.pikku/error/pikku-error-types.gen.d.ts +1 -1
  35. package/dist/.pikku/error/pikku-error-types.gen.js +1 -1
  36. package/dist/.pikku/function/index.d.ts +1 -1
  37. package/dist/.pikku/function/index.js +1 -1
  38. package/dist/.pikku/function/pikku-function-types.gen.d.ts +12 -2
  39. package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
  40. package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
  41. package/dist/.pikku/function/pikku-functions-meta.gen.json +2 -2
  42. package/dist/.pikku/function/pikku-functions.gen.js +1 -1
  43. package/dist/.pikku/gateway/index.d.ts +1 -1
  44. package/dist/.pikku/gateway/index.js +1 -1
  45. package/dist/.pikku/gateway/pikku-gateway-types.gen.d.ts +1 -1
  46. package/dist/.pikku/gateway/pikku-gateway-types.gen.js +1 -1
  47. package/dist/.pikku/http/index.d.ts +1 -1
  48. package/dist/.pikku/http/index.js +1 -1
  49. package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
  50. package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
  51. package/dist/.pikku/mcp/index.d.ts +1 -1
  52. package/dist/.pikku/mcp/index.js +1 -1
  53. package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
  54. package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
  55. package/dist/.pikku/middleware/index.d.ts +1 -1
  56. package/dist/.pikku/middleware/index.js +1 -1
  57. package/dist/.pikku/middleware/pikku-middleware-types.gen.d.ts +9 -2
  58. package/dist/.pikku/middleware/pikku-middleware-types.gen.js +1 -1
  59. package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +1 -1
  60. package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +1 -1
  61. package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
  62. package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
  63. package/dist/.pikku/pikku-services.gen.d.ts +1 -1
  64. package/dist/.pikku/queue/index.d.ts +1 -1
  65. package/dist/.pikku/queue/index.js +1 -1
  66. package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
  67. package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
  68. package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
  69. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
  70. package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
  71. package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
  72. package/dist/.pikku/scenarios/index.d.ts +1 -1
  73. package/dist/.pikku/scenarios/index.js +1 -1
  74. package/dist/.pikku/scenarios/pikku-personas.gen.js +1 -1
  75. package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
  76. package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
  77. package/dist/.pikku/scenarios/pikku-scenario-types.gen.d.ts +1 -1
  78. package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
  79. package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
  80. package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
  81. package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
  82. package/dist/.pikku/scheduler/index.d.ts +1 -1
  83. package/dist/.pikku/scheduler/index.js +1 -1
  84. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
  85. package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
  86. package/dist/.pikku/schemas/register.gen.js +1 -1
  87. package/dist/.pikku/scopes/index.d.ts +1 -1
  88. package/dist/.pikku/scopes/index.js +1 -1
  89. package/dist/.pikku/scopes/pikku-personas.gen.js +1 -1
  90. package/dist/.pikku/scopes/pikku-roles.gen.d.ts +1 -1
  91. package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
  92. package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
  93. package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
  94. package/dist/.pikku/secrets/index.d.ts +1 -1
  95. package/dist/.pikku/secrets/index.js +1 -1
  96. package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
  97. package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
  98. package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
  99. package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
  100. package/dist/.pikku/services/pikku-meta-service.gen.d.ts +1 -1
  101. package/dist/.pikku/services/pikku-meta-service.gen.js +1 -1
  102. package/dist/.pikku/setup/index.d.ts +1 -1
  103. package/dist/.pikku/setup/index.js +1 -1
  104. package/dist/.pikku/setup/pikku-setup-types.gen.d.ts +1 -1
  105. package/dist/.pikku/setup/pikku-setup-types.gen.js +1 -1
  106. package/dist/.pikku/trigger/index.d.ts +1 -1
  107. package/dist/.pikku/trigger/index.js +1 -1
  108. package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
  109. package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
  110. package/dist/.pikku/variables/index.d.ts +1 -1
  111. package/dist/.pikku/variables/index.js +1 -1
  112. package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
  113. package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
  114. package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
  115. package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
  116. package/dist/.pikku/workflow/index.d.ts +1 -1
  117. package/dist/.pikku/workflow/index.js +1 -1
  118. package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
  119. package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
  120. package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
  121. package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
  122. package/dist/bin/pikku-bin.mjs +2 -2
  123. package/dist/src/deploy/analyzer/analyzer.d.ts +1 -1
  124. package/dist/src/deploy/analyzer/index.d.ts +0 -1
  125. package/dist/src/deploy/build-pipeline.d.ts +2 -2
  126. package/dist/src/deploy/bundler/types.d.ts +1 -1
  127. package/dist/src/deploy/codegen/per-unit-codegen.d.ts +1 -1
  128. package/dist/src/deploy/plan/executor.d.ts +1 -1
  129. package/dist/src/deploy/plan/planner.d.ts +1 -1
  130. package/dist/src/deploy/plan/provider.d.ts +1 -1
  131. package/dist/src/deploy/server-entry.d.ts +1 -1
  132. package/dist/src/fabric/fabric-commands.d.ts +3 -3
  133. package/dist/src/fabric/functions/llm-key.function.d.ts +4 -4
  134. package/dist/src/functions/commands/db-generate.js +1 -1
  135. package/dist/src/functions/commands/deploy-apply.d.ts +1 -1
  136. package/dist/src/functions/commands/new-addon.js +14 -12
  137. package/dist/src/functions/db/better-auth-schema.js +5 -0
  138. package/dist/src/functions/db/local-db.d.ts +11 -3
  139. package/dist/src/functions/db/local-db.js +17 -8
  140. package/dist/src/functions/wirings/auth/serialize-auth-guards.d.ts +1 -1
  141. package/dist/src/functions/wirings/auth/serialize-auth-guards.js +9 -2
  142. package/dist/src/functions/wirings/functions/pikku-command-function-types-split.js +2 -2
  143. package/dist/src/functions/wirings/functions/serialize-function-types.js +11 -1
  144. package/dist/src/functions/wirings/middleware/serialize-middleware-types.d.ts +1 -1
  145. package/dist/src/functions/wirings/middleware/serialize-middleware-types.js +10 -2
  146. package/dist/src/utils/pikku-cli-config-error.d.ts +9 -0
  147. package/dist/src/utils/pikku-cli-config-error.js +9 -0
  148. package/dist/src/utils/pikku-cli-config.d.ts +2 -3
  149. package/dist/src/utils/pikku-cli-config.js +2 -3
  150. package/dist/src/utils/resolve-scaffold-feature.d.ts +6 -0
  151. package/dist/src/utils/resolve-scaffold-feature.js +22 -2
  152. package/dist/tsconfig.tsbuildinfo +1 -1
  153. package/package.json +8 -7
  154. package/surface.json +4 -26
  155. package/dist/src/deploy/analyzer/manifest.d.ts +0 -170
  156. package/dist/src/deploy/analyzer/manifest.js +0 -8
  157. package/dist/src/deploy/provider-adapter.d.ts +0 -176
  158. package/dist/src/deploy/provider-adapter.js +0 -10
@@ -1,4 +1,4 @@
1
- import type { DeploymentManifest } from '../analyzer/manifest.js';
1
+ import type { DeploymentManifest } from '@pikku/deploy';
2
2
  import type { PlanChange } from './types.js';
3
3
  export interface CurrentState {
4
4
  units: Array<{
@@ -10,7 +10,7 @@
10
10
  * fly.io, ECS, bare VM. Provider-specific config (Dockerfile, infra) is
11
11
  * emitted by the provider adapter on top of this entry + bundle.js.
12
12
  */
13
- import type { EntryGenerationContext } from './provider-adapter.js';
13
+ import type { EntryGenerationContext } from '@pikku/deploy';
14
14
  export declare function generateServerEntrySource(ctx: EntryGenerationContext): string;
15
15
  export declare const SERVER_DOCKERFILE = "# Generated by @pikku/cli \u2014 do not edit\n# Full image (not -slim): carries the build toolchain so externalised deps with\n# native addons can compile from source at `npm install` time.\nFROM node:26\n\nRUN apt-get update \\\n && apt-get install -y --no-install-recommends tini ca-certificates \\\n && rm -rf /var/lib/apt/lists/*\n\nRUN groupadd -r pikku && useradd -r -g pikku -u 1001 pikku\n\nWORKDIR /app\n\n# Pikku bundles user code + first-party deps inline; only externalised deps\n# (typically empty for pure-JS apps) are listed in package.json. We ship no\n# lockfile because the bundle has already pinned everything via the\n# extracted exact-dependencies \u2014 `npm install --omit=dev` resolves the\n# leftover externals (if any) at build time.\nCOPY --chown=pikku:pikku package.json ./\nRUN npm install --omit=dev --no-audit --no-fund\n\nCOPY --chown=pikku:pikku bundle.js ./\n\nUSER pikku\nENV NODE_ENV=production\nENV PORT=8080\nEXPOSE 8080\n\nENTRYPOINT [\"/usr/bin/tini\", \"--\"]\nCMD [\"node\", \"bundle.js\"]\n";
16
16
  export declare const SERVER_DOCKERIGNORE = "node_modules\n.git\n*.log\nmetafile.json\nexact-dependencies.json\n";
@@ -698,7 +698,7 @@ export declare const fabricCommands: {
698
698
  }, {
699
699
  proxyUrl: string;
700
700
  apiKey: string;
701
- format: "json" | "text" | "env" | "shell";
701
+ format: "text" | "json" | "env" | "shell";
702
702
  }, "rpc" | "session", import("../../.pikku/function/pikku-function-types.gen.js").PikkuFunctionSessionless<{
703
703
  shell?: boolean | undefined;
704
704
  env?: boolean | undefined;
@@ -706,7 +706,7 @@ export declare const fabricCommands: {
706
706
  }, {
707
707
  proxyUrl: string;
708
708
  apiKey: string;
709
- format: "json" | "text" | "env" | "shell";
709
+ format: "text" | "json" | "env" | "shell";
710
710
  }, "rpc" | "session", import("../../.pikku/function/pikku-function-types.gen.js").WiredServices, unknown> | import("../../.pikku/function/pikku-function-types.gen.js").PikkuFunction<{
711
711
  shell?: boolean | undefined;
712
712
  env?: boolean | undefined;
@@ -714,7 +714,7 @@ export declare const fabricCommands: {
714
714
  }, {
715
715
  proxyUrl: string;
716
716
  apiKey: string;
717
- format: "json" | "text" | "env" | "shell";
717
+ format: "text" | "json" | "env" | "shell";
718
718
  }, "rpc" | "session", import("../../.pikku/function/pikku-function-types.gen.js").WiredServices>, undefined, undefined>, import("../../.pikku/middleware/pikku-middleware-types.gen.js").PikkuMiddleware, (services: import("../../types/application-types.js").SingletonServices, data: any, session?: import("../../types/application-types.js").UserSession | undefined) => void | Promise<void>, string>;
719
719
  };
720
720
  };
@@ -8,8 +8,8 @@ export declare const FabricLLMKeyOutput: z.ZodObject<{
8
8
  proxyUrl: z.ZodString;
9
9
  apiKey: z.ZodString;
10
10
  format: z.ZodEnum<{
11
- json: "json";
12
11
  text: "text";
12
+ json: "json";
13
13
  env: "env";
14
14
  shell: "shell";
15
15
  }>;
@@ -21,7 +21,7 @@ export declare const FabricLLMKey: import("../../../.pikku/function/pikku-functi
21
21
  }, {
22
22
  proxyUrl: string;
23
23
  apiKey: string;
24
- format: "json" | "text" | "env" | "shell";
24
+ format: "text" | "json" | "env" | "shell";
25
25
  }, "rpc" | "session", import("../../../.pikku/function/pikku-function-types.gen.js").PikkuFunctionSessionless<{
26
26
  shell?: boolean | undefined;
27
27
  env?: boolean | undefined;
@@ -29,7 +29,7 @@ export declare const FabricLLMKey: import("../../../.pikku/function/pikku-functi
29
29
  }, {
30
30
  proxyUrl: string;
31
31
  apiKey: string;
32
- format: "json" | "text" | "env" | "shell";
32
+ format: "text" | "json" | "env" | "shell";
33
33
  }, "rpc" | "session", import("../../../.pikku/function/pikku-function-types.gen.js").WiredServices, unknown> | import("../../../.pikku/function/pikku-function-types.gen.js").PikkuFunction<{
34
34
  shell?: boolean | undefined;
35
35
  env?: boolean | undefined;
@@ -37,7 +37,7 @@ export declare const FabricLLMKey: import("../../../.pikku/function/pikku-functi
37
37
  }, {
38
38
  proxyUrl: string;
39
39
  apiKey: string;
40
- format: "json" | "text" | "env" | "shell";
40
+ format: "text" | "json" | "env" | "shell";
41
41
  }, "rpc" | "session", import("../../../.pikku/function/pikku-function-types.gen.js").WiredServices>, undefined, undefined>;
42
42
  export declare const renderLLMKey: (_services: unknown, data: {
43
43
  proxyUrl: string;
@@ -14,7 +14,7 @@ export const dbGenerate = pikkuSessionlessFunc({
14
14
  }
15
15
  const state = await getInspectorState();
16
16
  const addons = [...state.rpc.wireAddonDeclarations.values()].map(({ package: pkg, remote }) => ({ package: pkg, remote }));
17
- const { upToDate, written } = await generateMigrations(resolved, config.rootDir, config.srcDirectories, logger, addons);
17
+ const { upToDate, written } = await generateMigrations(resolved, config.rootDir, config.srcDirectories, logger, addons, state.serviceAggregation.requiredServices);
18
18
  for (const source of upToDate) {
19
19
  logger.info(`db generate: ${source} is already covered by existing migrations`);
20
20
  }
@@ -1,4 +1,4 @@
1
- import type { ProviderAdapter, EntryGenerationContext } from '../../deploy/provider-adapter.js';
1
+ import type { ProviderAdapter, EntryGenerationContext } from '@pikku/deploy';
2
2
  import type { InspectorState } from '@pikku/inspector';
3
3
  export declare function getEntryContext(unitDir: string, pikkuDir: string, unit: EntryGenerationContext['unit'], inspectorState: InspectorState): EntryGenerationContext;
4
4
  export declare function resolveProvider(config?: {
@@ -139,17 +139,20 @@ export function getAddonFiles(vars, flags) {
139
139
  // these never have to point at the source tree.
140
140
  imports: {
141
141
  '#pikku/*.js': './dist/.pikku/*.js',
142
- '#pikku/*': './dist/.pikku/*/index.js',
142
+ '#pikku/*': ['./dist/.pikku/*/index.js', './dist/.pikku/*'],
143
143
  },
144
+ // An addon's generated tree roots at `.pikku/addon/`, but a consumer
145
+ // reaches it by the same subpath an application would — the leaf is the
146
+ // package's business, not its callers'.
144
147
  exports: {
145
148
  '.': {
146
149
  types: './dist/src/index.d.ts',
147
150
  import: './dist/src/index.js',
148
151
  },
149
- './.pikku/*': './dist/.pikku/*',
150
- './.pikku/pikku-metadata.gen.json': './dist/.pikku/pikku-metadata.gen.json',
152
+ './.pikku/*': './dist/.pikku/addon/*',
153
+ './.pikku/pikku-metadata.gen.json': './dist/.pikku/addon/pikku-metadata.gen.json',
151
154
  './.pikku/rpc/pikku-rpc-wirings-map.internal.gen.js': {
152
- types: './dist/.pikku/rpc/pikku-rpc-wirings-map.internal.gen.d.ts',
155
+ types: './dist/.pikku/addon/rpc/pikku-rpc-wirings-map.internal.gen.d.ts',
153
156
  },
154
157
  },
155
158
  files: ['dist'],
@@ -204,7 +207,7 @@ export function getAddonFiles(vars, flags) {
204
207
  resolveJsonModule: true,
205
208
  paths: {
206
209
  '#pikku/*.js': ['./.pikku/*.ts'],
207
- '#pikku/*': ['./.pikku/*/index.ts'],
210
+ '#pikku/*': ['./.pikku/*/index.ts', './.pikku/*'],
208
211
  },
209
212
  },
210
213
  include: ['src/**/*', 'types/**/*', '.pikku/**/*.ts'],
@@ -322,15 +325,14 @@ export const createSingletonServices = pikkuAddonServices(async (
322
325
  `;
323
326
  }
324
327
  else {
328
+ // The plain service takes no constructor argument — the secret, credential
329
+ // and oauth variants are the ones handed something to authenticate with.
325
330
  files['src/services.ts'] =
326
331
  `import { ${pascalName}Service } from './${name}-api.service.js'
327
332
  import { pikkuAddonServices } from '#pikku/addon/setup'
328
333
 
329
- export const createSingletonServices = pikkuAddonServices(async (
330
- config,
331
- { variables }
332
- ) => {
333
- const ${camelName} = new ${pascalName}Service(variables)
334
+ export const createSingletonServices = pikkuAddonServices(async () => {
335
+ const ${camelName} = new ${pascalName}Service()
334
336
 
335
337
  return { ${camelName} }
336
338
  })
@@ -688,7 +690,7 @@ export function getTestFiles(vars) {
688
690
  type: 'module',
689
691
  imports: {
690
692
  '#pikku/*.js': './.pikku/*.ts',
691
- '#pikku/*': './.pikku/*/index.ts',
693
+ '#pikku/*': ['./.pikku/*/index.ts', './.pikku/*'],
692
694
  },
693
695
  scripts: {
694
696
  pretest: 'pikku all',
@@ -727,7 +729,7 @@ export function getTestFiles(vars) {
727
729
  types: ['node'],
728
730
  paths: {
729
731
  '#pikku/*.js': ['./.pikku/*.ts'],
730
- '#pikku/*': ['./.pikku/*/index.ts'],
732
+ '#pikku/*': ['./.pikku/*/index.ts', './.pikku/*'],
731
733
  },
732
734
  },
733
735
  include: ['src/*', '.pikku/**/*', 'types/**/*'],
@@ -97,6 +97,11 @@ function fakeSecretService() {
97
97
  const placeholder = 'schema-introspection-only';
98
98
  return {
99
99
  getSecret: async () => createSecretValue(placeholder),
100
+ // Every key resolves, for the same reason `hasSecret` says yes to all of
101
+ // them: an auth factory that batches its secrets must come back with the
102
+ // whole batch, or it throws on the one it considers required and the
103
+ // schema is never read at all.
104
+ getSecrets: async (keys) => Object.fromEntries(keys.map((key) => [key, createSecretValue(placeholder)])),
100
105
  hasSecret: async () => true,
101
106
  setSecret: async () => { },
102
107
  };
@@ -197,10 +197,18 @@ export interface DesiredRuntimeSchema extends DesiredSchema {
197
197
  * scope tables, so the schemas that wanted them are left out and returned in
198
198
  * `skipped` — reported rather than dropped, because the tables they would have
199
199
  * recognised now have nothing to explain them.
200
+ *
201
+ * `services` narrows the declaration to the schemas the services a project's
202
+ * code actually reaches own, for the caller that is about to write a migration:
203
+ * a project with no agents should not carry `agent_threads` in its migrations
204
+ * forever. Omit it and the whole declaration answers, which is what the caller
205
+ * that is *recognising* tables wants — a table created by a service that has
206
+ * since been dropped still has to read as a runtime table rather than as an
207
+ * unexplained one.
200
208
  */
201
209
  export declare function desiredRuntimeSchema(resolved: ResolvedDb, rootDir: string, srcDirectories: string[], logger: {
202
210
  error: (msg: string) => void;
203
- }): Promise<DesiredRuntimeSchema>;
211
+ }, services?: ReadonlySet<string>): Promise<DesiredRuntimeSchema>;
204
212
  export declare function introspectSchema(resolved: ResolvedDb): Promise<SchemaMap>;
205
213
  export interface SchemaDriftResult {
206
214
  /** In the migrations, absent from the database — it is behind. */
@@ -367,7 +375,7 @@ export declare function addonSchemaSources(rootDir: string, dialect: ResolvedDb[
367
375
  */
368
376
  export declare function schemaSources(resolved: ResolvedDb, rootDir: string, srcDirectories: string[], logger: {
369
377
  error: (msg: string) => void;
370
- }, addons?: AddonDeclaration[]): Promise<SchemaSource[]>;
378
+ }, addons?: AddonDeclaration[], services?: ReadonlySet<string>): Promise<SchemaSource[]>;
371
379
  export interface GeneratedMigration {
372
380
  source: string;
373
381
  file: string;
@@ -402,5 +410,5 @@ export interface GenerateResult {
402
410
  */
403
411
  export declare function generateMigrations(resolved: ResolvedDb, rootDir: string, srcDirectories: string[], logger: {
404
412
  error: (msg: string) => void;
405
- }, addons?: AddonDeclaration[]): Promise<GenerateResult>;
413
+ }, addons?: AddonDeclaration[], services?: ReadonlySet<string>): Promise<GenerateResult>;
406
414
  export {};
@@ -6,7 +6,7 @@ import { runInNewContext } from 'node:vm';
6
6
  import { transformSync } from 'esbuild';
7
7
  import { CamelCasePlugin, Kysely, PostgresDialect } from 'kysely';
8
8
  import { migrate, baselineMigrations, } from './db-migrator.js';
9
- import { applyPikkuSchemas, compilePikkuSchemas, pikkuSchemas, resolveRequirements, createCoercionPlugin, } from '@pikku/kysely';
9
+ import { applyPikkuSchemas, compilePikkuSchemas, pikkuSchemas, requiredPikkuSchemas, resolveRequirements, createCoercionPlugin, } from '@pikku/kysely';
10
10
  import { loadAuthOptions, getAuthMigrations } from './better-auth-schema.js';
11
11
  import { generateSchemaTypes } from './db-codegen.js';
12
12
  import { generateZodTypes } from './zod-codegen.js';
@@ -797,13 +797,22 @@ async function applyAuthSchema(kysely, rootDir, srcDirectories, logger) {
797
797
  * scope tables, so the schemas that wanted them are left out and returned in
798
798
  * `skipped` — reported rather than dropped, because the tables they would have
799
799
  * recognised now have nothing to explain them.
800
+ *
801
+ * `services` narrows the declaration to the schemas the services a project's
802
+ * code actually reaches own, for the caller that is about to write a migration:
803
+ * a project with no agents should not carry `agent_threads` in its migrations
804
+ * forever. Omit it and the whole declaration answers, which is what the caller
805
+ * that is *recognising* tables wants — a table created by a service that has
806
+ * since been dropped still has to read as a runtime table rather than as an
807
+ * unexplained one.
800
808
  */
801
- export async function desiredRuntimeSchema(resolved, rootDir, srcDirectories, logger) {
809
+ export async function desiredRuntimeSchema(resolved, rootDir, srcDirectories, logger, services) {
810
+ const declared = services ? requiredPikkuSchemas(services) : pikkuSchemas;
802
811
  const skipped = [];
803
812
  const collect = async (db, introspect) => {
804
813
  await applyAuthSchema(db, rootDir, srcDirectories, logger);
805
814
  const before = await introspect();
806
- const { types, unmet } = await resolveRequirements(db);
815
+ const { types, unmet } = await resolveRequirements(db, declared);
807
816
  for (const { schema, requirement } of unmet) {
808
817
  skipped.push({
809
818
  schema: schema.name,
@@ -812,7 +821,7 @@ export async function desiredRuntimeSchema(resolved, rootDir, srcDirectories, lo
812
821
  });
813
822
  }
814
823
  const unavailable = new Set(unmet.map(({ schema }) => schema.name));
815
- const schemas = pikkuSchemas.filter((s) => !unavailable.has(s.name));
824
+ const schemas = declared.filter((s) => !unavailable.has(s.name));
816
825
  await applyPikkuSchemas(db, schemas);
817
826
  const after = await introspect();
818
827
  for (const table of before.keys())
@@ -1109,7 +1118,7 @@ export async function addonSchemaSources(rootDir, dialect, addons, logger) {
1109
1118
  * Auth comes first because the runtime's scope tables reference its `user`
1110
1119
  * table; addons come last because they may reference either.
1111
1120
  */
1112
- export async function schemaSources(resolved, rootDir, srcDirectories, logger, addons = []) {
1121
+ export async function schemaSources(resolved, rootDir, srcDirectories, logger, addons = [], services) {
1113
1122
  const sources = [];
1114
1123
  const auth = await desiredAuthSchema(resolved, rootDir, srcDirectories, logger);
1115
1124
  if (auth) {
@@ -1119,7 +1128,7 @@ export async function schemaSources(resolved, rootDir, srcDirectories, logger, a
1119
1128
  origin: 'pikkuBetterAuth (Better Auth)',
1120
1129
  });
1121
1130
  }
1122
- const runtime = await desiredRuntimeSchema(resolved, rootDir, srcDirectories, logger);
1131
+ const runtime = await desiredRuntimeSchema(resolved, rootDir, srcDirectories, logger, services);
1123
1132
  if (runtime.tables.size > 0) {
1124
1133
  sources.push({
1125
1134
  name: 'pikku-runtime',
@@ -1185,8 +1194,8 @@ function addColumnStatements(table, columns) {
1185
1194
  * Migrations are written one file per source, numbered in dependency order, so
1186
1195
  * a project can review and apply them independently.
1187
1196
  */
1188
- export async function generateMigrations(resolved, rootDir, srcDirectories, logger, addons = []) {
1189
- const sources = await schemaSources(resolved, rootDir, srcDirectories, logger, addons);
1197
+ export async function generateMigrations(resolved, rootDir, srcDirectories, logger, addons = [], services) {
1198
+ const sources = await schemaSources(resolved, rootDir, srcDirectories, logger, addons, services);
1190
1199
  const result = { upToDate: [], written: [] };
1191
1200
  for (const source of sources) {
1192
1201
  // Re-read after each write: a migration just written for an earlier source
@@ -9,4 +9,4 @@
9
9
  * references it; that config imports it back, which is a type-only edge and
10
10
  * carries no runtime cycle.
11
11
  */
12
- export declare const serializeAuthGuards: (functionTypesImportPath: string, packageName?: string) => string;
12
+ export declare const serializeAuthGuards: (functionTypesImportPath: string, requiredServicesTypeImport: string, packageName?: string) => string;
@@ -9,7 +9,7 @@
9
9
  * references it; that config imports it back, which is a type-only edge and
10
10
  * carries no runtime cycle.
11
11
  */
12
- export const serializeAuthGuards = (functionTypesImportPath, packageName) => {
12
+ export const serializeAuthGuards = (functionTypesImportPath, requiredServicesTypeImport, packageName) => {
13
13
  const packageNameValue = packageName ? `'${packageName}'` : 'null';
14
14
  return `/**
15
15
  * Permission, auth gate and credential definitions
@@ -29,11 +29,18 @@ import type {
29
29
  Session,
30
30
  SingletonServices,
31
31
  WiredServices,
32
- WiredSingletonServices,
33
32
  } from '${functionTypesImportPath}'
33
+ ${requiredServicesTypeImport}
34
34
 
35
35
  export { defineCredential } from '@pikku/core/credential'
36
36
 
37
+ /**
38
+ * Derived here rather than imported: nothing outside a generated leaf ever
39
+ * names this intersection, so the function leaf keeps it to itself and the
40
+ * leaves that want it spell it out.
41
+ */
42
+ type WiredSingletonServices = RequiredSingletonServices & SingletonServices
43
+
37
44
  /** \`WiredSingletonServices\` without \`secrets\`, for auth gates. */
38
45
  type WiredAuthServices = SecretlessServices<WiredSingletonServices>
39
46
 
@@ -32,11 +32,11 @@ export const pikkuFunctionTypesSplit = pikkuSessionlessFunc({
32
32
  ? `import type { ${pikkuConfigType.type} } from '${getFileImportRelativePath(functionTypesFile, pikkuConfigType.typePath, packageMappings)}'`
33
33
  : '// Config type not found, will use fallback';
34
34
  const content = serializeFunctionTypes(`import type { ${userSessionType.type} } from '${getFileImportRelativePath(functionTypesFile, userSessionType.typePath, packageMappings)}'`, userSessionType.type, `import type { ${singletonServicesType.type} } from '${getFileImportRelativePath(functionTypesFile, singletonServicesType.typePath, packageMappings)}'`, singletonServicesType.type, `import type { ${wireServicesType.type} } from '${getFileImportRelativePath(functionTypesFile, wireServicesType.typePath, packageMappings)}'`, wireServicesType.type, `import type { TypedPikkuRPC, FlattenedRPCMap } from '${getFileImportRelativePath(functionTypesFile, rpcInternalMapDeclarationFile, packageMappings)}'`, `import type { RequiredSingletonServices } from '${getFileImportRelativePath(functionTypesFile, servicesFile, packageMappings)}'`, configTypeImport, getFileImportRelativePath(functionTypesFile, authGuardsFile, packageMappings), undefined, typeof config.addon === 'object' ? config.addon?.categories : undefined, `import type { ScopeId } from '${getFileImportRelativePath(functionTypesFile, config.scopesFile, packageMappings)}'`, credentialsTypeImport, getFileImportRelativePath(functionTypesFile, middlewareTypesFile, packageMappings), { addon: !!config.addon });
35
- await writeFileInDir(logger, middlewareTypesFile, serializeMiddlewareTypes(getFileImportRelativePath(middlewareTypesFile, functionTypesFile, packageMappings), config.addonName));
35
+ await writeFileInDir(logger, middlewareTypesFile, serializeMiddlewareTypes(getFileImportRelativePath(middlewareTypesFile, functionTypesFile, packageMappings), `import type { RequiredSingletonServices } from '${getFileImportRelativePath(middlewareTypesFile, servicesFile, packageMappings)}'`, config.addonName));
36
36
  await writeFileInDir(logger, setupTypesFile, serializeSetupTypes(getFileImportRelativePath(setupTypesFile, functionTypesFile, packageMappings), pikkuConfigType
37
37
  ? `import type { ${pikkuConfigType.type} } from '${getFileImportRelativePath(setupTypesFile, pikkuConfigType.typePath, packageMappings)}'`
38
38
  : '// Config type not found, will use fallback', pikkuConfigType?.type, `import type { RequiredSingletonServices, RequiredWireServices } from '${getFileImportRelativePath(setupTypesFile, servicesFile, packageMappings)}'`));
39
- await writeFileInDir(logger, authGuardsFile, serializeAuthGuards(getFileImportRelativePath(authGuardsFile, functionTypesFile, packageMappings), config.addonName));
39
+ await writeFileInDir(logger, authGuardsFile, serializeAuthGuards(getFileImportRelativePath(authGuardsFile, functionTypesFile, packageMappings), `import type { RequiredSingletonServices } from '${getFileImportRelativePath(authGuardsFile, servicesFile, packageMappings)}'`, config.addonName));
40
40
  const addonRefs = serializeAddonRefs({
41
41
  addonHttp: visitState.exportedContracts.addonHttp,
42
42
  addonChannel: visitState.exportedContracts.addonChannel,
@@ -60,8 +60,18 @@ ${userSessionTypeName !== 'Session' ? `export type Session = ${userSessionTypeNa
60
60
  * services **non-optional** at every call site. A service is optional only when
61
61
  * nothing destructures it — in which case it is never created either. This is
62
62
  * why an \`if (!service)\` guard inside a function body is always dead code.
63
+ *
64
+ * Only the wire-services half lives here. The singleton half is declared by the
65
+ * auth and middleware leaves that use it, because whether a name earns its
66
+ * export is measured rather than chosen: emit declarations for a project and
67
+ * \`WiredServices\` is named by 147 of its \`.d.ts\` files, while the singleton
68
+ * intersection is named by none outside the leaves that declare it. Export the
69
+ * latter and it is a compatibility promise nothing asked for; unexport
70
+ * \`WiredServices\` and every wired module inlines the intersection instead,
71
+ * which asks it to name each member service through a specifier it does not
72
+ * have — 3308 TS2883s. \`--noEmit\` cannot surface any of that, so re-check with
73
+ * \`tsc --declaration --emitDeclarationOnly\` before moving either one.
63
74
  */
64
- export type WiredSingletonServices = RequiredSingletonServices & SingletonServices
65
75
  export type WiredServices = SecretlessServices<RequiredSingletonServices & Services>
66
76
 
67
77
  /**
@@ -6,4 +6,4 @@
6
6
  * here rather than alongside the function types that consume it; those types
7
7
  * import it back, which is a type-only edge and carries no runtime cycle.
8
8
  */
9
- export declare const serializeMiddlewareTypes: (functionTypesImportPath: string, packageName?: string) => string;
9
+ export declare const serializeMiddlewareTypes: (functionTypesImportPath: string, requiredServicesTypeImport: string, packageName?: string) => string;
@@ -6,7 +6,7 @@
6
6
  * here rather than alongside the function types that consume it; those types
7
7
  * import it back, which is a type-only edge and carries no runtime cycle.
8
8
  */
9
- export const serializeMiddlewareTypes = (functionTypesImportPath, packageName) => {
9
+ export const serializeMiddlewareTypes = (functionTypesImportPath, requiredServicesTypeImport, packageName) => {
10
10
  const packageNameValue = packageName ? `'${packageName}'` : 'null';
11
11
  return `/**
12
12
  * Middleware type definitions and registration for all wirings
@@ -21,11 +21,19 @@ import {
21
21
  addChannelMiddleware as addChannelMiddlewareCore,
22
22
  } from '@pikku/core/channel'
23
23
  import type { PikkuAgentMiddlewareHooks } from '@pikku/core/agent'
24
- import type { Services, SingletonServices, WiredSingletonServices } from '${functionTypesImportPath}'
24
+ import type { Services, SingletonServices } from '${functionTypesImportPath}'
25
+ ${requiredServicesTypeImport}
25
26
 
26
27
  export { cors } from '@pikku/core/middleware'
27
28
  export type { MiddlewarePriority }
28
29
 
30
+ /**
31
+ * Derived here rather than imported: nothing outside a generated leaf ever
32
+ * names this intersection, so the function leaf keeps it to itself and the
33
+ * leaves that want it spell it out.
34
+ */
35
+ type WiredSingletonServices = RequiredSingletonServices & SingletonServices
36
+
29
37
  /**
30
38
  * Type-safe middleware definition that can access your application's services and session.
31
39
  * Use this to define reusable middleware that can be applied to multiple wirings.
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A config that was found and parsed, but says something impossible.
3
+ *
4
+ * Its message reaches the developer verbatim — every other error out of the
5
+ * loader is reported as "failed to load", which sends the reader hunting for a
6
+ * broken file rather than the line that is wrong.
7
+ */
8
+ export declare class PikkuCLIConfigError extends Error {
9
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A config that was found and parsed, but says something impossible.
3
+ *
4
+ * Its message reaches the developer verbatim — every other error out of the
5
+ * loader is reported as "failed to load", which sends the reader hunting for a
6
+ * broken file rather than the line that is wrong.
7
+ */
8
+ export class PikkuCLIConfigError extends Error {
9
+ }
@@ -1,5 +1,6 @@
1
1
  import type { PikkuCLIConfig } from '../../types/config.js';
2
2
  import type { CLILogger } from '../services/cli-logger.service.js';
3
+ import { PikkuCLIConfigError } from './pikku-cli-config-error.js';
3
4
  export declare const getPikkuCLIConfig: (logger: CLILogger, configFile: string | undefined, requiredFields: Array<keyof PikkuCLIConfig>, exitProcess?: boolean, outDirOverride?: string) => Promise<PikkuCLIConfig>;
4
5
  /**
5
6
  * Like `getPikkuCLIConfig`, but returns null when the project has no config at
@@ -12,9 +13,7 @@ export declare const getPikkuCLIConfig: (logger: CLILogger, configFile: string |
12
13
  * a real error, and staying quiet about it would hide it.
13
14
  */
14
15
  export declare const tryGetPikkuCLIConfig: (logger: CLILogger, configFile: string | undefined, requiredFields: Array<keyof PikkuCLIConfig>, outDirOverride?: string) => Promise<PikkuCLIConfig | null>;
15
- /** A config that was found and parsed, but says something impossible. */
16
- export declare class PikkuCLIConfigError extends Error {
17
- }
16
+ export { PikkuCLIConfigError };
18
17
  /**
19
18
  * The two schema registers must be two directories.
20
19
  *
@@ -2,6 +2,7 @@ import { join, dirname, resolve, isAbsolute, parse as parsePath } from 'path';
2
2
  import { readdir, readFile } from 'fs/promises';
3
3
  import { resolveScaffoldFeature } from './resolve-scaffold-feature.js';
4
4
  import { setScaffoldWriteGuard } from './file-writer.js';
5
+ import { PikkuCLIConfigError } from './pikku-cli-config-error.js';
5
6
  const CLIENT_FILE_KEYS = [
6
7
  'fetchFile',
7
8
  'websocketFile',
@@ -59,9 +60,7 @@ async function findConfigFile() {
59
60
  }
60
61
  return configFile;
61
62
  }
62
- /** A config that was found and parsed, but says something impossible. */
63
- export class PikkuCLIConfigError extends Error {
64
- }
63
+ export { PikkuCLIConfigError };
65
64
  /**
66
65
  * The two schema registers must be two directories.
67
66
  *
@@ -16,5 +16,11 @@ export type ResolvedScaffoldFeature = {
16
16
  * A bare string is refused rather than read as a path: under `boolean | object`
17
17
  * no string is valid, and guessing one into `path` would turn a typo into a
18
18
  * generated file nobody asked for.
19
+ *
20
+ * An unrecognised key is refused for the same reason, and `auth` is refused by
21
+ * name. It was a real key once, and one a config can still be carrying: a
22
+ * config that sets it is asking for a gate that no longer exists there, so
23
+ * ignoring it silently would leave the file saying something untrue about the
24
+ * surface it configures.
19
25
  */
20
26
  export declare const resolveScaffoldFeature: (feature: string, value: PikkuScaffoldFeature | undefined) => ResolvedScaffoldFeature;
@@ -1,3 +1,5 @@
1
+ import { PikkuCLIConfigError } from './pikku-cli-config-error.js';
2
+ const SCAFFOLD_FEATURE_KEYS = ['path'];
1
3
  /**
2
4
  * Reads one `scaffold.<feature>` value.
3
5
  *
@@ -9,6 +11,12 @@
9
11
  * A bare string is refused rather than read as a path: under `boolean | object`
10
12
  * no string is valid, and guessing one into `path` would turn a typo into a
11
13
  * generated file nobody asked for.
14
+ *
15
+ * An unrecognised key is refused for the same reason, and `auth` is refused by
16
+ * name. It was a real key once, and one a config can still be carrying: a
17
+ * config that sets it is asking for a gate that no longer exists there, so
18
+ * ignoring it silently would leave the file saying something untrue about the
19
+ * surface it configures.
12
20
  */
13
21
  export const resolveScaffoldFeature = (feature, value) => {
14
22
  if (value === undefined || value === false) {
@@ -17,11 +25,23 @@ export const resolveScaffoldFeature = (feature, value) => {
17
25
  if (value === true) {
18
26
  return { enabled: true };
19
27
  }
20
- if (typeof value !== 'object') {
21
- throw new Error(`pikku.config.json: scaffold.${feature} must be true, false, or an object ` +
28
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
29
+ throw new PikkuCLIConfigError(`pikku.config.json: scaffold.${feature} must be true, false, or an object ` +
22
30
  `like { "path": "src/${feature}.gen.ts" } — received ${JSON.stringify(value)}. ` +
23
31
  `A bare string is never a shorthand for a path.`);
24
32
  }
33
+ for (const key of Object.keys(value)) {
34
+ if (key === 'auth') {
35
+ throw new PikkuCLIConfigError(`pikku.config.json: scaffold.${feature} no longer takes "auth" — a scaffold ` +
36
+ `flag says whether a surface is generated, not who may call it. ` +
37
+ `Authentication is declared on the function, its wiring, its scopes and ` +
38
+ `its addon, and enforced there on every call. Remove the key.`);
39
+ }
40
+ if (!SCAFFOLD_FEATURE_KEYS.includes(key)) {
41
+ throw new PikkuCLIConfigError(`pikku.config.json: scaffold.${feature} has no "${key}" option — the only ` +
42
+ `key is "path", which overrides where the generated file is written.`);
43
+ }
44
+ }
25
45
  return {
26
46
  enabled: true,
27
47
  path: value.path,