@pikku/inspector 0.12.28 → 0.12.31
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.
- package/CHANGELOG.md +51 -0
- package/dist/add/add-file-with-factory.js +1 -0
- package/dist/error-codes.d.ts +1 -0
- package/dist/error-codes.js +2 -0
- package/dist/inspector.js +2 -0
- package/dist/types.d.ts +8 -0
- package/dist/utils/get-files-and-methods.d.ts +2 -1
- package/dist/utils/get-files-and-methods.js +2 -1
- package/dist/utils/load-addon-functions-meta.js +14 -0
- package/dist/utils/serialize-inspector-state.d.ts +9 -0
- package/dist/utils/serialize-inspector-state.js +4 -0
- package/dist/utils/workflow/derive-workflow-plan.js +19 -0
- package/dist/utils/workflow/dsl/extract-dsl-workflow.js +26 -1
- package/dist/utils/workflow/dsl/patterns.d.ts +4 -0
- package/dist/utils/workflow/dsl/patterns.js +12 -0
- package/dist/visit.js +1 -0
- package/package.json +6 -6
- package/src/add/add-file-with-factory.ts +1 -0
- package/src/add/pii-check.test.ts +5 -2
- package/src/error-codes.ts +3 -0
- package/src/inspector.ts +2 -0
- package/src/types.ts +8 -0
- package/src/utils/filter-inspector-state.test.ts +110 -1
- package/src/utils/get-files-and-methods.ts +6 -0
- package/src/utils/load-addon-functions-meta.ts +23 -0
- package/src/utils/serialize-inspector-state.ts +17 -0
- package/src/utils/workflow/derive-workflow-plan.test.ts +18 -0
- package/src/utils/workflow/derive-workflow-plan.ts +20 -0
- package/src/utils/workflow/dsl/extract-dsl-workflow.ts +31 -0
- package/src/utils/workflow/dsl/patterns.ts +19 -0
- package/src/visit.ts +6 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -46,12 +46,19 @@ export interface SerializableInspectorState {
|
|
|
46
46
|
>
|
|
47
47
|
wireServicesMeta: Array<[string, string[]]>
|
|
48
48
|
addonRequiredParentServices: string[]
|
|
49
|
+
addonServerlessIncompatible: Array<[string, string[]]>
|
|
49
50
|
configFactories: Array<
|
|
50
51
|
[
|
|
51
52
|
string,
|
|
52
53
|
{ variable: string; type: string | null; typePath: string | null }[],
|
|
53
54
|
]
|
|
54
55
|
>
|
|
56
|
+
serverLifecycleFactories: Array<
|
|
57
|
+
[
|
|
58
|
+
string,
|
|
59
|
+
{ variable: string; type: string | null; typePath: string | null }[],
|
|
60
|
+
]
|
|
61
|
+
>
|
|
55
62
|
filesAndMethods: InspectorState['filesAndMethods']
|
|
56
63
|
filesAndMethodsErrors: Array<
|
|
57
64
|
[
|
|
@@ -312,7 +319,13 @@ export function serializeInspectorState(
|
|
|
312
319
|
wireServicesFactories: Array.from(state.wireServicesFactories.entries()),
|
|
313
320
|
wireServicesMeta: Array.from(state.wireServicesMeta.entries()),
|
|
314
321
|
addonRequiredParentServices: state.addonRequiredParentServices,
|
|
322
|
+
addonServerlessIncompatible: Array.from(
|
|
323
|
+
state.addonServerlessIncompatible.entries()
|
|
324
|
+
),
|
|
315
325
|
configFactories: Array.from(state.configFactories.entries()),
|
|
326
|
+
serverLifecycleFactories: Array.from(
|
|
327
|
+
state.serverLifecycleFactories.entries()
|
|
328
|
+
),
|
|
316
329
|
filesAndMethods: state.filesAndMethods,
|
|
317
330
|
filesAndMethodsErrors: Array.from(
|
|
318
331
|
state.filesAndMethodsErrors.entries()
|
|
@@ -491,7 +504,11 @@ export function deserializeInspectorState(
|
|
|
491
504
|
wireServicesFactories: new Map(data.wireServicesFactories),
|
|
492
505
|
wireServicesMeta: new Map(data.wireServicesMeta),
|
|
493
506
|
addonRequiredParentServices: data.addonRequiredParentServices || [],
|
|
507
|
+
addonServerlessIncompatible: new Map(
|
|
508
|
+
data.addonServerlessIncompatible || []
|
|
509
|
+
),
|
|
494
510
|
configFactories: new Map(data.configFactories),
|
|
511
|
+
serverLifecycleFactories: new Map(data.serverLifecycleFactories),
|
|
495
512
|
filesAndMethods: data.filesAndMethods,
|
|
496
513
|
filesAndMethodsErrors: new Map(
|
|
497
514
|
data.filesAndMethodsErrors.map(([key, entries]) => [
|
|
@@ -119,4 +119,22 @@ describe('deriveWorkflowPlan', () => {
|
|
|
119
119
|
assert.equal(plan.deterministic, true)
|
|
120
120
|
assert.deepEqual(plan.plannedSteps, [{ stepName: 'a' }])
|
|
121
121
|
})
|
|
122
|
+
|
|
123
|
+
test('suspend steps appear in the plan with __workflow_suspend: key and sentence-case displayName', () => {
|
|
124
|
+
const plan = deriveWorkflowPlan([
|
|
125
|
+
rpc('build'),
|
|
126
|
+
{ type: 'suspend', reason: 'building' } as WorkflowStepMeta,
|
|
127
|
+
rpc('publish'),
|
|
128
|
+
{ type: 'suspend', reason: 'awaiting_approval' } as WorkflowStepMeta,
|
|
129
|
+
{ type: 'suspend', reason: 'building-image' } as WorkflowStepMeta,
|
|
130
|
+
])
|
|
131
|
+
assert.equal(plan.deterministic, true)
|
|
132
|
+
assert.deepEqual(plan.plannedSteps, [
|
|
133
|
+
{ stepName: 'build' },
|
|
134
|
+
{ stepName: '__workflow_suspend:building', displayName: 'Building' },
|
|
135
|
+
{ stepName: 'publish' },
|
|
136
|
+
{ stepName: '__workflow_suspend:awaiting_approval', displayName: 'Awaiting approval' },
|
|
137
|
+
{ stepName: '__workflow_suspend:building-image', displayName: 'Building image' },
|
|
138
|
+
])
|
|
139
|
+
})
|
|
122
140
|
})
|
|
@@ -58,6 +58,16 @@ function containsConditional(steps: WorkflowStepMeta[]): boolean {
|
|
|
58
58
|
return steps.some((step) => step.type === 'branch' || step.type === 'switch')
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Turn an internal snake/kebab reason key into a sentence-case display label.
|
|
63
|
+
* e.g. "awaiting_approval" → "Awaiting approval", "building-image" → "Building image"
|
|
64
|
+
*/
|
|
65
|
+
function reasonToLabel(reason: string): string {
|
|
66
|
+
return reason
|
|
67
|
+
.replace(/[-_]/g, ' ')
|
|
68
|
+
.replace(/^(.)/, (c) => c.toUpperCase())
|
|
69
|
+
}
|
|
70
|
+
|
|
61
71
|
/** Flatten named steps (rpc/inline/sleep/parallel children) in source order. */
|
|
62
72
|
function collectNamedSteps(steps: WorkflowStepMeta[]): WorkflowPlannedStep[] {
|
|
63
73
|
const planned: WorkflowPlannedStep[] = []
|
|
@@ -68,6 +78,16 @@ function collectNamedSteps(steps: WorkflowStepMeta[]): WorkflowPlannedStep[] {
|
|
|
68
78
|
case 'sleep':
|
|
69
79
|
planned.push({ stepName: step.stepName })
|
|
70
80
|
break
|
|
81
|
+
case 'suspend':
|
|
82
|
+
// Runtime stores the suspend step as `__workflow_suspend:${reason}`.
|
|
83
|
+
// We surface it in the planned ladder with a readable displayName so
|
|
84
|
+
// the UI shows it in the right position instead of appending it at the
|
|
85
|
+
// bottom as an unrecognised orphan.
|
|
86
|
+
planned.push({
|
|
87
|
+
stepName: `__workflow_suspend:${step.reason}`,
|
|
88
|
+
displayName: reasonToLabel(step.reason),
|
|
89
|
+
})
|
|
90
|
+
break
|
|
71
91
|
case 'parallel':
|
|
72
92
|
for (const child of step.children) {
|
|
73
93
|
planned.push({ stepName: child.stepName })
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
ParallelGroupStepMeta,
|
|
8
8
|
FanoutStepMeta,
|
|
9
9
|
CancelStepMeta,
|
|
10
|
+
SuspendStepMeta,
|
|
10
11
|
SetStepMeta,
|
|
11
12
|
SwitchStepMeta,
|
|
12
13
|
SwitchCaseMeta,
|
|
@@ -21,6 +22,7 @@ import type {
|
|
|
21
22
|
import {
|
|
22
23
|
isWorkflowDoCall,
|
|
23
24
|
isWorkflowSleepCall,
|
|
25
|
+
isWorkflowSuspendCall,
|
|
24
26
|
isThrowCancelException,
|
|
25
27
|
extractCancelReason,
|
|
26
28
|
isParallelFanout,
|
|
@@ -512,6 +514,10 @@ function extractExpressionStatement(
|
|
|
512
514
|
return extractSleepStep(call, context)
|
|
513
515
|
}
|
|
514
516
|
|
|
517
|
+
if (isWorkflowSuspendCall(call, context.checker)) {
|
|
518
|
+
return extractSuspendStep(call, context)
|
|
519
|
+
}
|
|
520
|
+
|
|
515
521
|
// Check for parallel group or fanout
|
|
516
522
|
if (isParallelFanout(call)) {
|
|
517
523
|
return extractParallelFanout(call, context)
|
|
@@ -698,6 +704,31 @@ function extractSleepStep(
|
|
|
698
704
|
}
|
|
699
705
|
}
|
|
700
706
|
|
|
707
|
+
/**
|
|
708
|
+
* Extract suspend step from workflow.suspend() call
|
|
709
|
+
*/
|
|
710
|
+
function extractSuspendStep(
|
|
711
|
+
call: ts.CallExpression,
|
|
712
|
+
context: ExtractionContext
|
|
713
|
+
): SuspendStepMeta | null {
|
|
714
|
+
const args = call.arguments
|
|
715
|
+
if (args.length < 1) return null
|
|
716
|
+
|
|
717
|
+
try {
|
|
718
|
+
const reason = extractStringLiteral(args[0], context.checker)
|
|
719
|
+
return {
|
|
720
|
+
type: 'suspend',
|
|
721
|
+
reason,
|
|
722
|
+
}
|
|
723
|
+
} catch (error) {
|
|
724
|
+
context.errors.push({
|
|
725
|
+
message: `Failed to extract suspend step: ${error instanceof Error ? error.message : String(error)}`,
|
|
726
|
+
node: call,
|
|
727
|
+
})
|
|
728
|
+
return null
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
701
732
|
/**
|
|
702
733
|
* Extract cancel step from throw WorkflowCancelledException statement
|
|
703
734
|
*/
|
|
@@ -42,6 +42,25 @@ export function isWorkflowSleepCall(
|
|
|
42
42
|
)
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Check if a call expression is workflow.suspend()
|
|
47
|
+
*/
|
|
48
|
+
export function isWorkflowSuspendCall(
|
|
49
|
+
node: ts.CallExpression,
|
|
50
|
+
_checker: ts.TypeChecker
|
|
51
|
+
): boolean {
|
|
52
|
+
if (!ts.isPropertyAccessExpression(node.expression)) {
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const propAccess = node.expression
|
|
57
|
+
return (
|
|
58
|
+
propAccess.name.text === 'suspend' &&
|
|
59
|
+
ts.isIdentifier(propAccess.expression) &&
|
|
60
|
+
propAccess.expression.text === 'workflow'
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
45
64
|
/**
|
|
46
65
|
* Check if a throw statement throws WorkflowCancelledException
|
|
47
66
|
* Matches: throw new WorkflowCancelledException(...) or throw WorkflowCancelledException(...)
|
package/src/visit.ts
CHANGED
|
@@ -87,6 +87,12 @@ export const visitSetup = (
|
|
|
87
87
|
)
|
|
88
88
|
|
|
89
89
|
addFileWithFactory(node, checker, state.configFactories, 'CreateConfig')
|
|
90
|
+
addFileWithFactory(
|
|
91
|
+
node,
|
|
92
|
+
checker,
|
|
93
|
+
state.serverLifecycleFactories,
|
|
94
|
+
'ServerLifecycle'
|
|
95
|
+
)
|
|
90
96
|
|
|
91
97
|
addRPCInvocations(node, state, logger)
|
|
92
98
|
addWireAddon(node, state, logger)
|