@pikku/cli 0.12.89 → 0.12.91
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/README.md +25 -2
- package/console-app/assets/{index-BaOGIr2O.js → index-CpkvmAGb.js} +148 -148
- package/console-app/assets/{index-CSzCJzBb.css → index-D0HG8q0B.css} +1 -1
- package/console-app/index.html +2 -2
- package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-channel.js +21 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-contracts-meta.gen.json +6 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +52 -1
- package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
- package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.d.ts +4 -4
- package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.json +73 -13
- package/dist/.pikku/function/pikku-functions.gen.js +9 -1
- package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap-scenarios.gen.d.ts +8 -0
- package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +8 -0
- package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
- package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
- package/dist/.pikku/pikku-meta-service.gen.js +1 -1
- package/dist/.pikku/pikku-services.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +4 -0
- package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.d.ts +1 -0
- package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +10 -0
- package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.json +1 -0
- package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +4 -0
- package/dist/.pikku/scenarios/pikku-scenario-functions.gen.js +1 -0
- package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.d.ts +1 -0
- package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +10 -0
- package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +4 -0
- package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.js +1 -0
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
- package/dist/.pikku/schemas/register.gen.js +9 -1
- package/dist/.pikku/schemas/schemas/DbBaselineInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/DbCheckInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/DbCodegenInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/DbExportInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/FabricValidateInput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/ScenarioRunInput.schema.json +1 -1
- package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +1 -1
- package/dist/.pikku/scopes/pikku-scope-types.gen.js +1 -1
- package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-scenario-actors.gen.d.ts +19 -0
- package/dist/.pikku/workflow/pikku-scenario-actors.gen.js +17 -0
- package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +152 -3
- package/dist/.pikku/workflow/pikku-workflow-types.gen.js +39 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
- package/dist/bin/pikku-bin.mjs +2 -2
- package/dist/src/cli.wiring.js +42 -0
- package/dist/src/fabric/fabric-commands.d.ts +9 -3
- package/dist/src/fabric/fabric-commands.js +6 -0
- package/dist/src/fabric/functions/validate.function.d.ts +15 -5
- package/dist/src/fabric/functions/validate.function.js +69 -16
- package/dist/src/fabric/lib/config.d.ts +11 -1
- package/dist/src/fabric/lib/config.js +39 -1
- package/dist/src/fabric/lib/frontend-typecheck.d.ts +46 -0
- package/dist/src/fabric/lib/frontend-typecheck.js +139 -0
- package/dist/src/functions/commands/db-baseline.d.ts +13 -0
- package/dist/src/functions/commands/db-baseline.js +48 -0
- package/dist/src/functions/commands/db-check.d.ts +12 -0
- package/dist/src/functions/commands/db-check.js +64 -0
- package/dist/src/functions/commands/db-codegen.d.ts +11 -0
- package/dist/src/functions/commands/db-codegen.js +39 -0
- package/dist/src/functions/commands/db-export.d.ts +12 -0
- package/dist/src/functions/commands/db-export.js +31 -0
- package/dist/src/functions/commands/db-generate.js +19 -26
- package/dist/src/functions/commands/dev.js +2 -0
- package/dist/src/functions/commands/load-user-project.d.ts +7 -0
- package/dist/src/functions/commands/load-user-project.js +21 -0
- package/dist/src/functions/commands/pikku-command-bootstrap.js +12 -0
- package/dist/src/functions/commands/scenario-browser.d.ts +69 -0
- package/dist/src/functions/commands/scenario-browser.js +60 -0
- package/dist/src/functions/commands/scenario-formatter.d.ts +81 -0
- package/dist/src/functions/commands/scenario-formatter.js +120 -0
- package/dist/src/functions/commands/scenario-ladder.d.ts +66 -0
- package/dist/src/functions/commands/scenario-ladder.js +132 -0
- package/dist/src/functions/commands/scenario-plan.d.ts +61 -0
- package/dist/src/functions/commands/scenario-plan.js +106 -0
- package/dist/src/functions/commands/scenario.d.ts +18 -0
- package/dist/src/functions/commands/scenario.js +305 -38
- package/dist/src/functions/commands/serve.js +2 -0
- package/dist/src/functions/db/db-migrator.d.ts +35 -4
- package/dist/src/functions/db/db-migrator.js +67 -10
- package/dist/src/functions/db/local-db.d.ts +235 -13
- package/dist/src/functions/db/local-db.js +430 -45
- package/dist/src/functions/db/postgres/postgres-introspector.js +19 -1
- package/dist/src/functions/db/postgres/postgres-migrator.d.ts +1 -0
- package/dist/src/functions/db/postgres/postgres-migrator.js +4 -1
- package/dist/src/functions/db/sqlite/sqlite-introspector.js +2 -1
- package/dist/src/functions/db/sqlite/sqlite-migrator.d.ts +1 -0
- package/dist/src/functions/db/sqlite/sqlite-migrator.js +6 -1
- package/dist/src/functions/wirings/functions/pikku-command-functions.js +26 -8
- package/dist/src/functions/wirings/functions/serialize-function-types.js +3 -3
- package/dist/src/functions/wirings/scenarios/scenario-partition.d.ts +29 -0
- package/dist/src/functions/wirings/scenarios/scenario-partition.js +58 -0
- package/dist/src/functions/wirings/scenarios/serialize-feature-meta.d.ts +14 -0
- package/dist/src/functions/wirings/scenarios/serialize-feature-meta.js +29 -0
- package/dist/src/functions/wirings/scenarios/serialize-scenario-meta.d.ts +12 -0
- package/dist/src/functions/wirings/scenarios/serialize-scenario-meta.js +56 -0
- package/dist/src/functions/wirings/scenarios/serialize-scenario-registration.d.ts +7 -0
- package/dist/src/functions/wirings/scenarios/serialize-scenario-registration.js +40 -0
- package/dist/src/functions/wirings/workflow/pikku-command-workflow.js +43 -8
- package/dist/src/functions/wirings/workflow/serialize-scenario-actors.d.ts +1 -1
- package/dist/src/functions/wirings/workflow/serialize-scenario-actors.js +13 -2
- package/dist/src/functions/wirings/workflow/serialize-scenario-step-map.d.ts +4 -0
- package/dist/src/functions/wirings/workflow/serialize-scenario-step-map.js +62 -0
- package/dist/src/functions/wirings/workflow/serialize-workflow-bootstrap-map.js +10 -3
- package/dist/src/functions/wirings/workflow/serialize-workflow-map.js +10 -3
- package/dist/src/functions/wirings/workflow/serialize-workflow-meta.js +4 -4
- package/dist/src/functions/wirings/workflow/serialize-workflow-types.d.ts +1 -1
- package/dist/src/functions/wirings/workflow/serialize-workflow-types.js +232 -3
- package/dist/src/server/server-ready.d.ts +12 -0
- package/dist/src/server/server-ready.js +12 -0
- package/dist/src/server/spawn-dev-server.d.ts +50 -0
- package/dist/src/server/spawn-dev-server.js +112 -0
- package/dist/src/services.js +12 -1
- package/dist/src/utils/meta-diff.js +5 -2
- package/dist/src/utils/pikku-cli-config.js +26 -0
- package/dist/src/utils/resolve-scenario-actors.d.ts +23 -0
- package/dist/src/utils/resolve-scenario-actors.js +74 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -6
- package/skills/pikku-i18n/SKILL.md +53 -6
- package/skills/pikku-scenario/SKILL.md +196 -29
|
@@ -8,9 +8,9 @@ export const serializeWorkflowMeta = (outputPath, metaDir, workflowNames, packag
|
|
|
8
8
|
const pkg = packageName ? `'${packageName}'` : 'null';
|
|
9
9
|
if (workflowNames.length === 0) {
|
|
10
10
|
return `import { pikkuState } from '@pikku/core/internal'
|
|
11
|
-
import type {
|
|
11
|
+
import type { WorkflowsRuntimeMeta } from '@pikku/core/workflow/types'
|
|
12
12
|
|
|
13
|
-
const workflowsMeta:
|
|
13
|
+
const workflowsMeta: WorkflowsRuntimeMeta = {}
|
|
14
14
|
|
|
15
15
|
pikkuState(${pkg}, 'workflows', 'meta', workflowsMeta)`;
|
|
16
16
|
}
|
|
@@ -32,13 +32,13 @@ pikkuState(${pkg}, 'workflows', 'meta', workflowsMeta)`;
|
|
|
32
32
|
})
|
|
33
33
|
.join('\n');
|
|
34
34
|
return `import { pikkuState } from '@pikku/core/internal'
|
|
35
|
-
import type {
|
|
35
|
+
import type { WorkflowsRuntimeMeta } from '@pikku/core/workflow/types'
|
|
36
36
|
|
|
37
37
|
${imports}
|
|
38
38
|
|
|
39
39
|
const workflowsMeta = {
|
|
40
40
|
${metaEntries}
|
|
41
|
-
} as
|
|
41
|
+
} as WorkflowsRuntimeMeta
|
|
42
42
|
|
|
43
43
|
pikkuState(${pkg}, 'workflows', 'meta', workflowsMeta)`;
|
|
44
44
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const serializeWorkflowTypes: (functionTypesImportPath: string, rpcMapImportPath: string, workflowMapImportPath: string, agentMapImportPath: string, scopesImportPath: string) => string;
|
|
1
|
+
export declare const serializeWorkflowTypes: (functionTypesImportPath: string, rpcMapImportPath: string, workflowMapImportPath: string, agentMapImportPath: string, scopesImportPath: string, scenarioStepMapImportPath?: string, scenarioActorsImportPath?: string) => string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const serializeWorkflowTypes = (functionTypesImportPath, rpcMapImportPath, workflowMapImportPath, agentMapImportPath, scopesImportPath) => {
|
|
1
|
+
export const serializeWorkflowTypes = (functionTypesImportPath, rpcMapImportPath, workflowMapImportPath, agentMapImportPath, scopesImportPath, scenarioStepMapImportPath = './pikku-scenario-step-map.gen.js', scenarioActorsImportPath = './pikku-scenario-actors.gen.js') => {
|
|
2
2
|
return `import { WorkflowCancelledException } from '@pikku/core/workflow'
|
|
3
3
|
import { template } from '@pikku/core/workflow'
|
|
4
4
|
import {
|
|
@@ -6,13 +6,17 @@ import {
|
|
|
6
6
|
type PikkuWorkflowGraphConfig,
|
|
7
7
|
type PikkuWorkflowGraphResult,
|
|
8
8
|
} from '@pikku/core/workflow'
|
|
9
|
-
import type { PikkuWorkflowWire, PikkuScenarioWire, WorkflowStepOptions } from '@pikku/core/workflow'
|
|
9
|
+
import type { PikkuWorkflowWire, PikkuScenarioWire, WorkflowStepOptions, ScenarioStepOptions } from '@pikku/core/workflow'
|
|
10
10
|
|
|
11
11
|
export { WorkflowCancelledException }
|
|
12
12
|
import type { PikkuFunctionSessionless, PikkuFunctionConfig } from '${functionTypesImportPath}'
|
|
13
13
|
import type { FlattenedRPCMap } from '${rpcMapImportPath}'
|
|
14
14
|
import type { FlattenedWorkflowMap } from '${workflowMapImportPath}'
|
|
15
15
|
import type { AgentMap as FlattenedAgentMap } from '${agentMapImportPath}'
|
|
16
|
+
import type { FlattenedScenarioStepMap } from '${scenarioStepMapImportPath}'
|
|
17
|
+
import type { TypedScenarioActors } from '${scenarioActorsImportPath}'
|
|
18
|
+
|
|
19
|
+
export type { TypedScenarioActors }
|
|
16
20
|
|
|
17
21
|
export { template }
|
|
18
22
|
|
|
@@ -38,7 +42,44 @@ export interface TypedWorkflow extends PikkuWorkflowWire {
|
|
|
38
42
|
): Promise<T>
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
/**
|
|
46
|
+
* The typed half of a scenario wire: \`step\` and its \`given\`/\`when\`/\`then\`
|
|
47
|
+
* sugar, narrowed to the names declared by \`pikkuScenarioStep\` in this project.
|
|
48
|
+
* The phase only changes the prose the reporter renders.
|
|
49
|
+
*/
|
|
50
|
+
export interface TypedScenarioSteps {
|
|
51
|
+
step<K extends keyof FlattenedScenarioStepMap>(
|
|
52
|
+
stepName: string,
|
|
53
|
+
stepFunc: K,
|
|
54
|
+
data?: FlattenedScenarioStepMap[K]['input'],
|
|
55
|
+
options?: ScenarioStepOptions
|
|
56
|
+
): Promise<FlattenedScenarioStepMap[K]['output']>
|
|
57
|
+
|
|
58
|
+
given<K extends keyof FlattenedScenarioStepMap>(
|
|
59
|
+
stepName: string,
|
|
60
|
+
stepFunc: K,
|
|
61
|
+
data?: FlattenedScenarioStepMap[K]['input'],
|
|
62
|
+
options?: ScenarioStepOptions
|
|
63
|
+
): Promise<FlattenedScenarioStepMap[K]['output']>
|
|
64
|
+
|
|
65
|
+
when<K extends keyof FlattenedScenarioStepMap>(
|
|
66
|
+
stepName: string,
|
|
67
|
+
stepFunc: K,
|
|
68
|
+
data?: FlattenedScenarioStepMap[K]['input'],
|
|
69
|
+
options?: ScenarioStepOptions
|
|
70
|
+
): Promise<FlattenedScenarioStepMap[K]['output']>
|
|
71
|
+
|
|
72
|
+
then<K extends keyof FlattenedScenarioStepMap>(
|
|
73
|
+
stepName: string,
|
|
74
|
+
stepFunc: K,
|
|
75
|
+
data?: FlattenedScenarioStepMap[K]['input'],
|
|
76
|
+
options?: ScenarioStepOptions
|
|
77
|
+
): Promise<FlattenedScenarioStepMap[K]['output']>
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type TypedScenario = TypedWorkflow &
|
|
81
|
+
Omit<PikkuScenarioWire, keyof PikkuWorkflowWire | keyof TypedScenarioSteps> &
|
|
82
|
+
TypedScenarioSteps
|
|
42
83
|
|
|
43
84
|
import type { StandardSchemaV1 } from '@standard-schema/spec'
|
|
44
85
|
import type { InferSchemaOutput, PikkuPermission, PikkuMiddleware, NodeConfig, PikkuApprovalDescription } from '${functionTypesImportPath}'
|
|
@@ -129,6 +170,49 @@ export type PikkuScenarioConfigWithSchema<
|
|
|
129
170
|
InputSchema extends StandardSchemaV1 ? InferSchemaOutput<InputSchema> : unknown,
|
|
130
171
|
OutputSchema extends StandardSchemaV1 ? InferSchemaOutput<OutputSchema> : unknown
|
|
131
172
|
>
|
|
173
|
+
/**
|
|
174
|
+
* Runs before the scenario body. Same signature as \`func\`, but its return
|
|
175
|
+
* value is discarded — a hook is setup, not a step, so it is never recorded
|
|
176
|
+
* on the ladder. Throwing skips the body and fails the run; \`after\` still
|
|
177
|
+
* runs.
|
|
178
|
+
*/
|
|
179
|
+
before?: PikkuScenarioHook<InputSchema>
|
|
180
|
+
/**
|
|
181
|
+
* Always runs after the scenario body, in a \`finally\`, whether it passed or
|
|
182
|
+
* failed. Throwing fails a run that would otherwise have passed; on an
|
|
183
|
+
* already-failed run it attaches as the \`cause\` and never replaces the
|
|
184
|
+
* original error.
|
|
185
|
+
*/
|
|
186
|
+
after?: PikkuScenarioHook<InputSchema>
|
|
187
|
+
/**
|
|
188
|
+
* Why this scenario is held out of a default run, stated where the scenario
|
|
189
|
+
* is. The scenario still appears in the plan and is reported as skipped
|
|
190
|
+
* rather than quietly omitted; naming it directly with \`--flows\` runs it
|
|
191
|
+
* anyway.
|
|
192
|
+
*/
|
|
193
|
+
skip?: string
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* A scenario lifecycle hook: the scenario's own \`(services, data, wire)\`
|
|
198
|
+
* signature with its result discarded.
|
|
199
|
+
*/
|
|
200
|
+
export type PikkuScenarioHook<
|
|
201
|
+
InputSchema extends StandardSchemaV1 | undefined = undefined
|
|
202
|
+
> = PikkuFunctionScenario<
|
|
203
|
+
InputSchema extends StandardSchemaV1 ? InferSchemaOutput<InputSchema> : unknown,
|
|
204
|
+
void
|
|
205
|
+
>
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Declares a scenario hook. Returns the function verbatim — a hook is never
|
|
209
|
+
* registered, so this exists purely to give an inline hook a call site to be
|
|
210
|
+
* contextually typed from, the way every other pikku primitive is.
|
|
211
|
+
*/
|
|
212
|
+
export function pikkuScenarioHook<In = unknown>(
|
|
213
|
+
hook: PikkuFunctionScenario<In, void>
|
|
214
|
+
): PikkuFunctionScenario<In, void> {
|
|
215
|
+
return hook
|
|
132
216
|
}
|
|
133
217
|
|
|
134
218
|
/**
|
|
@@ -152,6 +236,151 @@ export function pikkuScenario(func: any) {
|
|
|
152
236
|
return typeof func === 'function' ? { func } : func
|
|
153
237
|
}
|
|
154
238
|
|
|
239
|
+
/**
|
|
240
|
+
* A scenario as a feature references it. Any \`pikkuScenario\` export is
|
|
241
|
+
* assignable; \`In\` is recovered from it so the paired form's \`data\` is
|
|
242
|
+
* checked against that scenario's own input.
|
|
243
|
+
*/
|
|
244
|
+
export type PikkuScenarioRef<In = any, Out = any> = PikkuFunctionConfig<
|
|
245
|
+
In,
|
|
246
|
+
Out,
|
|
247
|
+
'scenario' | 'actors',
|
|
248
|
+
PikkuFunctionScenario<In, Out>,
|
|
249
|
+
any,
|
|
250
|
+
any
|
|
251
|
+
>
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* One entry in a feature's \`scenarios\` list, validated against itself: a bare
|
|
255
|
+
* scenario, or a scenario paired with the input to run it with. The paired form
|
|
256
|
+
* is gherkin's \`Examples:\` written as an ordinary loop.
|
|
257
|
+
*/
|
|
258
|
+
export type PikkuFeatureEntry<Entry> = Entry extends {
|
|
259
|
+
scenario: PikkuScenarioRef<infer In>
|
|
260
|
+
}
|
|
261
|
+
? { scenario: PikkuScenarioRef<In>; data: In }
|
|
262
|
+
: Entry extends PikkuScenarioRef
|
|
263
|
+
? Entry
|
|
264
|
+
: never
|
|
265
|
+
|
|
266
|
+
export type PikkuFeatureConfig<Scenarios extends readonly unknown[]> = {
|
|
267
|
+
/** Human-readable name. The export identifier is the id. */
|
|
268
|
+
name: string
|
|
269
|
+
description?: string
|
|
270
|
+
tags?: string[]
|
|
271
|
+
scenarios: { [K in keyof Scenarios]: PikkuFeatureEntry<Scenarios[K]> }
|
|
272
|
+
/**
|
|
273
|
+
* Runs ONCE before the whole group — not before each scenario. Per-scenario
|
|
274
|
+
* setup is the scenario's own \`before\`; gherkin's \`Background:\` is
|
|
275
|
+
* deliberately not expressible here.
|
|
276
|
+
*/
|
|
277
|
+
before?: PikkuScenarioHook
|
|
278
|
+
after?: PikkuScenarioHook
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* A feature: an ordered group of scenarios, mirroring gherkin's Feature ↔
|
|
283
|
+
* Scenario structure. Scenarios are referenced by imported identifier, so a
|
|
284
|
+
* renamed or deleted scenario is a compile error rather than a silent skip.
|
|
285
|
+
*
|
|
286
|
+
* A scenario does not have to belong to a feature — a void-input scenario
|
|
287
|
+
* still runs standalone.
|
|
288
|
+
*
|
|
289
|
+
* \`\`\`ts
|
|
290
|
+
* export const credentialFeature = pikkuFeature({
|
|
291
|
+
* name: 'Credential API',
|
|
292
|
+
* tags: ['credential'],
|
|
293
|
+
* before: startsMockOAuthServer,
|
|
294
|
+
* after: stopsMockOAuthServer,
|
|
295
|
+
* scenarios: [
|
|
296
|
+
* credentialLazyLoadScenario,
|
|
297
|
+
* ...['stripe', 'google'].map((name) => ({
|
|
298
|
+
* scenario: credentialRoundTripScenario,
|
|
299
|
+
* data: { name },
|
|
300
|
+
* })),
|
|
301
|
+
* ],
|
|
302
|
+
* })
|
|
303
|
+
* \`\`\`
|
|
304
|
+
*/
|
|
305
|
+
export function pikkuFeature<const Scenarios extends readonly unknown[]>(
|
|
306
|
+
config: PikkuFeatureConfig<Scenarios>
|
|
307
|
+
): PikkuFeatureConfig<Scenarios> {
|
|
308
|
+
return config
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export type PikkuFunctionScenarioStep<
|
|
312
|
+
In = unknown,
|
|
313
|
+
Out = never,
|
|
314
|
+
B extends boolean = false
|
|
315
|
+
> = PikkuFunctionSessionless<In, Out, B extends true ? 'scenarioStep' | 'browser' : 'scenarioStep'>
|
|
316
|
+
|
|
317
|
+
export type PikkuScenarioStepConfigWithSchema<
|
|
318
|
+
InputSchema extends StandardSchemaV1 | undefined = undefined,
|
|
319
|
+
OutputSchema extends StandardSchemaV1 | undefined = undefined,
|
|
320
|
+
B extends boolean = false
|
|
321
|
+
> = {
|
|
322
|
+
/** Registered name — this is the string \`scenario.step()\` references. */
|
|
323
|
+
name: string
|
|
324
|
+
/**
|
|
325
|
+
* What this step does, for the console and for whoever reads the source. It
|
|
326
|
+
* is also the fallback prose when no \`template\` is declared, in which case a
|
|
327
|
+
* reporter renders "Given the shopper buys an apple". Defaults to the call
|
|
328
|
+
* site's step name.
|
|
329
|
+
*/
|
|
330
|
+
description?: string
|
|
331
|
+
/**
|
|
332
|
+
* The prose a reporter renders for this step, with \`{placeholders}\` filled
|
|
333
|
+
* from the input the step was called with — \`'sees {packageName}'\` reports as
|
|
334
|
+
* "Then the admin sees @pikku/addon-todos". Every input field should appear,
|
|
335
|
+
* so the report names the values under test rather than repeating one
|
|
336
|
+
* sentence per call site.
|
|
337
|
+
*/
|
|
338
|
+
template?: string
|
|
339
|
+
/**
|
|
340
|
+
* This step drives a browser. The runner provisions one for the step's actor
|
|
341
|
+
* before calling it — which also makes an actor mandatory — and \`wire.browser\`
|
|
342
|
+
* is non-optional inside \`func\`.
|
|
343
|
+
*/
|
|
344
|
+
browser?: B
|
|
345
|
+
title?: string
|
|
346
|
+
tags?: string[]
|
|
347
|
+
input?: InputSchema
|
|
348
|
+
output?: OutputSchema
|
|
349
|
+
errors?: Array<typeof PikkuError>
|
|
350
|
+
func: PikkuFunctionScenarioStep<
|
|
351
|
+
InputSchema extends StandardSchemaV1 ? InferSchemaOutput<InputSchema> : unknown,
|
|
352
|
+
OutputSchema extends StandardSchemaV1 ? InferSchemaOutput<OutputSchema> : unknown,
|
|
353
|
+
B
|
|
354
|
+
>
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export type PikkuScenarioStepConfig<In, Out, B extends boolean = false> =
|
|
358
|
+
Omit<PikkuScenarioStepConfigWithSchema<undefined, undefined, B>, 'func' | 'input' | 'output'> & {
|
|
359
|
+
func: PikkuFunctionScenarioStep<In, Out, B>
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* A named, reusable scenario step. Unlike \`scenario.do\`, which can only name an
|
|
364
|
+
* RPC, a step's body is an ordinary pikku function — so it may drive a browser,
|
|
365
|
+
* invoke RPCs as its actor, or run a workflow.
|
|
366
|
+
*
|
|
367
|
+
* Steps are deliberately NOT registered as RPCs: a browser-driving step must
|
|
368
|
+
* never be network-callable.
|
|
369
|
+
*/
|
|
370
|
+
export function pikkuScenarioStep<
|
|
371
|
+
InputSchema extends StandardSchemaV1 | undefined = undefined,
|
|
372
|
+
OutputSchema extends StandardSchemaV1 | undefined = undefined,
|
|
373
|
+
B extends boolean = false
|
|
374
|
+
>(
|
|
375
|
+
config: PikkuScenarioStepConfigWithSchema<InputSchema, OutputSchema, B>
|
|
376
|
+
): PikkuFunctionConfig<InputSchema extends StandardSchemaV1 ? InferSchemaOutput<InputSchema> : unknown, OutputSchema extends StandardSchemaV1 ? InferSchemaOutput<OutputSchema> : unknown, B extends true ? 'scenarioStep' | 'browser' : 'scenarioStep', PikkuFunctionScenarioStep<InputSchema extends StandardSchemaV1 ? InferSchemaOutput<InputSchema> : unknown, OutputSchema extends StandardSchemaV1 ? InferSchemaOutput<OutputSchema> : unknown, B>, InputSchema, OutputSchema>
|
|
377
|
+
export function pikkuScenarioStep<In, Out = unknown, B extends boolean = false>(
|
|
378
|
+
config: PikkuScenarioStepConfig<In, Out, B>
|
|
379
|
+
): PikkuFunctionConfig<In, Out, B extends true ? 'scenarioStep' | 'browser' : 'scenarioStep'>
|
|
380
|
+
export function pikkuScenarioStep(config: any) {
|
|
381
|
+
return config
|
|
382
|
+
}
|
|
383
|
+
|
|
155
384
|
type TypedRef<T> = { $ref: string; path?: string } & { __phantomType?: T }
|
|
156
385
|
|
|
157
386
|
type TemplateString = {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Printed once the server is listening AND the project's `afterStart`
|
|
3
|
+
* lifecycle has resolved.
|
|
4
|
+
*
|
|
5
|
+
* The runtime's own `listening on …` line is emitted inside `server.start()`,
|
|
6
|
+
* which runs *before* `afterStart` — so anything a project seeds there (users,
|
|
7
|
+
* scopes, fixtures) is still pending when it appears, and a parent process
|
|
8
|
+
* that treats it as readiness races the seed. This marker is the point at
|
|
9
|
+
* which the server is actually usable, and it is what `--spawn` waits for.
|
|
10
|
+
*/
|
|
11
|
+
export declare const SERVER_READY_MARKER = "pikku: ready";
|
|
12
|
+
export declare const serverReadyLine: (hostname: string, port: number) => string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Printed once the server is listening AND the project's `afterStart`
|
|
3
|
+
* lifecycle has resolved.
|
|
4
|
+
*
|
|
5
|
+
* The runtime's own `listening on …` line is emitted inside `server.start()`,
|
|
6
|
+
* which runs *before* `afterStart` — so anything a project seeds there (users,
|
|
7
|
+
* scopes, fixtures) is still pending when it appears, and a parent process
|
|
8
|
+
* that treats it as readiness races the seed. This marker is the point at
|
|
9
|
+
* which the server is actually usable, and it is what `--spawn` waits for.
|
|
10
|
+
*/
|
|
11
|
+
export const SERVER_READY_MARKER = 'pikku: ready';
|
|
12
|
+
export const serverReadyLine = (hostname, port) => `${SERVER_READY_MARKER} on http://${hostname}:${port}`;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rejects if anything is already listening on `port`.
|
|
3
|
+
*
|
|
4
|
+
* A readiness check cannot tell "my server" from "someone else's": both answer
|
|
5
|
+
* on the same address once spawned, so a stale server left on the target port
|
|
6
|
+
* silently absorbs a whole run and reports failures that belong to code nobody
|
|
7
|
+
* is looking at. A TCP-level check before spawning is the only point at which
|
|
8
|
+
* the two are distinguishable.
|
|
9
|
+
*/
|
|
10
|
+
export declare const assertPortFree: (port: number, host?: string, timeoutMs?: number) => Promise<void>;
|
|
11
|
+
export interface ReadyBarrier {
|
|
12
|
+
/** Feed a chunk of the child's output. */
|
|
13
|
+
observe: (text: string) => void;
|
|
14
|
+
/** Record that the child exited, so the wait fails fast instead of timing out. */
|
|
15
|
+
markExited: (code: number | null) => void;
|
|
16
|
+
wait: (options?: {
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
pollMs?: number;
|
|
19
|
+
}) => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Watches a spawned server's output for the ready marker. Separated from the
|
|
23
|
+
* spawn so the three outcomes that matter — ready, the child died, nothing
|
|
24
|
+
* happened for long enough — are testable without a real process.
|
|
25
|
+
*/
|
|
26
|
+
export declare const createReadyBarrier: (label: string) => ReadyBarrier;
|
|
27
|
+
export interface SpawnedServer {
|
|
28
|
+
/** Kills the whole process group, so the server's own children go with it. */
|
|
29
|
+
stop: () => void;
|
|
30
|
+
/** Resolves on the ready marker; rejects if the server dies or never reports. */
|
|
31
|
+
waitUntilReady: (options?: {
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
}) => Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
export interface SpawnDevServerOptions {
|
|
36
|
+
cwd: string;
|
|
37
|
+
port: number;
|
|
38
|
+
hostname?: string;
|
|
39
|
+
coverage?: boolean;
|
|
40
|
+
/** Passed through as `--test`, which sets PIKKU_TEST_RUN for isTestRun(). */
|
|
41
|
+
test?: boolean;
|
|
42
|
+
env?: NodeJS.ProcessEnv;
|
|
43
|
+
/** Where the server's own output goes. Defaults to this process's streams. */
|
|
44
|
+
onOutput?: (text: string) => void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Start `pikku dev` as its own process group and stream its output under a
|
|
48
|
+
* `[server]` prefix. Does not wait — call `waitUntilReady`.
|
|
49
|
+
*/
|
|
50
|
+
export declare const spawnDevServer: (options: SpawnDevServerOptions) => Promise<SpawnedServer>;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { connect } from 'node:net';
|
|
3
|
+
import { SERVER_READY_MARKER } from './server-ready.js';
|
|
4
|
+
/**
|
|
5
|
+
* Rejects if anything is already listening on `port`.
|
|
6
|
+
*
|
|
7
|
+
* A readiness check cannot tell "my server" from "someone else's": both answer
|
|
8
|
+
* on the same address once spawned, so a stale server left on the target port
|
|
9
|
+
* silently absorbs a whole run and reports failures that belong to code nobody
|
|
10
|
+
* is looking at. A TCP-level check before spawning is the only point at which
|
|
11
|
+
* the two are distinguishable.
|
|
12
|
+
*/
|
|
13
|
+
export const assertPortFree = (port, host = '127.0.0.1', timeoutMs = 1000) => new Promise((resolve, reject) => {
|
|
14
|
+
const socket = connect({ port, host });
|
|
15
|
+
const settle = (error) => {
|
|
16
|
+
socket.removeAllListeners();
|
|
17
|
+
socket.destroy();
|
|
18
|
+
error ? reject(error) : resolve();
|
|
19
|
+
};
|
|
20
|
+
socket.setTimeout(timeoutMs);
|
|
21
|
+
socket.on('connect', () => settle(new Error(`Port ${port} is already in use — something is already listening on ${host}:${port}. ` +
|
|
22
|
+
`Stop it, or point this environment's apiUrl at a free port.`)));
|
|
23
|
+
// Nothing listening, or unreachable: either way this process is free to bind.
|
|
24
|
+
socket.on('error', () => settle());
|
|
25
|
+
socket.on('timeout', () => settle());
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Watches a spawned server's output for the ready marker. Separated from the
|
|
29
|
+
* spawn so the three outcomes that matter — ready, the child died, nothing
|
|
30
|
+
* happened for long enough — are testable without a real process.
|
|
31
|
+
*/
|
|
32
|
+
export const createReadyBarrier = (label) => {
|
|
33
|
+
let ready = false;
|
|
34
|
+
let exit;
|
|
35
|
+
// Stdout arrives in arbitrary chunks, so the marker can straddle two of them.
|
|
36
|
+
// Keep just enough of the tail to rejoin a split marker.
|
|
37
|
+
let tail = '';
|
|
38
|
+
return {
|
|
39
|
+
observe: (text) => {
|
|
40
|
+
if (ready)
|
|
41
|
+
return;
|
|
42
|
+
const window = tail + text;
|
|
43
|
+
if (window.includes(SERVER_READY_MARKER)) {
|
|
44
|
+
ready = true;
|
|
45
|
+
tail = '';
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
tail = window.slice(-SERVER_READY_MARKER.length);
|
|
49
|
+
},
|
|
50
|
+
markExited: (code) => {
|
|
51
|
+
exit = { code };
|
|
52
|
+
},
|
|
53
|
+
wait: async ({ timeoutMs = 120_000, pollMs = 100 } = {}) => {
|
|
54
|
+
const deadline = Date.now() + timeoutMs;
|
|
55
|
+
while (Date.now() < deadline) {
|
|
56
|
+
if (ready)
|
|
57
|
+
return;
|
|
58
|
+
if (exit) {
|
|
59
|
+
throw new Error(`${label} exited with code ${exit.code} before it was ready`);
|
|
60
|
+
}
|
|
61
|
+
await new Promise((r) => setTimeout(r, pollMs));
|
|
62
|
+
}
|
|
63
|
+
throw new Error(`${label} did not report ready within ${timeoutMs / 1000} seconds`);
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Start `pikku dev` as its own process group and stream its output under a
|
|
69
|
+
* `[server]` prefix. Does not wait — call `waitUntilReady`.
|
|
70
|
+
*/
|
|
71
|
+
export const spawnDevServer = async (options) => {
|
|
72
|
+
const { cwd, port, hostname = '127.0.0.1', coverage, test = true, env, onOutput = (text) => process.stdout.write(text), } = options;
|
|
73
|
+
await assertPortFree(port, hostname);
|
|
74
|
+
const args = ['pikku', 'dev', '--port', String(port)];
|
|
75
|
+
if (coverage)
|
|
76
|
+
args.push('--coverage');
|
|
77
|
+
if (test)
|
|
78
|
+
args.push('--test');
|
|
79
|
+
const child = spawn('npx', args, {
|
|
80
|
+
cwd,
|
|
81
|
+
env: { ...process.env, ...env },
|
|
82
|
+
stdio: 'pipe',
|
|
83
|
+
detached: true,
|
|
84
|
+
});
|
|
85
|
+
const barrier = createReadyBarrier('Server');
|
|
86
|
+
const forward = (chunk) => {
|
|
87
|
+
const text = chunk.toString();
|
|
88
|
+
barrier.observe(text);
|
|
89
|
+
onOutput(text
|
|
90
|
+
.split('\n')
|
|
91
|
+
.map((line) => (line ? `[server] ${line}` : line))
|
|
92
|
+
.join('\n'));
|
|
93
|
+
};
|
|
94
|
+
child.stdout?.on('data', forward);
|
|
95
|
+
child.stderr?.on('data', forward);
|
|
96
|
+
child.on('exit', (code) => barrier.markExited(code));
|
|
97
|
+
return {
|
|
98
|
+
waitUntilReady: barrier.wait,
|
|
99
|
+
stop: () => {
|
|
100
|
+
child.stdout?.destroy();
|
|
101
|
+
child.stderr?.destroy();
|
|
102
|
+
if (child.pid) {
|
|
103
|
+
try {
|
|
104
|
+
process.kill(-child.pid, 'SIGTERM');
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// Process group may already be gone
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
};
|
package/dist/src/services.js
CHANGED
|
@@ -149,7 +149,14 @@ export const createSingletonServices = async (config) => {
|
|
|
149
149
|
let unfilteredStateIsSetupOnly = false;
|
|
150
150
|
let inspectedTsGeneration;
|
|
151
151
|
let inspectorInvalidated = false;
|
|
152
|
-
|
|
152
|
+
/**
|
|
153
|
+
* `unfiltered` is for commands that RUN the project rather than generate from
|
|
154
|
+
* it. The CLI filters narrow what gets written out, which is meaningless to a
|
|
155
|
+
* runner and actively wrong for one: `--tags` on `pikku scenario run` selects
|
|
156
|
+
* which scenarios to run, and if it also narrowed the state the runner would
|
|
157
|
+
* lose the very step functions it is about to call.
|
|
158
|
+
*/
|
|
159
|
+
const getInspectorState = async (refresh = false, setupOnly = false, bootstrapMode = false, unfiltered = false) => {
|
|
153
160
|
// In bootstrap mode, return a minimal "zero state" with core types
|
|
154
161
|
// This allows bootstrap to run immediately without inspecting the codebase
|
|
155
162
|
if (bootstrapMode) {
|
|
@@ -240,6 +247,7 @@ export const createSingletonServices = async (config) => {
|
|
|
240
247
|
rootDir,
|
|
241
248
|
isAddon: !!config.addon,
|
|
242
249
|
oldProgram,
|
|
250
|
+
tsconfig: config.tsconfig,
|
|
243
251
|
types: {
|
|
244
252
|
configFileType: config.configFile,
|
|
245
253
|
userSessionType: config.userSessionType,
|
|
@@ -287,6 +295,9 @@ export const createSingletonServices = async (config) => {
|
|
|
287
295
|
}
|
|
288
296
|
}
|
|
289
297
|
}
|
|
298
|
+
if (unfiltered) {
|
|
299
|
+
return unfilteredState;
|
|
300
|
+
}
|
|
290
301
|
// Apply filters as a post-processing step
|
|
291
302
|
const filteredState = filterInspectorState(unfilteredState, filters, logger);
|
|
292
303
|
return filteredState;
|
|
@@ -19,7 +19,7 @@ const UNWRAP = {
|
|
|
19
19
|
email: 'templates',
|
|
20
20
|
agent: 'agentsMeta',
|
|
21
21
|
};
|
|
22
|
-
// Workflows (incl.
|
|
22
|
+
// Workflows (incl. scenarios) are written one file per workflow under
|
|
23
23
|
// this dir; the file basename (sans `.gen.json`) is the id, `-verbose` excluded.
|
|
24
24
|
const WORKFLOW_META_DIR = 'workflow/meta';
|
|
25
25
|
function readJson(path) {
|
|
@@ -40,7 +40,10 @@ function readSingle(outDir, category, relPath) {
|
|
|
40
40
|
const unwrapKey = UNWRAP[category];
|
|
41
41
|
if (unwrapKey) {
|
|
42
42
|
const inner = raw[unwrapKey];
|
|
43
|
-
raw =
|
|
43
|
+
raw =
|
|
44
|
+
inner && typeof inner === 'object' && !Array.isArray(inner)
|
|
45
|
+
? inner
|
|
46
|
+
: {};
|
|
44
47
|
}
|
|
45
48
|
if (category !== 'http')
|
|
46
49
|
return raw;
|
|
@@ -341,6 +341,9 @@ const _getPikkuCLIConfig = async (logger, configFile = undefined, requiredFields
|
|
|
341
341
|
if (!result.workflowMapDeclarationFile) {
|
|
342
342
|
result.workflowMapDeclarationFile = join(workflowDir, 'pikku-workflow-map.gen.d.ts');
|
|
343
343
|
}
|
|
344
|
+
if (!result.scenarioStepMapDeclarationFile) {
|
|
345
|
+
result.scenarioStepMapDeclarationFile = join(workflowDir, 'pikku-scenario-step-map.gen.d.ts');
|
|
346
|
+
}
|
|
344
347
|
if (!result.workflowTypesFile) {
|
|
345
348
|
result.workflowTypesFile = join(workflowDir, 'pikku-workflow-types.gen.ts');
|
|
346
349
|
}
|
|
@@ -351,6 +354,29 @@ const _getPikkuCLIConfig = async (logger, configFile = undefined, requiredFields
|
|
|
351
354
|
if (!result.scenarioActorsFile) {
|
|
352
355
|
result.scenarioActorsFile = join(workflowDir, 'pikku-scenario-actors.gen.ts');
|
|
353
356
|
}
|
|
357
|
+
// Scenarios
|
|
358
|
+
const scenarioDir = join(result.outDir, 'scenarios');
|
|
359
|
+
if (!result.scenarioStepsFile) {
|
|
360
|
+
result.scenarioStepsFile = join(scenarioDir, 'pikku-scenario-functions.gen.ts');
|
|
361
|
+
}
|
|
362
|
+
if (!result.scenarioStepsMetaFile) {
|
|
363
|
+
result.scenarioStepsMetaFile = join(scenarioDir, 'pikku-scenario-functions-meta.gen.ts');
|
|
364
|
+
}
|
|
365
|
+
if (!result.scenarioStepsMetaJsonFile) {
|
|
366
|
+
result.scenarioStepsMetaJsonFile = join(scenarioDir, 'pikku-scenario-functions-meta.gen.json');
|
|
367
|
+
}
|
|
368
|
+
if (!result.scenarioWiringsFile) {
|
|
369
|
+
result.scenarioWiringsFile = join(scenarioDir, 'pikku-scenario-wirings.gen.ts');
|
|
370
|
+
}
|
|
371
|
+
if (!result.scenarioWiringsMetaFile) {
|
|
372
|
+
result.scenarioWiringsMetaFile = join(scenarioDir, 'pikku-scenario-wirings-meta.gen.ts');
|
|
373
|
+
}
|
|
374
|
+
if (!result.scenarioMetaDir) {
|
|
375
|
+
result.scenarioMetaDir = join(scenarioDir, 'meta');
|
|
376
|
+
}
|
|
377
|
+
if (!result.scenarioBootstrapFile) {
|
|
378
|
+
result.scenarioBootstrapFile = join(result.outDir, 'pikku-bootstrap-scenarios.gen.ts');
|
|
379
|
+
}
|
|
354
380
|
// Services
|
|
355
381
|
if (!result.servicesFile) {
|
|
356
382
|
result.servicesFile = join(result.outDir, 'pikku-services.gen.ts');
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { PikkuCLIConfig } from '../../types/config.js';
|
|
2
|
+
type ScenariosConfig = NonNullable<PikkuCLIConfig['scenarios']>;
|
|
3
|
+
type ActorsConfig = NonNullable<ScenariosConfig['actors']>;
|
|
4
|
+
type ActorConfig = ActorsConfig[string];
|
|
5
|
+
/**
|
|
6
|
+
* The actor registry a run actually sees: every declared actor, plus one
|
|
7
|
+
* materialised actor for each persona nobody declared an actor for.
|
|
8
|
+
*
|
|
9
|
+
* A persona is the KIND of person (its `description`); an actor is one BODY
|
|
10
|
+
* that signs in, with an email of its own. Most apps want exactly one body per
|
|
11
|
+
* kind, so declaring the persona is enough — this fills in the actor. You only
|
|
12
|
+
* write an actor by hand when a scenario needs TWO of the same kind, which is
|
|
13
|
+
* what tenant-isolation and peer-sharing scenarios are made of.
|
|
14
|
+
*
|
|
15
|
+
* Every consumer must resolve through here rather than reading
|
|
16
|
+
* `config.scenarios.actors` directly: codegen emits `scenarioActorConfigs` from
|
|
17
|
+
* it (and `ScenarioActorName` is a `keyof` over that literal, so an actor missing
|
|
18
|
+
* at codegen time can never be referenced in a typed step), while `scenario run`
|
|
19
|
+
* builds the HTTP and Playwright actor maps from it. Those were already three
|
|
20
|
+
* separate reads of the same field before personas existed.
|
|
21
|
+
*/
|
|
22
|
+
export declare const resolveScenarioActors: (scenarios: ScenariosConfig | undefined) => ActorsConfig;
|
|
23
|
+
export type { ActorConfig as ResolvedScenarioActorConfig };
|