@pikku/inspector 0.12.0 → 0.12.2

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 (112) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/add/add-ai-agent.d.ts +1 -1
  3. package/dist/add/add-ai-agent.js +1 -1
  4. package/dist/add/add-channel.js +14 -0
  5. package/dist/add/add-cli.d.ts +1 -1
  6. package/dist/add/add-cli.js +29 -8
  7. package/dist/add/add-file-extends-core-type.d.ts +1 -1
  8. package/dist/add/add-file-with-config.d.ts +1 -1
  9. package/dist/add/add-file-with-factory.d.ts +1 -1
  10. package/dist/add/add-file-with-factory.js +2 -2
  11. package/dist/add/add-functions.d.ts +1 -1
  12. package/dist/add/add-functions.js +6 -7
  13. package/dist/add/add-gateway.d.ts +2 -0
  14. package/dist/add/add-gateway.js +57 -0
  15. package/dist/add/add-http-route.d.ts +3 -2
  16. package/dist/add/add-http-route.js +5 -1
  17. package/dist/add/add-http-routes.d.ts +1 -1
  18. package/dist/add/add-http-routes.js +1 -0
  19. package/dist/add/add-keyed-wiring.d.ts +1 -1
  20. package/dist/add/add-mcp-prompt.d.ts +1 -1
  21. package/dist/add/add-mcp-resource.d.ts +1 -1
  22. package/dist/add/add-queue-worker.d.ts +1 -1
  23. package/dist/add/add-rpc-invocations.d.ts +3 -3
  24. package/dist/add/add-rpc-invocations.js +10 -10
  25. package/dist/add/add-schedule.d.ts +1 -1
  26. package/dist/add/add-secret.d.ts +1 -1
  27. package/dist/add/add-trigger.d.ts +1 -1
  28. package/dist/add/add-trigger.js +2 -2
  29. package/dist/add/add-wire-addon.d.ts +7 -0
  30. package/dist/add/add-wire-addon.js +70 -0
  31. package/dist/add/add-workflow.d.ts +1 -1
  32. package/dist/error-codes.d.ts +1 -0
  33. package/dist/error-codes.js +1 -0
  34. package/dist/index.d.ts +1 -1
  35. package/dist/inspector.d.ts +1 -1
  36. package/dist/inspector.js +11 -4
  37. package/dist/types.d.ts +31 -20
  38. package/dist/utils/compute-required-schemas.js +1 -2
  39. package/dist/utils/contract-hashes.d.ts +20 -3
  40. package/dist/utils/contract-hashes.js +77 -10
  41. package/dist/utils/custom-types-generator.d.ts +1 -1
  42. package/dist/utils/detect-schema-vendor.d.ts +1 -1
  43. package/dist/utils/does-type-extend-core-type.d.ts +1 -1
  44. package/dist/utils/ensure-function-metadata.d.ts +1 -1
  45. package/dist/utils/extract-services.d.ts +1 -1
  46. package/dist/utils/filter-inspector-state.d.ts +1 -1
  47. package/dist/utils/filter-utils.d.ts +2 -2
  48. package/dist/utils/get-files-and-methods.d.ts +1 -1
  49. package/dist/utils/middleware.d.ts +2 -2
  50. package/dist/utils/permissions.d.ts +2 -2
  51. package/dist/utils/post-process.d.ts +4 -3
  52. package/dist/utils/post-process.js +24 -8
  53. package/dist/utils/resolve-addon-package.d.ts +16 -0
  54. package/dist/utils/{resolve-external-package.js → resolve-addon-package.js} +8 -8
  55. package/dist/utils/schema-generator.d.ts +2 -2
  56. package/dist/utils/serialize-inspector-state.d.ts +17 -3
  57. package/dist/utils/serialize-inspector-state.js +14 -2
  58. package/dist/utils/validate-auth-sessionless.d.ts +3 -0
  59. package/dist/utils/validate-auth-sessionless.js +14 -0
  60. package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +1 -1
  61. package/dist/visit.d.ts +1 -1
  62. package/dist/visit.js +4 -0
  63. package/package.json +3 -3
  64. package/src/add/add-ai-agent.ts +2 -2
  65. package/src/add/add-channel.ts +17 -0
  66. package/src/add/add-cli.ts +53 -15
  67. package/src/add/add-file-extends-core-type.ts +1 -1
  68. package/src/add/add-file-with-config.ts +1 -1
  69. package/src/add/add-file-with-factory.ts +3 -3
  70. package/src/add/add-functions.ts +21 -19
  71. package/src/add/add-gateway.ts +95 -0
  72. package/src/add/add-http-route.ts +19 -2
  73. package/src/add/add-http-routes.ts +2 -1
  74. package/src/add/add-keyed-wiring.ts +1 -1
  75. package/src/add/add-mcp-prompt.ts +1 -1
  76. package/src/add/add-mcp-resource.ts +1 -1
  77. package/src/add/add-queue-worker.ts +1 -1
  78. package/src/add/add-rpc-invocations.ts +11 -11
  79. package/src/add/add-schedule.ts +1 -1
  80. package/src/add/add-secret.ts +1 -1
  81. package/src/add/add-trigger.ts +4 -4
  82. package/src/add/add-wire-addon.ts +80 -0
  83. package/src/add/add-workflow.ts +2 -2
  84. package/src/error-codes.ts +1 -0
  85. package/src/index.ts +1 -0
  86. package/src/inspector.ts +17 -5
  87. package/src/types.ts +38 -20
  88. package/src/utils/compute-required-schemas.ts +1 -2
  89. package/src/utils/contract-hashes.test.ts +30 -5
  90. package/src/utils/contract-hashes.ts +110 -14
  91. package/src/utils/custom-types-generator.ts +1 -1
  92. package/src/utils/detect-schema-vendor.ts +1 -1
  93. package/src/utils/does-type-extend-core-type.ts +1 -1
  94. package/src/utils/ensure-function-metadata.ts +1 -1
  95. package/src/utils/extract-services.ts +1 -1
  96. package/src/utils/filter-inspector-state.test.ts +3 -5
  97. package/src/utils/filter-inspector-state.ts +7 -2
  98. package/src/utils/filter-utils.test.ts +1 -1
  99. package/src/utils/filter-utils.ts +2 -2
  100. package/src/utils/get-files-and-methods.ts +1 -1
  101. package/src/utils/middleware.ts +2 -2
  102. package/src/utils/permissions.ts +2 -2
  103. package/src/utils/post-process.ts +34 -12
  104. package/src/utils/{resolve-external-package.ts → resolve-addon-package.ts} +17 -10
  105. package/src/utils/resolve-versions.test.ts +1 -1
  106. package/src/utils/schema-generator.ts +4 -4
  107. package/src/utils/serialize-inspector-state.ts +35 -5
  108. package/src/utils/validate-auth-sessionless.ts +29 -0
  109. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +2 -2
  110. package/src/visit.ts +9 -1
  111. package/tsconfig.tsbuildinfo +1 -1
  112. package/dist/utils/resolve-external-package.d.ts +0 -12
