@pikku/inspector 0.12.41 → 0.12.43

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 (108) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/dist/add/add-addon-bans.js +1 -0
  3. package/dist/add/add-ai-agent.d.ts +3 -1
  4. package/dist/add/add-ai-agent.js +82 -0
  5. package/dist/add/add-credential.js +3 -1
  6. package/dist/add/add-functions.js +10 -9
  7. package/dist/add/add-gateway.js +3 -0
  8. package/dist/add/add-http-route.js +2 -2
  9. package/dist/add/add-mcp-prompt.js +0 -1
  10. package/dist/add/add-mcp-resource.js +0 -1
  11. package/dist/add/add-permission.d.ts +1 -1
  12. package/dist/add/add-permission.js +3 -177
  13. package/dist/add/add-queue-worker.js +3 -0
  14. package/dist/add/add-schedule.js +3 -0
  15. package/dist/add/add-scope.d.ts +2 -0
  16. package/dist/add/add-scope.js +146 -0
  17. package/dist/add/add-trigger.js +3 -0
  18. package/dist/add/add-wire-remote-addon.d.ts +10 -0
  19. package/dist/add/add-wire-remote-addon.js +66 -0
  20. package/dist/add/add-workflow-graph.js +32 -3
  21. package/dist/error-codes.d.ts +3 -0
  22. package/dist/error-codes.js +4 -0
  23. package/dist/inspector.js +19 -7
  24. package/dist/types.d.ts +19 -13
  25. package/dist/utils/custom-types-generator.js +2 -1
  26. package/dist/utils/ensure-function-metadata.d.ts +15 -0
  27. package/dist/utils/ensure-function-metadata.js +24 -0
  28. package/dist/utils/filter-inspector-state.js +9 -0
  29. package/dist/utils/get-property-value.d.ts +7 -2
  30. package/dist/utils/get-property-value.js +48 -1
  31. package/dist/utils/load-addon-functions-meta.js +81 -6
  32. package/dist/utils/nested-project-filter.d.ts +1 -0
  33. package/dist/utils/nested-project-filter.js +29 -0
  34. package/dist/utils/permissions.d.ts +1 -21
  35. package/dist/utils/permissions.js +17 -108
  36. package/dist/utils/post-process.d.ts +30 -1
  37. package/dist/utils/post-process.js +150 -72
  38. package/dist/utils/serialize-inspector-state.d.ts +13 -8
  39. package/dist/utils/serialize-inspector-state.js +12 -6
  40. package/dist/utils/serialize-permissions-groups-meta.d.ts +0 -2
  41. package/dist/utils/serialize-permissions-groups-meta.js +0 -26
  42. package/dist/utils/workflow/derive-workflow-plan.js +9 -3
  43. package/dist/utils/workflow/dsl/extract-dsl-workflow.js +56 -1
  44. package/dist/utils/workflow/dsl/patterns.d.ts +10 -0
  45. package/dist/utils/workflow/dsl/patterns.js +24 -8
  46. package/dist/utils/workflow/dsl/validation.js +3 -4
  47. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +14 -0
  48. package/dist/utils/workflow/graph/finalize-workflows.d.ts +7 -0
  49. package/dist/utils/workflow/graph/finalize-workflows.js +11 -2
  50. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +2 -0
  51. package/dist/utils/workflow/graph/serialize-workflow-graph.js +4 -0
  52. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +6 -2
  53. package/dist/visit.js +4 -0
  54. package/package.json +3 -3
  55. package/src/add/add-addon-bans.ts +1 -0
  56. package/src/add/add-ai-agent.test.ts +87 -0
  57. package/src/add/add-ai-agent.ts +122 -0
  58. package/src/add/add-credential.ts +6 -0
  59. package/src/add/add-functions.ts +10 -12
  60. package/src/add/add-gateway.ts +11 -0
  61. package/src/add/add-http-route.ts +2 -8
  62. package/src/add/add-mcp-prompt.ts +0 -1
  63. package/src/add/add-mcp-resource.ts +0 -1
  64. package/src/add/add-permission.ts +4 -242
  65. package/src/add/add-queue-worker.ts +11 -0
  66. package/src/add/add-schedule.ts +11 -0
  67. package/src/add/add-scope.test.ts +346 -0
  68. package/src/add/add-scope.ts +225 -0
  69. package/src/add/add-trigger.ts +11 -0
  70. package/src/add/add-wire-remote-addon.ts +77 -0
  71. package/src/add/add-workflow-graph-input.test.ts +94 -0
  72. package/src/add/add-workflow-graph-notes.test.ts +74 -0
  73. package/src/add/add-workflow-graph.ts +35 -3
  74. package/src/add/inline-wiring-function.test.ts +104 -0
  75. package/src/add/validate-workflow-graph-addons.test.ts +102 -0
  76. package/src/error-codes.ts +5 -0
  77. package/src/inspector.ts +22 -6
  78. package/src/types.ts +16 -17
  79. package/src/utils/custom-types-generator.test.ts +26 -1
  80. package/src/utils/custom-types-generator.ts +2 -1
  81. package/src/utils/ensure-function-metadata.ts +42 -0
  82. package/src/utils/filter-inspector-state.test.ts +0 -2
  83. package/src/utils/filter-inspector-state.ts +9 -0
  84. package/src/utils/get-property-value.test.ts +141 -0
  85. package/src/utils/get-property-value.ts +62 -1
  86. package/src/utils/load-addon-functions-meta.test.ts +157 -0
  87. package/src/utils/load-addon-functions-meta.ts +94 -6
  88. package/src/utils/load-addon-scopes.test.ts +138 -0
  89. package/src/utils/nested-project-filter.test.ts +53 -0
  90. package/src/utils/nested-project-filter.ts +28 -0
  91. package/src/utils/permissions.test.ts +7 -232
  92. package/src/utils/permissions.ts +20 -160
  93. package/src/utils/post-process.test.ts +269 -4
  94. package/src/utils/post-process.ts +216 -95
  95. package/src/utils/serialize-inspector-state.ts +22 -25
  96. package/src/utils/serialize-permissions-groups-meta.ts +0 -28
  97. package/src/utils/workflow/derive-workflow-plan.test.ts +41 -3
  98. package/src/utils/workflow/derive-workflow-plan.ts +11 -4
  99. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +66 -0
  100. package/src/utils/workflow/dsl/patterns.ts +34 -8
  101. package/src/utils/workflow/dsl/validation.ts +3 -4
  102. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +15 -0
  103. package/src/utils/workflow/graph/finalize-workflows.test.ts +50 -0
  104. package/src/utils/workflow/graph/finalize-workflows.ts +13 -2
  105. package/src/utils/workflow/graph/serialize-workflow-graph.ts +6 -0
  106. package/src/utils/workflow/graph/workflow-graph.types.ts +8 -0
  107. package/src/visit.ts +4 -0
  108. package/tsconfig.tsbuildinfo +1 -1