package/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  ## 0.12.0
2
2
 
3
+ ## 0.12.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 3e04565: chore: update dependencies to latest minor/patch versions
8
+ - Updated dependencies [cc4c9e9]
9
+ - Updated dependencies [3e04565]
10
+ - @pikku/core@0.12.2
11
+
12
+ ## 0.12.1
13
+
14
+ ### Patch Changes
15
+
16
+ - 62a8725: Rename 'external' to 'addon' throughout the codebase. All types, functions, config keys, and CLI options previously named `external` or `External` are now named `addon` or `Addon` (e.g. `ExternalPackageConfig` → `AddonConfig`, `externalPackages` → `addons`, `function-external` → `function-addon`).
17
+ - 8eed717: Add `readonly` flag to function config and runtime enforcement. Functions can be marked `readonly: true` in their config. At runtime, if a session has `readonly: true`, only functions marked as readonly can be called — otherwise a `ReadonlySessionError` (403) is thrown.
18
+ - 62a8725: `pikku versions check` now prints rich, human-readable output for all contract version errors instead of raw error codes. Each error type (PKU861–PKU865) shows the function name, separate input/output schema hashes with a `prev → current` arrow, and clear next-step instructions.
19
+
20
+ The version manifest now stores separate `inputHash` and `outputHash` per version entry (backward-compatible — old string-hash manifests still load and validate correctly). `VersionValidateError` gains optional detail fields (`functionKey`, `version`, `previousInputHash`, `currentInputHash`, `previousOutputHash`, `currentOutputHash`, `nextVersion`, `latestVersion`, `expectedNextVersion`) for use by tooling.
21
+
22
+ - 62a8725: Replace config-based addon declarations with the new `wireAddon()` code-based API. Addons are now declared directly in wiring files using `wireAddon({ name, package, rpcEndpoint?, auth?, tags? })` instead of the `addons` field in `pikku.config.json`. The inspector reads these declarations from the TypeScript AST at build time.
23
+ - 62a8725: Add `secretOverrides` and `variableOverrides` support to `wireAddon()`. These optional maps allow an app to remap an addon's secret/variable keys to its own names (e.g. `secretOverrides: { SENDGRID_API_KEY: 'MY_EMAIL_API_KEY' }`). The inspector validates that all override keys exist in the app's own secrets/variables definitions.
24
+ - Updated dependencies [62a8725]
25
+ - Updated dependencies [a3bdb0d]
26
+ - Updated dependencies [e0349ff]
27
+ - Updated dependencies [62a8725]
28
+ - Updated dependencies [e04531f]
29
+ - Updated dependencies [62a8725]
30
+ - Updated dependencies [a83efb8]
31
+ - Updated dependencies [8eed717]
32
+ - Updated dependencies [62a8725]
33
+ - Updated dependencies [62a8725]
34
+ - Updated dependencies [62a8725]
35
+ - @pikku/core@0.12.1
36
+
3
37
  ### New Features