@@ -1,7 +1,61 @@
1
1
  import { strict as assert } from 'assert'
2
2
  import { describe, test } from 'node:test'
3
- import { aggregateRequiredServices } from './post-process.js'
4
- import type { InspectorState } from '../types.js'
3
+ import { mkdtempSync, writeFileSync, rmSync } from 'node:fs'
4
+ import { tmpdir } from 'node:os'
5
+ import { join } from 'node:path'
6
+ import {
7
+ aggregateRequiredServices,
8
+ validateSecretOverrides,
9
+ validateCredentialOverrides,
10
+ validateVariableOverrides,
11
+ validateRemoteAddonDependencies,
12
+ validateRemoteAddonAuth,
13
+ } from './post-process.js'
14
+ import { ErrorCode } from '../error-codes.js'
15
+ import type { InspectorState, InspectorLogger } from '../types.js'
16
+
17
+ const makeCriticalLogger = () => {
18
+ const criticals: { code: string; message: string }[] = []
19
+ const logger = {
20
+ debug: () => {},
21
+ info: () => {},
22
+ warn: () => {},
23
+ error: () => {},
24
+ critical: (code: any, message: string) =>
25
+ criticals.push({ code: String(code), message }),
26
+ diagnostic: () => {},
27
+ } as unknown as InspectorLogger
28
+ return { logger, criticals }
29
+ }
30
+
31
+ const makeOverrideState = (
32
+ kind: 'secrets' | 'credentials' | 'variables',
33
+ declaredNames: string[],
34
+ overrides: Record<string, string>,
35
+ overrideKey: 'secretOverrides' | 'credentialOverrides' | 'variableOverrides'
36
+ ): Omit<InspectorState, 'typesLookup'> =>
37
+ ({
38
+ rpc: {
39
+ wireAddonDeclarations: new Map([
40
+ [
41
+ 'slack-marketing',
42
+ { package: '@addon/slack', [overrideKey]: overrides },
43
+ ],
44
+ ]),
45
+ },
46
+ secrets: {
47
+ definitions:
48
+ kind === 'secrets' ? declaredNames.map((name) => ({ name })) : [],
49
+ },
50
+ credentials: {
51
+ definitions:
52
+ kind === 'credentials' ? declaredNames.map((name) => ({ name })) : [],
53
+ },
54
+ variables: {
55
+ definitions:
56
+ kind === 'variables' ? declaredNames.map((name) => ({ name })) : [],
57
+ },
58
+ }) as unknown as Omit<InspectorState, 'typesLookup'>
5
59
 
6
60
  function makeState(
7
61
  overrides: {
@@ -22,7 +76,7 @@ function makeState(
22
76
  },
23
77
  functions: { meta: overrides.functionsMeta ?? {} },
24
78
  middleware: { definitions: {}, tagMiddleware: new Map() },
25
- permissions: { definitions: {}, tagPermissions: new Map() },
79
+ permissions: { definitions: {} },
26
80
  http: {
27
81
  meta: {
28
82
  get: {},
@@ -34,7 +88,6 @@ function makeState(
34
88
  options: {},
35
89
  },
36
90
  routeMiddleware: new Map(),
37
- routePermissions: new Map(),
38
91
  },
39
92
  channels: { meta: {} },
40
93
  queueWorkers: { meta: {} },
@@ -252,3 +305,215 @@ describe('aggregateRequiredServices — per-function addon services', () => {
252
305
  assert.ok(!required.has('credentialService'))
253
306
  })
254
307
  })
308
+
309
+ describe('override validation resolves the override target (value), not the logical key', () => {
310
+ test('validateSecretOverrides accepts an override whose value is a declared secret', () => {
311
+ const { logger, criticals } = makeCriticalLogger()
312
+ const state = makeOverrideState(
313
+ 'secrets',
314
+ ['slack_marketing_secret'],
315
+ { slack: 'slack_marketing_secret' },
316
+ 'secretOverrides'
317
+ )
318
+ validateSecretOverrides(logger, state)
319
+ assert.deepEqual(criticals, [])
320
+ })
321
+
322
+ test('validateSecretOverrides flags an override whose value is not declared', () => {
323
+ const { logger, criticals } = makeCriticalLogger()
324
+ const state = makeOverrideState(
325
+ 'secrets',
326
+ ['slack_marketing_secret'],
327
+ { slack: 'ghost_secret' },
328
+ 'secretOverrides'
329
+ )
330
+ validateSecretOverrides(logger, state)
331
+ assert.equal(criticals.length, 1)
332
+ assert.match(criticals[0]!.message, /ghost_secret/)
333
+ })
334
+
335
+ test('validateCredentialOverrides accepts an override whose value is a declared credential', () => {
336
+ const { logger, criticals } = makeCriticalLogger()
337
+ const state = makeOverrideState(
338
+ 'credentials',
339
+ ['marketing_cred'],
340
+ { slack: 'marketing_cred' },
341
+ 'credentialOverrides'
342
+ )
343
+ validateCredentialOverrides(logger, state)
344
+ assert.deepEqual(criticals, [])
345
+ })
346
+
347
+ test('validateVariableOverrides accepts an override whose value is a declared variable', () => {
348
+ const { logger, criticals } = makeCriticalLogger()
349
+ const state = makeOverrideState(
350
+ 'variables',
351
+ ['marketing_region'],
352
+ { region: 'marketing_region' },
353
+ 'variableOverrides'
354
+ )
355
+ validateVariableOverrides(logger, state)
356
+ assert.deepEqual(criticals, [])
357
+ })
358
+ })
359
+
360
+ const makeRemoteState = (
361
+ rootDir: string,
362
+ decl: {
363
+ package: string
364
+ remote?: boolean
365
+ authCredentialId?: string
366
+ authSecretId?: string
367
+ },
368
+ declared: { credentials?: string[]; secrets?: string[] } = {}
369
+ ): Omit<InspectorState, 'typesLookup'> =>
370
+ ({
371
+ rootDir,
372
+ rpc: {
373
+ wireAddonDeclarations: new Map([['registry', decl]]),
374
+ },
375
+ credentials: {
376
+ definitions: (declared.credentials ?? []).map((name) => ({ name })),
377
+ },
378
+ secrets: {
379
+ definitions: (declared.secrets ?? []).map((name) => ({ name })),
380
+ },
381
+ }) as unknown as Omit<InspectorState, 'typesLookup'>
382
+
383
+ describe('validateRemoteAddonDependencies (wireRemoteAddon must be a devDependency)', () => {
384
+ const writePkg = (
385
+ deps: Record<string, string>,
386
+ devDeps: Record<string, string>
387
+ ): string => {
388
+ const dir = mkdtempSync(join(tmpdir(), 'pikku-remote-dep-'))
389
+ writeFileSync(
390
+ join(dir, 'package.json'),
391
+ JSON.stringify({ dependencies: deps, devDependencies: devDeps })
392
+ )
393
+ return dir
394
+ }
395
+
396
+ test('passes when the remote addon is in devDependencies', () => {
397
+ const { logger, criticals } = makeCriticalLogger()
398
+ const dir = writePkg({}, { '@pikkufabric/addon-registry': '1.0.0' })
399
+ try {
400
+ validateRemoteAddonDependencies(
401
+ logger,
402
+ makeRemoteState(dir, {
403
+ package: '@pikkufabric/addon-registry',
404
+ remote: true,
405
+ })
406
+ )
407
+ assert.deepEqual(criticals, [])
408
+ } finally {
409
+ rmSync(dir, { recursive: true, force: true })
410
+ }
411
+ })
412
+
413
+ test('errors when the remote addon is a production dependency', () => {
414
+ const { logger, criticals } = makeCriticalLogger()
415
+ const dir = writePkg({ '@pikkufabric/addon-registry': '1.0.0' }, {})
416
+ try {
417
+ validateRemoteAddonDependencies(
418
+ logger,
419
+ makeRemoteState(dir, {
420
+ package: '@pikkufabric/addon-registry',
421
+ remote: true,
422
+ })
423
+ )
424
+ assert.equal(criticals.length, 1)
425
+ assert.equal(criticals[0]!.code, ErrorCode.REMOTE_ADDON_NOT_DEV_DEPENDENCY)
426
+ assert.match(criticals[0]!.message, /devDependencies/)
427
+ } finally {
428
+ rmSync(dir, { recursive: true, force: true })
429
+ }
430
+ })
431
+
432
+ test('errors when the remote addon is missing from both', () => {
433
+ const { logger, criticals } = makeCriticalLogger()
434
+ const dir = writePkg({}, {})
435
+ try {
436
+ validateRemoteAddonDependencies(
437
+ logger,
438
+ makeRemoteState(dir, {
439
+ package: '@pikkufabric/addon-registry',
440
+ remote: true,
441
+ })
442
+ )
443
+ assert.equal(criticals.length, 1)
444
+ assert.equal(criticals[0]!.code, ErrorCode.REMOTE_ADDON_NOT_DEV_DEPENDENCY)
445
+ } finally {
446
+ rmSync(dir, { recursive: true, force: true })
447
+ }
448
+ })
449
+
450
+ test('ignores a non-remote (wireAddon) declaration', () => {
451
+ const { logger, criticals } = makeCriticalLogger()
452
+ const dir = writePkg({ '@addon/local': '1.0.0' }, {})
453
+ try {
454
+ validateRemoteAddonDependencies(
455
+ logger,
456
+ makeRemoteState(dir, { package: '@addon/local' })
457
+ )
458
+ assert.deepEqual(criticals, [])
459
+ } finally {
460
+ rmSync(dir, { recursive: true, force: true })
461
+ }
462
+ })
463
+ })
464
+
465
+ describe('validateRemoteAddonAuth (bound credential/secret must exist)', () => {
466
+ test('passes when the bound credential is wired', () => {
467
+ const { logger, criticals } = makeCriticalLogger()
468
+ validateRemoteAddonAuth(
469
+ logger,
470
+ makeRemoteState(
471
+ '/tmp',
472
+ {
473
+ package: '@pikkufabric/addon-registry',
474
+ remote: true,
475
+ authCredentialId: 'fabricRegistryToken',
476
+ },
477
+ { credentials: ['fabricRegistryToken'] }
478
+ )
479
+ )
480
+ assert.deepEqual(criticals, [])
481
+ })
482
+
483
+ test('errors when the bound credential is not wired', () => {
484
+ const { logger, criticals } = makeCriticalLogger()
485
+ validateRemoteAddonAuth(
486
+ logger,
487
+ makeRemoteState(
488
+ '/tmp',
489
+ {
490
+ package: '@pikkufabric/addon-registry',
491
+ remote: true,
492
+ authCredentialId: 'ghostToken',
493
+ },
494
+ { credentials: [] }
495
+ )
496
+ )
497
+ assert.equal(criticals.length, 1)
498
+ assert.equal(criticals[0]!.code, ErrorCode.REMOTE_ADDON_AUTH_UNRESOLVED)
499
+ assert.match(criticals[0]!.message, /ghostToken/)
500
+ })
501
+
502
+ test('errors when the bound secret is not wired', () => {
503
+ const { logger, criticals } = makeCriticalLogger()
504
+ validateRemoteAddonAuth(
505
+ logger,
506
+ makeRemoteState(
507
+ '/tmp',
508
+ {
509
+ package: '@pikkufabric/addon-registry',
510
+ remote: true,
511
+ authSecretId: 'ghostSecret',
512
+ },
513
+ { secrets: [] }
514
+ )
515
+ )
516
+ assert.equal(criticals.length, 1)
517
+ assert.equal(criticals[0]!.code, ErrorCode.REMOTE_ADDON_AUTH_UNRESOLVED)
518
+ })
519
+ })