4
38
 
5
39
  - AI agent metadata extraction
@@ -1,2 +1,2 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  export declare const addAIAgent: AddWiring;
@@ -33,7 +33,7 @@ function resolveToolReferences(obj, checker, agentName, logger) {
33
33
  continue;
34
34
  }
35
35
  }
36
- if (calleeName === 'external') {
36
+ if (calleeName === 'addon') {
37
37
  const [firstArg] = element.arguments;
38
38
  if (firstArg && ts.isStringLiteral(firstArg)) {
39
39
  resolved.push(firstArg.text);
@@ -7,6 +7,7 @@ import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
7
7
  import { resolveMiddleware, resolveChannelMiddleware, } from '../utils/middleware.js';
8
8
  import { extractWireNames } from '../utils/post-process.js';
9
9
  import { resolveIdentifier } from '../utils/resolve-identifier.js';
10
+ import { validateAuthSessionless } from '../utils/validate-auth-sessionless.js';
10
11
  /**
11
12
  * Safely get the "initializer" expression of a property-like AST node:
12
13
  * - for `foo: expr`, returns `expr`
@@ -365,6 +366,7 @@ export const addChannel = (logger, node, checker, state, options) => {
365
366
  if (disabled)
366
367
  return;
367
368
  const query = getPropertyValue(obj, 'query');
369
+ const binary = getPropertyValue(obj, 'binary');
368
370
  const connect = getPropertyAssignmentInitializer(obj, 'onConnect', true, checker);
369
371
  const disconnect = getPropertyAssignmentInitializer(obj, 'onDisconnect', true, checker);
370
372
  // default onMessage handler
@@ -416,6 +418,17 @@ export const addChannel = (logger, node, checker, state, options) => {
416
418
  }
417
419
  }
418
420
  extractWireNames(middleware).forEach((name) => state.serviceAggregation.usedMiddleware.add(name));
421
+ // --- validate auth/sessionless ---
422
+ const handlersToValidate = [
423
+ connectFuncId,
424
+ disconnectFuncId,
425
+ message?.pikkuFuncId,
426
+ ].filter(Boolean);
427
+ for (const funcId of handlersToValidate) {
428
+ if (!validateAuthSessionless(logger, obj, state, funcId, `Channel '${name}'`)) {
429
+ return;
430
+ }
431
+ }
419
432
  state.channels.files.add(node.getSourceFile().fileName);
420
433
  state.channels.meta[name] = {
421
434
  name,
@@ -427,6 +440,7 @@ export const addChannel = (logger, node, checker, state, options) => {
427
440
  disconnect: disconnectFuncId ? { pikkuFuncId: disconnectFuncId } : null,
428
441
  message,
429
442
  messageWirings,
443
+ binary: binary === undefined ? undefined : binary,
430
444
  summary,
431
445
  description,
432
446
  errors,
@@ -1,4 +1,4 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  /**
3
3
  * Adds CLI command metadata to the inspector state
4
4
  */
@@ -1,9 +1,10 @@
1
1
  import ts from 'typescript';
2
- import { extractFunctionName } from '../utils/extract-function-name.js';
2
+ import { extractFunctionName, makeContextBasedId, } from '../utils/extract-function-name.js';
3
3
  import { resolveMiddleware } from '../utils/middleware.js';
4
4
  import { extractWireNames } from '../utils/post-process.js';
5
5
  import { getPropertyValue } from '../utils/get-property-value.js';
6
6
  import { resolveIdentifier } from '../utils/resolve-identifier.js';
7
+ import { validateAuthSessionless } from '../utils/validate-auth-sessionless.js';
7
8
  // Track if we've warned about missing Config type to avoid duplicate warnings
8
9
  const configTypeWarningShown = new Set();
9
10
  /**
@@ -94,10 +95,14 @@ function processCLIConfig(logger, node, sourceFile, typeChecker, inspectorState,
94
95
  programMeta.options = processOptions(logger, prop.initializer, typeChecker, inspectorState, options);
95
96
  }
96
97
  break;
97
- case 'render':
98
- // Extract the actual renderer function name
99
- programMeta.defaultRenderName = extractFunctionName(prop.initializer, typeChecker, inspectorState.rootDir).pikkuFuncId;
98
+ case 'render': {
99
+ let renderFuncId = extractFunctionName(prop.initializer, typeChecker, inspectorState.rootDir).pikkuFuncId;
100
+ if (renderFuncId.startsWith('__temp_')) {
101
+ renderFuncId = makeContextBasedId('cli-render', programName);
102
+ }
103
+ programMeta.defaultRenderName = renderFuncId;
100
104
  break;
105
+ }
101
106
  }
102
107
  }
103
108
  return { programName, programMeta };
@@ -155,8 +160,12 @@ function processCommand(logger, inspectorState, options, name, node, sourceFile,
155
160
  if (ts.isIdentifier(node) ||
156
161
  ts.isArrowFunction(node) ||
157
162
  ts.isFunctionExpression(node)) {
163
+ let pikkuFuncId = extractFunctionName(node, typeChecker, inspectorState.rootDir).pikkuFuncId;
164
+ if (pikkuFuncId.startsWith('__temp_')) {
165
+ pikkuFuncId = makeContextBasedId('cli', programName, ...fullPath);
166
+ }
158
167
  return {
159
- pikkuFuncId: extractFunctionName(node, typeChecker, inspectorState.rootDir).pikkuFuncId,
168
+ pikkuFuncId,
160
169
  positionals: [],
161
170
  options: {},
162
171
  };
@@ -194,6 +203,9 @@ function processCommand(logger, inspectorState, options, name, node, sourceFile,
194
203
  const propName = prop.name.text;
195
204
  if (propName === 'func') {
196
205
  pikkuFuncId = extractFunctionName(prop.initializer, typeChecker, inspectorState.rootDir).pikkuFuncId;
206
+ if (pikkuFuncId.startsWith('__temp_')) {
207
+ pikkuFuncId = makeContextBasedId('cli', programName, ...fullPath);
208
+ }
197
209
  meta.pikkuFuncId = pikkuFuncId;
198
210
  }
199
211
  else if (propName === 'options' &&
@@ -237,9 +249,14 @@ function processCommand(logger, inspectorState, options, name, node, sourceFile,
237
249
  case 'func':
238
250
  // Already handled in first pass
239
251
  break;
240
- case 'render':
241
- meta.renderName = extractFunctionName(prop.initializer, typeChecker, inspectorState.rootDir).pikkuFuncId;
252
+ case 'render': {
253
+ let renderFuncId = extractFunctionName(prop.initializer, typeChecker, inspectorState.rootDir).pikkuFuncId;
254
+ if (renderFuncId.startsWith('__temp_')) {
255
+ renderFuncId = makeContextBasedId('cli-render', programName, ...fullPath);
256
+ }
257
+ meta.renderName = renderFuncId;
242
258
  break;
259
+ }
243
260
  case 'options':
244
261
  // Process with pikkuFuncId from first pass
245
262
  if (optionsNode) {
@@ -270,10 +287,14 @@ function processCommand(logger, inspectorState, options, name, node, sourceFile,
270
287
  break;
271
288
  }
272
289
  }
290
+ // --- validate auth/sessionless ---
291
+ if (meta.pikkuFuncId &&
292
+ !validateAuthSessionless(logger, node, inspectorState, meta.pikkuFuncId, `CLI command '${name}'`)) {
293
+ return null;
294
+ }
273
295
  // --- track used functions/middleware for service aggregation ---
274
296
  inspectorState.serviceAggregation.usedFunctions.add(meta.pikkuFuncId);
275
297
  extractWireNames(meta.middleware).forEach((name) => inspectorState.serviceAggregation.usedMiddleware.add(name));
276
- // Note: subcommands are tracked recursively when they're processed
277
298
  return meta;
278
299
  }
279
300
  /**
@@ -1,3 +1,3 @@
1
1
  import * as ts from 'typescript';
2
- import { PathToNameAndType, InspectorState } from '../types.js';
2
+ import type { PathToNameAndType, InspectorState } from '../types.js';
3
3
  export declare const addFileExtendsCoreType: (node: ts.Node, checker: ts.TypeChecker, methods: PathToNameAndType, expectedTypeName: string, state?: InspectorState) => void;
@@ -1,3 +1,3 @@
1
1
  import * as ts from 'typescript';
2
- import { PathToNameAndType } from '../types.js';
2
+ import type { PathToNameAndType } from '../types.js';
3
3
  export declare const addFileWithConfig: (node: ts.Node, checker: ts.TypeChecker, configs: PathToNameAndType) => void;
@@ -1,3 +1,3 @@
1
1
  import * as ts from 'typescript';
2
- import { PathToNameAndType, InspectorState } from '../types.js';
2
+ import type { PathToNameAndType, InspectorState } from '../types.js';
3
3
  export declare const addFileWithFactory: (node: ts.Node, checker: ts.TypeChecker, methods: PathToNameAndType | undefined, expectedTypeName: string, state?: InspectorState) => void;
@@ -3,9 +3,9 @@ import { extractServicesFromFunction } from '../utils/extract-services.js';
3
3
  // Mapping of wrapper function names to their corresponding types
4
4
  const wrapperFunctionMap = {
5
5
  pikkuConfig: 'CreateConfig',
6
- pikkuExternalConfig: 'CreateConfig',
6
+ pikkuAddonConfig: 'CreateConfig',
7
7
  pikkuServices: 'CreateSingletonServices',
8
- pikkuExternalServices: 'CreateSingletonServices',
8
+ pikkuAddonServices: 'CreateSingletonServices',
9
9
  pikkuWireServices: 'CreateWireServices',
10
10
  };
11
11
  export const addFileWithFactory = (node, checker, methods = new Map(), expectedTypeName, state) => {
@@ -1,4 +1,4 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  /**
3
3
  * Inspect pikkuFunc calls, extract input/output and first-arg destructuring,
4
4
  * then push into state.functions.meta.
@@ -154,9 +154,7 @@ const getNamesAndTypes = (checker, typesMap, direction, funcName, type) => {
154
154
  const aliasType = rawNames.join(' | ');
155
155
  const aliasName = funcIdToTypeName(funcName) + direction;
156
156
  // record the alias in your TypesMap
157
- const references = rawTypes
158
- .map((t) => resolveTypeImports(t, typesMap, true, checker))
159
- .flat();
157
+ const references = rawTypes.flatMap((t) => resolveTypeImports(t, typesMap, true, checker));
160
158
  typesMap.addCustomType(aliasName, aliasType, references);
161
159
  return {
162
160
  names: [aliasName],
@@ -164,8 +162,7 @@ const getNamesAndTypes = (checker, typesMap, direction, funcName, type) => {
164
162
  };
165
163
  }
166
164
  // 4) Single, valid name → inline it
167
- const uniqueNames = rawNames
168
- .map((name, i) => {
165
+ const uniqueNames = rawNames.flatMap((name, i) => {
169
166
  const t = rawTypes[i];
170
167
  if (!t) {
171
168
  throw new Error(`Expected type for name "${name}" in ${funcName}`);
@@ -175,8 +172,7 @@ const getNamesAndTypes = (checker, typesMap, direction, funcName, type) => {
175
172
  }
176
173
  // non-primitive: import/alias it inline
177
174
  return resolveTypeImports(t, typesMap, false, checker);
178
- })
179
- .flat();
175
+ });
180
176
  return {
181
177
  names: uniqueNames,
182
178
  types: rawTypes,
@@ -248,6 +244,7 @@ export const addFunctions = (logger, node, checker, state, options) => {
248
244
  let expose;
249
245
  let remote;
250
246
  let mcp;
247
+ let readonly_;
251
248
  let requiresApproval;
252
249
  let version;
253
250
  let objectNode;
@@ -313,6 +310,7 @@ export const addFunctions = (logger, node, checker, state, options) => {
313
310
  expose = getPropertyValue(firstArg, 'expose');
314
311
  remote = getPropertyValue(firstArg, 'remote');
315
312
  mcp = getPropertyValue(firstArg, 'mcp');
313
+ readonly_ = getPropertyValue(firstArg, 'readonly');
316
314
  requiresApproval = getPropertyValue(firstArg, 'requiresApproval');
317
315
  const versionRaw = getPropertyValue(firstArg, 'version');
318
316
  if (versionRaw !== null && versionRaw !== undefined) {
@@ -566,6 +564,7 @@ export const addFunctions = (logger, node, checker, state, options) => {
566
564
  expose: expose || undefined,
567
565
  remote: remote || undefined,
568
566
  mcp: mcpEnabled || undefined,
567
+ readonly: readonly_ || undefined,
569
568
  requiresApproval: requiresApproval || undefined,
570
569
  version,
571
570
  title,
@@ -0,0 +1,2 @@
1
+ import type { AddWiring } from '../types.js';
2
+ export declare const addGateway: AddWiring;
@@ -0,0 +1,57 @@
1
+ import * as ts from 'typescript';
2
+ import { getPropertyValue, getCommonWireMetaData, } from '../utils/get-property-value.js';
3
+ import { extractFunctionName, makeContextBasedId, } from '../utils/extract-function-name.js';
4
+ import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
5
+ import { resolveMiddleware } from '../utils/middleware.js';
6
+ import { extractWireNames } from '../utils/post-process.js';
7
+ import { ErrorCode } from '../error-codes.js';
8
+ export const addGateway = (logger, node, checker, state, _options) => {
9
+ if (!ts.isCallExpression(node)) {
10
+ return;
11
+ }
12
+ const args = node.arguments;
13
+ const firstArg = args[0];
14
+ const expression = node.expression;
15
+ if (!ts.isIdentifier(expression) || expression.text !== 'wireGateway') {
16
+ return;
17
+ }
18
+ if (!firstArg || !ts.isObjectLiteralExpression(firstArg)) {
19
+ return;
20
+ }
21
+ const obj = firstArg;
22
+ const nameValue = getPropertyValue(obj, 'name');
23
+ const typeValue = getPropertyValue(obj, 'type');
24
+ const routeValue = getPropertyValue(obj, 'route');
25
+ const { disabled, tags, summary, description, errors } = getCommonWireMetaData(obj, 'Gateway', nameValue, logger);
26
+ if (disabled)
27
+ return;
28
+ const funcInitializer = getPropertyAssignmentInitializer(obj, 'func', true, checker);
29
+ if (!funcInitializer) {
30
+ logger.critical(ErrorCode.MISSING_FUNC, `No valid 'func' property for gateway '${nameValue}'.`);
31
+ return;
32
+ }
33
+ const extracted = extractFunctionName(funcInitializer, checker, state.rootDir);
34
+ let pikkuFuncId = extracted.pikkuFuncId;
35
+ if (pikkuFuncId.startsWith('__temp_') && nameValue) {
36
+ pikkuFuncId = makeContextBasedId('gateway', nameValue);
37
+ }
38
+ if (!nameValue || !typeValue) {
39
+ return;
40
+ }
41
+ const middleware = resolveMiddleware(state, obj, tags, checker);
42
+ state.serviceAggregation.usedFunctions.add(pikkuFuncId);
43
+ extractWireNames(middleware).forEach((name) => state.serviceAggregation.usedMiddleware.add(name));
44
+ state.gateways.files.add(node.getSourceFile().fileName);
45
+ state.gateways.meta[nameValue] = {
46
+ pikkuFuncId,
47
+ name: nameValue,
48
+ type: typeValue,
49
+ route: routeValue,
50
+ gateway: true,
51
+ summary,
52
+ description,
53
+ errors,
54
+ tags,
55
+ middleware,
56
+ };
57
+ };
@@ -1,5 +1,5 @@
1
1
  import * as ts from 'typescript';
2
- import { AddWiring, InspectorState } from '../types.js';
2
+ import type { AddWiring, InspectorState } from '../types.js';
3
3
  import type { InspectorLogger } from '../types.js';
4
4
  /**
5
5
  * Parameters for registering an HTTP route
@@ -12,12 +12,13 @@ export interface RegisterHTTPRouteParams {
12
12
  sourceFile: ts.SourceFile;
13
13
  basePath?: string;
14
14
  inheritedTags?: string[];
15
+ inheritedAuth?: boolean;
15
16
  }
16
17
  /**
17
18
  * Shared function to register an HTTP route in the inspector state.
18
19
  * Used by both wireHTTP and wireHTTPRoutes.
19
20
  */
20
- export declare function registerHTTPRoute({ obj, state, checker, logger, sourceFile, basePath, inheritedTags, }: RegisterHTTPRouteParams): void;
21
+ export declare function registerHTTPRoute({ obj, state, checker, logger, sourceFile, basePath, inheritedTags, inheritedAuth, }: RegisterHTTPRouteParams): void;
21
22
  /**
22
23
  * Process wireHTTP calls
23
24
  */
@@ -8,6 +8,7 @@ import { resolveHTTPPermissionsFromObject } from '../utils/permissions.js';
8
8
  import { extractWireNames } from '../utils/post-process.js';
9
9
  import { ensureFunctionMetadata } from '../utils/ensure-function-metadata.js';
10
10
  import { ErrorCode } from '../error-codes.js';
11
+ import { validateAuthSessionless } from '../utils/validate-auth-sessionless.js';
11
12
  import { detectSchemaVendorOrError } from '../utils/detect-schema-vendor.js';
12
13
  /**
13
14
  * Extract header schema reference from headers property
@@ -76,7 +77,7 @@ const computeInputTypes = (metaTypes, methodType, inputType, queryValues, params
76
77
  * Shared function to register an HTTP route in the inspector state.
77
78
  * Used by both wireHTTP and wireHTTPRoutes.
78
79
  */
79
- export function registerHTTPRoute({ obj, state, checker, logger, sourceFile, basePath = '', inheritedTags = [], }) {
80
+ export function registerHTTPRoute({ obj, state, checker, logger, sourceFile, basePath = '', inheritedTags = [], inheritedAuth, }) {
80
81
  // Extract route path
81
82
  const routePath = getPropertyValue(obj, 'route');
82
83
  if (!routePath)
@@ -118,6 +119,9 @@ export function registerHTTPRoute({ obj, state, checker, logger, sourceFile, bas
118
119
  logger.critical(ErrorCode.FUNCTION_METADATA_NOT_FOUND, `No function metadata found for '${funcName}'.`);
119
120
  return;
120
121
  }
122
+ if (!validateAuthSessionless(logger, obj, state, funcName, `Route '${fullRoute}'`, inheritedAuth)) {
123
+ return;
124
+ }
121
125
  const input = fnMeta.inputs?.[0] || null;
122
126
  // Validate that route params and query params exist in function input type
123
127
  if (params.length > 0 || query.length > 0) {
@@ -1,4 +1,4 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  /**
3
3
  * Process wireHTTPRoutes calls
4
4
  */
@@ -155,5 +155,6 @@ function processRoute(obj, groupConfig, state, checker, logger, sourceFile) {
155
155
  sourceFile,
156
156
  basePath: groupConfig.basePath,
157
157
  inheritedTags: groupConfig.tags,
158
+ inheritedAuth: groupConfig.auth,
158
159
  });
159
160
  }
@@ -1,4 +1,4 @@
1
- import { AddWiring, InspectorState } from '../types.js';
1
+ import type { AddWiring, InspectorState } from '../types.js';
2
2
  export interface KeyedWiringConfig {
3
3
  functionName: string;
4
4
  idField: string;
@@ -1,2 +1,2 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  export declare const addMCPPrompt: AddWiring;
@@ -1,2 +1,2 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  export declare const addMCPResource: AddWiring;
@@ -1,2 +1,2 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  export declare const addQueueWorker: AddWiring;
@@ -1,9 +1,9 @@
1
1
  import * as ts from 'typescript';
2
- import { InspectorState, InspectorLogger } from '../types.js';
2
+ import type { InspectorState, InspectorLogger } from '../types.js';
3
3
  /**
4
4
  * Scan for rpc.invoke() calls to track which functions are actually being invoked
5
- * Also detects external package usage via:
5
+ * Also detects addon usage via:
6
6
  * - Namespaced calls: rpc.invoke('namespace:function')
7
- * - External helper: external('namespace:function')
7
+ * - Addon helper: addon('namespace:function')
8
8
  */
9
9
  export declare function addRPCInvocations(node: ts.Node, state: InspectorState, logger: InspectorLogger): void;
@@ -11,25 +11,25 @@ function extractNamespace(functionRef) {
11
11
  }
12
12
  /**
13
13
  * Scan for rpc.invoke() calls to track which functions are actually being invoked
14
- * Also detects external package usage via:
14
+ * Also detects addon usage via:
15
15
  * - Namespaced calls: rpc.invoke('namespace:function')
16
- * - External helper: external('namespace:function')
16
+ * - Addon helper: addon('namespace:function')
17
17
  */
18
18
  export function addRPCInvocations(node, state, logger) {
19
- // Look for call expressions: external('ext:hello') or rpc.invoke('...')
19
+ // Look for call expressions: addon('ext:hello') or rpc.invoke('...')
20
20
  if (ts.isCallExpression(node)) {
21
21
  const { expression, arguments: args } = node;
22
- // Check for external('namespace:function') calls
23
- if (ts.isIdentifier(expression) && expression.text === 'external') {
22
+ // Check for addon('namespace:function') calls
23
+ if (ts.isIdentifier(expression) && expression.text === 'addon') {
24
24
  const [firstArg] = args;
25
25
  if (firstArg && ts.isStringLiteral(firstArg)) {
26
26
  const functionRef = firstArg.text;
27
- logger.debug(`• Found external() call: ${functionRef}`);
27
+ logger.debug(`• Found addon() call: ${functionRef}`);
28
28
  state.rpc.invokedFunctions.add(functionRef);
29
29
  const namespace = extractNamespace(functionRef);
30
30
  if (namespace) {
31
- logger.debug(` → External package detected: ${namespace}`);
32
- state.rpc.usedExternalPackages.add(namespace);
31
+ logger.debug(` → Addon detected: ${namespace}`);
32
+ state.rpc.usedAddons.add(namespace);
33
33
  }
34
34
  }
35
35
  }
@@ -60,8 +60,8 @@ export function addRPCInvocations(node, state, logger) {
60
60
  state.rpc.invokedFunctions.add(functionRef);
61
61
  const namespace = extractNamespace(functionRef);
62
62
  if (namespace) {
63
- logger.debug(` → External package detected: ${namespace}`);
64
- state.rpc.usedExternalPackages.add(namespace);
63
+ logger.debug(` → Addon detected: ${namespace}`);
64
+ state.rpc.usedAddons.add(namespace);
65
65
  }
66
66
  }
67
67
  // Handle template literals like `function-${name}`
@@ -1,2 +1,2 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  export declare const addSchedule: AddWiring;
@@ -1,3 +1,3 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  export declare const addSecret: AddWiring;
3
3
  export declare const addOAuth2Credential: AddWiring;
@@ -1,2 +1,2 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  export declare const addTrigger: AddWiring;
@@ -4,7 +4,7 @@ import { extractFunctionName, makeContextBasedId, } from '../utils/extract-funct
4
4
  import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
5
5
  import { resolveMiddleware } from '../utils/middleware.js';
6
6
  import { extractWireNames } from '../utils/post-process.js';
7
- import { resolveExternalPackageName } from '../utils/resolve-external-package.js';
7
+ import { resolveAddonName } from '../utils/resolve-addon-package.js';
8
8
  import { ErrorCode } from '../error-codes.js';
9
9
  export const addTrigger = (logger, node, checker, state, options) => {
10
10
  if (!ts.isCallExpression(node)) {
@@ -76,7 +76,7 @@ const addWireTriggerSource = (logger, node, checker, state, options, firstArg) =
76
76
  return;
77
77
  }
78
78
  if (ts.isIdentifier(funcInitializer)) {
79
- const packageName = resolveExternalPackageName(funcInitializer, checker, options.externalPackages);
79
+ const packageName = resolveAddonName(funcInitializer, checker, state.rpc.wireAddonDeclarations);
80
80
  state.triggers.sourceMeta[nameValue] = {
81
81
  name: nameValue,
82
82
  pikkuFuncId: funcInitializer.text,
@@ -0,0 +1,7 @@
1
+ import * as ts from 'typescript';
2
+ import type { InspectorState, InspectorLogger } from '../types.js';
3
+ /**
4
+ * Detect wireAddon({ name: '...', package: '...' }) call expressions and
5
+ * populate state.rpc.wireAddonDeclarations and state.rpc.usedAddons.
6
+ */
7
+ export declare function addWireAddon(node: ts.Node, state: InspectorState, logger: InspectorLogger): void;
@@ -0,0 +1,70 @@
1
+ import * as ts from 'typescript';
2
+ function parseStringRecord(obj) {
3
+ const result = {};
4
+ for (const prop of obj.properties) {
5
+ if (!ts.isPropertyAssignment(prop))
6
+ continue;
7
+ const keyNode = prop.name;
8
+ const key = ts.isIdentifier(keyNode)
9
+ ? keyNode.text
10
+ : ts.isStringLiteral(keyNode)
11
+ ? keyNode.text
12
+ : undefined;
13
+ if (key && ts.isStringLiteral(prop.initializer)) {
14
+ result[key] = prop.initializer.text;
15
+ }
16
+ }
17
+ return result;
18
+ }
19
+ /**
20
+ * Detect wireAddon({ name: '...', package: '...' }) call expressions and
21
+ * populate state.rpc.wireAddonDeclarations and state.rpc.usedAddons.
22
+ */
23
+ export function addWireAddon(node, state, logger) {
24
+ if (!ts.isCallExpression(node))
25
+ return;
26
+ const { expression, arguments: args } = node;
27
+ if (!ts.isIdentifier(expression) || expression.text !== 'wireAddon')
28
+ return;
29
+ const [firstArg] = args;
30
+ if (!firstArg || !ts.isObjectLiteralExpression(firstArg))
31
+ return;
32
+ let name;
33
+ let pkg;
34
+ let rpcEndpoint;
35
+ let secretOverrides;
36
+ let variableOverrides;
37
+ for (const prop of firstArg.properties) {
38
+ if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name))
39
+ continue;
40
+ const key = prop.name.text;
41
+ if (key === 'name' && ts.isStringLiteral(prop.initializer)) {
42
+ name = prop.initializer.text;
43
+ }
44
+ else if (key === 'package' && ts.isStringLiteral(prop.initializer)) {
45
+ pkg = prop.initializer.text;
46
+ }
47
+ else if (key === 'rpcEndpoint' && ts.isStringLiteral(prop.initializer)) {
48
+ rpcEndpoint = prop.initializer.text;
49
+ }
50
+ else if (key === 'secretOverrides' &&
51
+ ts.isObjectLiteralExpression(prop.initializer)) {
52
+ secretOverrides = parseStringRecord(prop.initializer);
53
+ }
54
+ else if (key === 'variableOverrides' &&
55
+ ts.isObjectLiteralExpression(prop.initializer)) {
56
+ variableOverrides = parseStringRecord(prop.initializer);
57
+ }
58
+ }
59
+ if (!name || !pkg)
60
+ return;
61
+ logger.debug(`• Found wireAddon: ${name} → ${pkg}`);
62
+ state.rpc.wireAddonDeclarations.set(name, {
63
+ package: pkg,
64
+ rpcEndpoint,
65
+ secretOverrides,
66
+ variableOverrides,
67
+ });
68
+ state.rpc.usedAddons.add(name);
69
+ state.rpc.wireAddonFiles.add(node.getSourceFile().fileName);
70
+ }
@@ -1,4 +1,4 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  /**
3
3
  * Inspector for pikkuWorkflow() and pikkuSimpleWorkflow() calls
4
4
  * Detects workflow registration and extracts metadata
@@ -31,6 +31,7 @@ export declare enum ErrorCode {
31
31
  HANDLER_NOT_RESOLVED = "PKU568",
32
32
  ROUTE_PARAM_MISMATCH = "PKU571",
33
33
  ROUTE_QUERY_MISMATCH = "PKU572",
34
+ AUTH_DISABLED_REQUIRES_SESSIONLESS = "PKU573",
34
35
  MIDDLEWARE_HANDLER_INVALID = "PKU685",
35
36
  MIDDLEWARE_TAG_INVALID = "PKU715",
36
37
  MIDDLEWARE_EMPTY_ARRAY = "PKU736",
@@ -36,6 +36,7 @@ export var ErrorCode;
36
36
  // HTTP Route errors
37
37
  ErrorCode["ROUTE_PARAM_MISMATCH"] = "PKU571";
38
38
  ErrorCode["ROUTE_QUERY_MISMATCH"] = "PKU572";
39
+ ErrorCode["AUTH_DISABLED_REQUIRES_SESSIONLESS"] = "PKU573";
39
40
  // Middleware/Permission errors
40
41
  ErrorCode["MIDDLEWARE_HANDLER_INVALID"] = "PKU685";
41
42
  ErrorCode["MIDDLEWARE_TAG_INVALID"] = "PKU715";