@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
|
@@ -4,19 +4,22 @@ import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-
|
|
|
4
4
|
import { serializeFunctionImports } from './serialize-function-imports.js';
|
|
5
5
|
import { getFileImportRelativePath } from '../../../utils/file-import-path.js';
|
|
6
6
|
import { stripVerboseFields, hasVerboseFields, reattachFunctionServices, } from '../../../utils/strip-verbose-meta.js';
|
|
7
|
+
import { partitionScenarioFunctions, partitionScenarioFunctionsMeta, } from '../scenarios/scenario-partition.js';
|
|
8
|
+
import { serializeScenarioFunctionMeta } from '../scenarios/serialize-scenario-meta.js';
|
|
7
9
|
export const pikkuFunctions = pikkuSessionlessFunc({
|
|
8
10
|
func: async ({ logger, config, getInspectorState }) => {
|
|
9
11
|
const { functions, rpc } = await getInspectorState();
|
|
10
|
-
const { functionsMetaFile, functionsMetaJsonFile, functionsFile, packageMappings, schema, } = config;
|
|
11
|
-
|
|
12
|
+
const { functionsMetaFile, functionsMetaJsonFile, functionsFile, scenarioStepsFile, scenarioStepsMetaFile, scenarioStepsMetaJsonFile, packageMappings, schema, } = config;
|
|
13
|
+
const { app: appFunctionsMeta, scenario: scenarioFunctionsMeta } = partitionScenarioFunctionsMeta(functions.meta);
|
|
14
|
+
let minimalMeta = stripVerboseFields(appFunctionsMeta);
|
|
12
15
|
if (config.addonName) {
|
|
13
|
-
minimalMeta = reattachFunctionServices(minimalMeta,
|
|
16
|
+
minimalMeta = reattachFunctionServices(minimalMeta, appFunctionsMeta);
|
|
14
17
|
}
|
|
15
18
|
await writeFileInDir(logger, functionsMetaJsonFile, JSON.stringify(minimalMeta, null, 2));
|
|
16
19
|
// Write verbose JSON only if it has additional fields
|
|
17
|
-
if (hasVerboseFields(
|
|
20
|
+
if (hasVerboseFields(appFunctionsMeta)) {
|
|
18
21
|
const verbosePath = functionsMetaJsonFile.replace(/\.gen\.json$/, '-verbose.gen.json');
|
|
19
|
-
await writeFileInDir(logger, verbosePath, JSON.stringify(
|
|
22
|
+
await writeFileInDir(logger, verbosePath, JSON.stringify(appFunctionsMeta, null, 2));
|
|
20
23
|
}
|
|
21
24
|
const jsonImportPath = getFileImportRelativePath(functionsMetaFile, functionsMetaJsonFile, packageMappings);
|
|
22
25
|
const supportsImportAttributes = schema?.supportsImportAttributes ?? false;
|
|
@@ -31,11 +34,26 @@ export const pikkuFunctions = pikkuSessionlessFunc({
|
|
|
31
34
|
const hasRPCs = rpc.exposedFiles.size > 0 || rpc.internalFiles.size > 0;
|
|
32
35
|
const hasFunctions = functions.files.size > 0;
|
|
33
36
|
const shouldGenerateFunctionsFile = isAddon ? hasFunctions : hasRPCs;
|
|
37
|
+
// For addon packages, use all function files; for main packages, use internal RPC files
|
|
38
|
+
const filesToRegister = isAddon ? functions.files : rpc.internalFiles;
|
|
39
|
+
const { app: appFiles, scenario: scenarioFiles } = partitionScenarioFunctions(filesToRegister, functions.meta);
|
|
34
40
|
if (shouldGenerateFunctionsFile) {
|
|
35
|
-
|
|
36
|
-
const filesToRegister = isAddon ? functions.files : rpc.internalFiles;
|
|
37
|
-
await writeFileInDir(logger, functionsFile, serializeFunctionImports(functionsFile, filesToRegister, functions.meta, packageMappings, config.addonName));
|
|
41
|
+
await writeFileInDir(logger, functionsFile, serializeFunctionImports(functionsFile, appFiles, appFunctionsMeta, packageMappings, config.addonName));
|
|
38
42
|
}
|
|
43
|
+
// Always written, even when empty: a project that deletes its last scenario
|
|
44
|
+
// must stop registering the one it had, and the scenario bootstrap imports
|
|
45
|
+
// these unconditionally.
|
|
46
|
+
await writeFileInDir(logger, scenarioStepsMetaJsonFile, JSON.stringify(stripVerboseFields(scenarioFunctionsMeta), null, 2));
|
|
47
|
+
// The bootstrap imports the stripped copy; the verbose one is read off disk
|
|
48
|
+
// by whatever documents a scenario — the console needs `sourceFile` and
|
|
49
|
+
// `exportedName` to show the code a step runs.
|
|
50
|
+
if (hasVerboseFields(scenarioFunctionsMeta)) {
|
|
51
|
+
await writeFileInDir(logger, scenarioStepsMetaJsonFile.replace(/\.gen\.json$/, '-verbose.gen.json'), JSON.stringify(scenarioFunctionsMeta, null, 2));
|
|
52
|
+
}
|
|
53
|
+
await writeFileInDir(logger, scenarioStepsMetaFile, serializeScenarioFunctionMeta(getFileImportRelativePath(scenarioStepsMetaFile, scenarioStepsMetaJsonFile, packageMappings), getFileImportRelativePath(scenarioStepsMetaFile, functionsMetaFile, packageMappings), supportsImportAttributes, config.addonName));
|
|
54
|
+
await writeFileInDir(logger, scenarioStepsFile, scenarioFiles.size === 0
|
|
55
|
+
? 'export {}'
|
|
56
|
+
: serializeFunctionImports(scenarioStepsFile, scenarioFiles, scenarioFunctionsMeta, packageMappings, config.addonName));
|
|
39
57
|
return shouldGenerateFunctionsFile;
|
|
40
58
|
},
|
|
41
59
|
middleware: [
|
|
@@ -7,7 +7,7 @@ export const serializeFunctionTypes = (userSessionTypeImport, userSessionTypeNam
|
|
|
7
7
|
? nodeCategories.map((c) => `'${c}'`).join(' | ')
|
|
8
8
|
: 'string';
|
|
9
9
|
const workflowImport = workflowTypesImport ||
|
|
10
|
-
`import type { TypedWorkflow, TypedScenario } from '../workflow/pikku-workflow-types.gen.js'`;
|
|
10
|
+
`import type { TypedWorkflow, TypedScenario, TypedScenarioActors } from '../workflow/pikku-workflow-types.gen.js'`;
|
|
11
11
|
// Falls back to `string` when a project has no scopes codegen, so that
|
|
12
12
|
// `scopes` stays usable rather than resolving to an unbound type name.
|
|
13
13
|
const scopesImport = scopesTypeImport || `type ScopeId = string`;
|
|
@@ -299,7 +299,7 @@ export type PikkuFunctionSessionless<
|
|
|
299
299
|
Out,
|
|
300
300
|
RequiredServices,
|
|
301
301
|
Session,
|
|
302
|
-
PickRequired<PikkuWire<In, Out, false, Session, TypedPikkuRPC, null, any, TypedWorkflow, unknown, TypedScenario>, RequiredWires>
|
|
302
|
+
PickRequired<PikkuWire<In, Out, false, Session, TypedPikkuRPC, null, any, TypedWorkflow, unknown, TypedScenario, TypedScenarioActors>, RequiredWires>
|
|
303
303
|
>
|
|
304
304
|
|
|
305
305
|
/**
|
|
@@ -320,7 +320,7 @@ export type PikkuFunction<
|
|
|
320
320
|
Out,
|
|
321
321
|
RequiredServices,
|
|
322
322
|
Session,
|
|
323
|
-
PickRequired<PikkuWire<In, Out, true, Session, TypedPikkuRPC, null, any, TypedWorkflow, unknown, TypedScenario>, RequiredWires>
|
|
323
|
+
PickRequired<PikkuWire<In, Out, true, Session, TypedPikkuRPC, null, any, TypedWorkflow, unknown, TypedScenario, TypedScenarioActors>, RequiredWires>
|
|
324
324
|
>
|
|
325
325
|
|
|
326
326
|
/**
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { FunctionsMeta } from '@pikku/core';
|
|
2
|
+
import type { SerializedWorkflowGraphs } from '@pikku/inspector/workflow-graph';
|
|
3
|
+
export type WiringFileMap = Map<string, {
|
|
4
|
+
path: string;
|
|
5
|
+
exportedName: string;
|
|
6
|
+
}>;
|
|
7
|
+
/**
|
|
8
|
+
* Scenario steps and scenarios are ordinary pikku functions and workflows, so
|
|
9
|
+
* without a split they register into the same bootstrap a deployed server
|
|
10
|
+
* imports — dragging every step body (and whatever it imports: Playwright, test
|
|
11
|
+
* fixtures, assertion helpers) into production. These partitions are what keep
|
|
12
|
+
* them in `.pikku/scenarios/`, reachable only from `pikku scenario run`.
|
|
13
|
+
*/
|
|
14
|
+
export declare const isScenarioStep: (meta: FunctionsMeta[string] | undefined) => boolean;
|
|
15
|
+
export declare const partitionScenarioFunctions: (files: WiringFileMap, functionsMeta: FunctionsMeta) => {
|
|
16
|
+
app: WiringFileMap;
|
|
17
|
+
scenario: WiringFileMap;
|
|
18
|
+
};
|
|
19
|
+
export declare const partitionScenarioFunctionsMeta: (functionsMeta: FunctionsMeta) => {
|
|
20
|
+
app: FunctionsMeta;
|
|
21
|
+
scenario: FunctionsMeta;
|
|
22
|
+
};
|
|
23
|
+
export declare const isScenarioWorkflow: (graphMeta: SerializedWorkflowGraphs, name: string) => boolean;
|
|
24
|
+
export declare const partitionScenarioWorkflows: (workflowNames: string[], workflowFiles: WiringFileMap, graphMeta: SerializedWorkflowGraphs) => {
|
|
25
|
+
appNames: string[];
|
|
26
|
+
scenarioNames: string[];
|
|
27
|
+
appFiles: WiringFileMap;
|
|
28
|
+
scenarioFiles: WiringFileMap;
|
|
29
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scenario steps and scenarios are ordinary pikku functions and workflows, so
|
|
3
|
+
* without a split they register into the same bootstrap a deployed server
|
|
4
|
+
* imports — dragging every step body (and whatever it imports: Playwright, test
|
|
5
|
+
* fixtures, assertion helpers) into production. These partitions are what keep
|
|
6
|
+
* them in `.pikku/scenarios/`, reachable only from `pikku scenario run`.
|
|
7
|
+
*/
|
|
8
|
+
export const isScenarioStep = (meta) => meta?.scenarioStep === true;
|
|
9
|
+
export const partitionScenarioFunctions = (files, functionsMeta) => {
|
|
10
|
+
const app = new Map();
|
|
11
|
+
const scenario = new Map();
|
|
12
|
+
for (const [name, entry] of files) {
|
|
13
|
+
if (isScenarioStep(functionsMeta[name])) {
|
|
14
|
+
scenario.set(name, entry);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
app.set(name, entry);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return { app, scenario };
|
|
21
|
+
};
|
|
22
|
+
export const partitionScenarioFunctionsMeta = (functionsMeta) => {
|
|
23
|
+
const app = {};
|
|
24
|
+
const scenario = {};
|
|
25
|
+
for (const [name, meta] of Object.entries(functionsMeta)) {
|
|
26
|
+
if (isScenarioStep(meta)) {
|
|
27
|
+
scenario[name] = meta;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
app[name] = meta;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { app, scenario };
|
|
34
|
+
};
|
|
35
|
+
export const isScenarioWorkflow = (graphMeta, name) => graphMeta[name]?.source === 'scenario';
|
|
36
|
+
export const partitionScenarioWorkflows = (workflowNames, workflowFiles, graphMeta) => {
|
|
37
|
+
const appNames = [];
|
|
38
|
+
const scenarioNames = [];
|
|
39
|
+
for (const name of workflowNames) {
|
|
40
|
+
if (isScenarioWorkflow(graphMeta, name)) {
|
|
41
|
+
scenarioNames.push(name);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
appNames.push(name);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const appFiles = new Map();
|
|
48
|
+
const scenarioFiles = new Map();
|
|
49
|
+
for (const [name, entry] of workflowFiles) {
|
|
50
|
+
if (isScenarioWorkflow(graphMeta, name)) {
|
|
51
|
+
scenarioFiles.set(name, entry);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
appFiles.set(name, entry);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { appNames, scenarioNames, appFiles, scenarioFiles };
|
|
58
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FeaturesMeta } from '@pikku/core/workflow/types';
|
|
2
|
+
import type { InspectorFeature } from '@pikku/inspector';
|
|
3
|
+
/**
|
|
4
|
+
* The feature meta the console reads, built from what the inspector could read
|
|
5
|
+
* off the source.
|
|
6
|
+
*
|
|
7
|
+
* Entry order is the declared order and is deliberately not sorted — a
|
|
8
|
+
* feature's scenarios read top to bottom the way they were written, the same
|
|
9
|
+
* way a gherkin Feature file does.
|
|
10
|
+
*
|
|
11
|
+
* The source path is dropped: it is absolute to the machine that generated the
|
|
12
|
+
* meta, and nothing rendering a feature needs it.
|
|
13
|
+
*/
|
|
14
|
+
export declare const buildFeaturesMeta: (featureFiles: Map<string, InspectorFeature>) => FeaturesMeta;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The feature meta the console reads, built from what the inspector could read
|
|
3
|
+
* off the source.
|
|
4
|
+
*
|
|
5
|
+
* Entry order is the declared order and is deliberately not sorted — a
|
|
6
|
+
* feature's scenarios read top to bottom the way they were written, the same
|
|
7
|
+
* way a gherkin Feature file does.
|
|
8
|
+
*
|
|
9
|
+
* The source path is dropped: it is absolute to the machine that generated the
|
|
10
|
+
* meta, and nothing rendering a feature needs it.
|
|
11
|
+
*/
|
|
12
|
+
export const buildFeaturesMeta = (featureFiles) => {
|
|
13
|
+
const meta = {};
|
|
14
|
+
for (const [id, feature] of featureFiles) {
|
|
15
|
+
meta[id] = {
|
|
16
|
+
id,
|
|
17
|
+
name: feature.name ?? id,
|
|
18
|
+
...(feature.description ? { description: feature.description } : {}),
|
|
19
|
+
tags: feature.tags ?? [],
|
|
20
|
+
entries: feature.entries.map((entry) => entry.data === undefined
|
|
21
|
+
? { scenario: entry.scenario }
|
|
22
|
+
: { scenario: entry.scenario, data: entry.data }),
|
|
23
|
+
unresolvedEntries: feature.unresolvedEntries,
|
|
24
|
+
hasBefore: feature.hasBefore,
|
|
25
|
+
hasAfter: feature.hasAfter,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return meta;
|
|
29
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scenario workflow meta, merged onto whatever the app registered rather than
|
|
3
|
+
* replacing it — `pikkuState(…, 'workflows', 'meta', value)` is a wholesale
|
|
4
|
+
* setter, and the app's own meta file is one. The app file is imported here so
|
|
5
|
+
* the ordering holds however this module is reached, not only through the
|
|
6
|
+
* generated scenario bootstrap.
|
|
7
|
+
*/
|
|
8
|
+
export declare const serializeScenarioWorkflowMeta: (outputPath: string, metaDir: string, appMetaImportPath: string, scenarioNames: string[], packageMappings: Record<string, string>, supportsImportAttributes: boolean, packageName?: string) => string;
|
|
9
|
+
/**
|
|
10
|
+
* Scenario step meta, merged onto the app's function meta for the same reason.
|
|
11
|
+
*/
|
|
12
|
+
export declare const serializeScenarioFunctionMeta: (jsonImportPath: string, appMetaImportPath: string, supportsImportAttributes: boolean, packageName?: string) => string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { getFileImportRelativePath } from '../../../utils/file-import-path.js';
|
|
2
|
+
import { sanitizeTypeName } from '@pikku/inspector';
|
|
3
|
+
/**
|
|
4
|
+
* Scenario workflow meta, merged onto whatever the app registered rather than
|
|
5
|
+
* replacing it — `pikkuState(…, 'workflows', 'meta', value)` is a wholesale
|
|
6
|
+
* setter, and the app's own meta file is one. The app file is imported here so
|
|
7
|
+
* the ordering holds however this module is reached, not only through the
|
|
8
|
+
* generated scenario bootstrap.
|
|
9
|
+
*/
|
|
10
|
+
export const serializeScenarioWorkflowMeta = (outputPath, metaDir, appMetaImportPath, scenarioNames, packageMappings, supportsImportAttributes, packageName) => {
|
|
11
|
+
const pkg = packageName ? `'${packageName}'` : 'null';
|
|
12
|
+
const sortedNames = [...scenarioNames].sort();
|
|
13
|
+
const imports = sortedNames
|
|
14
|
+
.map((name) => {
|
|
15
|
+
const sanitizedIdentifier = sanitizeTypeName(name);
|
|
16
|
+
const jsonPath = `${metaDir}/${name}.gen.json`;
|
|
17
|
+
const importPath = getFileImportRelativePath(outputPath, jsonPath, packageMappings);
|
|
18
|
+
return supportsImportAttributes
|
|
19
|
+
? `import ${sanitizedIdentifier}Meta from '${importPath}' with { type: 'json' }`
|
|
20
|
+
: `import ${sanitizedIdentifier}Meta from '${importPath}'`;
|
|
21
|
+
})
|
|
22
|
+
.join('\n');
|
|
23
|
+
const metaEntries = sortedNames
|
|
24
|
+
.map((name) => ` '${name}': ${sanitizeTypeName(name)}Meta,`)
|
|
25
|
+
.join('\n');
|
|
26
|
+
return `import { pikkuState } from '@pikku/core/internal'
|
|
27
|
+
import type { WorkflowsRuntimeMeta } from '@pikku/core/workflow/types'
|
|
28
|
+
import '${appMetaImportPath}'
|
|
29
|
+
${imports ? `\n${imports}\n` : ''}
|
|
30
|
+
const scenariosMeta = {
|
|
31
|
+
${metaEntries}
|
|
32
|
+
} as WorkflowsRuntimeMeta
|
|
33
|
+
|
|
34
|
+
pikkuState(${pkg}, 'workflows', 'meta', {
|
|
35
|
+
...pikkuState(${pkg}, 'workflows', 'meta'),
|
|
36
|
+
...scenariosMeta,
|
|
37
|
+
})`;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Scenario step meta, merged onto the app's function meta for the same reason.
|
|
41
|
+
*/
|
|
42
|
+
export const serializeScenarioFunctionMeta = (jsonImportPath, appMetaImportPath, supportsImportAttributes, packageName) => {
|
|
43
|
+
const pkg = packageName ? `'${packageName}'` : 'null';
|
|
44
|
+
const importStatement = supportsImportAttributes
|
|
45
|
+
? `import metaData from '${jsonImportPath}' with { type: 'json' }`
|
|
46
|
+
: `import metaData from '${jsonImportPath}'`;
|
|
47
|
+
return `import { pikkuState } from '@pikku/core/internal'
|
|
48
|
+
import type { FunctionsMeta } from '@pikku/core'
|
|
49
|
+
import '${appMetaImportPath}'
|
|
50
|
+
${importStatement}
|
|
51
|
+
|
|
52
|
+
pikkuState(${pkg}, 'function', 'meta', {
|
|
53
|
+
...pikkuState(${pkg}, 'function', 'meta'),
|
|
54
|
+
...(metaData as FunctionsMeta),
|
|
55
|
+
})`;
|
|
56
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { WiringFileMap } from './scenario-partition.js';
|
|
2
|
+
/**
|
|
3
|
+
* The scenario counterpart of `serializeWorkflowRegistration`: registers the
|
|
4
|
+
* scenarios and features that were held back from the app wirings, so they
|
|
5
|
+
* exist only in the module graph `pikku scenario run` loads.
|
|
6
|
+
*/
|
|
7
|
+
export declare const serializeScenarioRegistration: (outputPath: string, metaImportPath: string, scenarioFiles: WiringFileMap, featureFiles: WiringFileMap, packageMappings: Record<string, string>, packageName?: string) => string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { getFileImportRelativePath } from '../../../utils/file-import-path.js';
|
|
2
|
+
/**
|
|
3
|
+
* The scenario counterpart of `serializeWorkflowRegistration`: registers the
|
|
4
|
+
* scenarios and features that were held back from the app wirings, so they
|
|
5
|
+
* exist only in the module graph `pikku scenario run` loads.
|
|
6
|
+
*/
|
|
7
|
+
export const serializeScenarioRegistration = (outputPath, metaImportPath, scenarioFiles, featureFiles, packageMappings, packageName) => {
|
|
8
|
+
const hasScenarios = scenarioFiles.size > 0;
|
|
9
|
+
const hasFeatures = featureFiles.size > 0;
|
|
10
|
+
if (!hasScenarios && !hasFeatures) {
|
|
11
|
+
return 'export {}';
|
|
12
|
+
}
|
|
13
|
+
const lines = [];
|
|
14
|
+
const registrars = [
|
|
15
|
+
...(hasScenarios ? ['addWorkflow'] : []),
|
|
16
|
+
...(hasFeatures ? ['addFeature'] : []),
|
|
17
|
+
];
|
|
18
|
+
lines.push(`import { ${registrars.join(', ')} } from '@pikku/core/workflow'`);
|
|
19
|
+
lines.push(`import '${metaImportPath}'`);
|
|
20
|
+
const sortedScenarios = Array.from(scenarioFiles.entries()).sort((a, b) => a[0].localeCompare(b[0]));
|
|
21
|
+
// A feature holds the very objects its scenarios were registered with, so it
|
|
22
|
+
// has to be imported for its membership to be resolvable at all.
|
|
23
|
+
const sortedFeatures = Array.from(featureFiles.entries()).sort((a, b) => a[0].localeCompare(b[0]));
|
|
24
|
+
for (const [, { path, exportedName }] of [
|
|
25
|
+
...sortedScenarios,
|
|
26
|
+
...sortedFeatures,
|
|
27
|
+
]) {
|
|
28
|
+
const importPath = getFileImportRelativePath(outputPath, path, packageMappings);
|
|
29
|
+
lines.push(`import { ${exportedName} } from '${importPath}'`);
|
|
30
|
+
}
|
|
31
|
+
lines.push('');
|
|
32
|
+
const packageArg = packageName ? `, '${packageName}'` : '';
|
|
33
|
+
for (const [pikkuFuncId, { exportedName }] of sortedScenarios) {
|
|
34
|
+
lines.push(`addWorkflow('${pikkuFuncId}', ${exportedName}${packageArg})`);
|
|
35
|
+
}
|
|
36
|
+
for (const [featureId, { exportedName }] of sortedFeatures) {
|
|
37
|
+
lines.push(`addFeature('${featureId}', ${exportedName}${packageArg})`);
|
|
38
|
+
}
|
|
39
|
+
return lines.join('\n');
|
|
40
|
+
};
|
|
@@ -4,13 +4,20 @@ import { writeFileInDir } from '../../../utils/file-writer.js';
|
|
|
4
4
|
import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
|
|
5
5
|
import { serializeWorkflowTypes } from './serialize-workflow-types.js';
|
|
6
6
|
import { serializeScenarioActors } from './serialize-scenario-actors.js';
|
|
7
|
+
import { resolveScenarioActors } from '../../../utils/resolve-scenario-actors.js';
|
|
7
8
|
import { serializeWorkflowRegistration } from './serialize-workflow-registration.js';
|
|
8
9
|
import { serializeWorkflowMap } from './serialize-workflow-map.js';
|
|
10
|
+
import { serializeScenarioStepMap } from './serialize-scenario-step-map.js';
|
|
9
11
|
import { serializeWorkflowBootstrapMap } from './serialize-workflow-bootstrap-map.js';
|
|
10
12
|
import { serializeWorkflowMeta } from './serialize-workflow-meta.js';
|
|
13
|
+
import { partitionScenarioWorkflows } from '../scenarios/scenario-partition.js';
|
|
14
|
+
import { serializeScenarioRegistration } from '../scenarios/serialize-scenario-registration.js';
|
|
15
|
+
import { buildFeaturesMeta } from '../scenarios/serialize-feature-meta.js';
|
|
16
|
+
import { serializeScenarioWorkflowMeta } from '../scenarios/serialize-scenario-meta.js';
|
|
11
17
|
import { getFileImportRelativePath } from '../../../utils/file-import-path.js';
|
|
12
18
|
import { stripVerboseFields, hasVerboseFields, } from '../../../utils/strip-verbose-meta.js';
|
|
13
19
|
import { join } from 'path';
|
|
20
|
+
import { rm } from 'fs/promises';
|
|
14
21
|
export const pikkuWorkflow = pikkuSessionlessFunc({
|
|
15
22
|
func: async ({ logger, config, getInspectorState }, input) => {
|
|
16
23
|
const bootstrap = input?.bootstrap === true;
|
|
@@ -28,6 +35,11 @@ export const pikkuWorkflow = pikkuSessionlessFunc({
|
|
|
28
35
|
];
|
|
29
36
|
const hasRelevantWorkflows = allWorkflowNames.length > 0;
|
|
30
37
|
const hasWorkflows = hasRelevantWorkflows;
|
|
38
|
+
// Scenarios and features are held back from every app-facing artifact: they
|
|
39
|
+
// are only ever run by `pikku scenario run`, and registering them in the app
|
|
40
|
+
// bootstrap drags each scenario's steps — and whatever those import — into a
|
|
41
|
+
// deployed server.
|
|
42
|
+
const { appNames, scenarioNames, appFiles, scenarioFiles } = partitionScenarioWorkflows(allWorkflowNames, workflows.files, workflows.graphMeta);
|
|
31
43
|
if (hasWorkflows) {
|
|
32
44
|
const singletonServices = visitState.serviceAggregation.allSingletonServices.length > 0
|
|
33
45
|
? visitState.serviceAggregation.allSingletonServices
|
|
@@ -44,35 +56,58 @@ export const pikkuWorkflow = pikkuSessionlessFunc({
|
|
|
44
56
|
}
|
|
45
57
|
}
|
|
46
58
|
if (hasWorkflows && workflowMetaDir) {
|
|
59
|
+
const scenarioNameSet = new Set(scenarioNames);
|
|
47
60
|
for (const [name, graphMeta] of Object.entries(workflows.graphMeta)) {
|
|
61
|
+
const metaDir = scenarioNameSet.has(name)
|
|
62
|
+
? config.scenarioMetaDir
|
|
63
|
+
: workflowMetaDir;
|
|
48
64
|
const minimalMeta = stripVerboseFields(graphMeta);
|
|
49
|
-
const minimalPath = join(
|
|
65
|
+
const minimalPath = join(metaDir, `${name}.gen.json`);
|
|
50
66
|
await writeFileInDir(logger, minimalPath, JSON.stringify(minimalMeta, null, 2), { ignoreModifyComment: true });
|
|
51
67
|
if (hasVerboseFields(graphMeta)) {
|
|
52
|
-
const verbosePath = join(
|
|
68
|
+
const verbosePath = join(metaDir, `${name}-verbose.gen.json`);
|
|
53
69
|
await writeFileInDir(logger, verbosePath, JSON.stringify(graphMeta, null, 2), { ignoreModifyComment: true });
|
|
54
70
|
}
|
|
55
71
|
}
|
|
56
72
|
}
|
|
73
|
+
// A project generated before scenarios moved out has their meta sitting in
|
|
74
|
+
// workflow/meta, and getWorkflowMeta() reads both directories — leaving it
|
|
75
|
+
// there would serve a stale copy of every scenario forever.
|
|
76
|
+
if (workflowMetaDir) {
|
|
77
|
+
await Promise.all(scenarioNames.flatMap((name) => [`${name}.gen.json`, `${name}-verbose.gen.json`].map((file) => rm(join(workflowMetaDir, file), { force: true }))));
|
|
78
|
+
}
|
|
57
79
|
if (workflowsWiringMetaFile && workflowMetaDir) {
|
|
58
|
-
await writeFileInDir(logger, workflowsWiringMetaFile, serializeWorkflowMeta(workflowsWiringMetaFile, workflowMetaDir,
|
|
80
|
+
await writeFileInDir(logger, workflowsWiringMetaFile, serializeWorkflowMeta(workflowsWiringMetaFile, workflowMetaDir, appNames, packageMappings, schema?.supportsImportAttributes ?? false, config.addonName));
|
|
59
81
|
}
|
|
82
|
+
await writeFileInDir(logger, config.scenarioWiringsMetaFile, serializeScenarioWorkflowMeta(config.scenarioWiringsMetaFile, config.scenarioMetaDir, getFileImportRelativePath(config.scenarioWiringsMetaFile, workflowsWiringMetaFile, packageMappings), scenarioNames, packageMappings, schema?.supportsImportAttributes ?? false, config.addonName));
|
|
83
|
+
await writeFileInDir(logger, config.scenarioWiringsFile, serializeScenarioRegistration(config.scenarioWiringsFile, getFileImportRelativePath(config.scenarioWiringsFile, config.scenarioWiringsMetaFile, packageMappings), scenarioFiles, workflows.featureFiles, packageMappings, config.addonName));
|
|
60
84
|
const metaImportPath = getFileImportRelativePath(workflowsWiringFile, workflowsWiringMetaFile, packageMappings);
|
|
61
|
-
await writeFileInDir(logger, workflowsWiringFile, serializeWorkflowRegistration(workflowsWiringFile, metaImportPath,
|
|
85
|
+
await writeFileInDir(logger, workflowsWiringFile, serializeWorkflowRegistration(workflowsWiringFile, metaImportPath, appNames, appFiles, workflows.graphFiles, packageMappings, config.addonName));
|
|
62
86
|
const functionTypesImportPath = getFileImportRelativePath(workflowTypesFile, functionTypesFile, packageMappings);
|
|
63
87
|
const rpcMapImportPath = getFileImportRelativePath(workflowTypesFile, config.rpcInternalMapDeclarationFile, packageMappings);
|
|
64
88
|
const workflowMapImportPath = getFileImportRelativePath(workflowTypesFile, workflowMapDeclarationFile, packageMappings);
|
|
65
89
|
const agentMapImportPath = getFileImportRelativePath(workflowTypesFile, config.agentMapDeclarationFile, packageMappings);
|
|
66
90
|
const scopesImportPath = getFileImportRelativePath(workflowTypesFile, config.scopesFile, packageMappings);
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
91
|
+
const scenarioStepMapImportPath = getFileImportRelativePath(workflowTypesFile, config.scenarioStepMapDeclarationFile, packageMappings);
|
|
92
|
+
const scenarioActorsImportPath = getFileImportRelativePath(workflowTypesFile, config.scenarioActorsFile, packageMappings);
|
|
93
|
+
await writeFileInDir(logger, workflowTypesFile, serializeWorkflowTypes(functionTypesImportPath, rpcMapImportPath, workflowMapImportPath, agentMapImportPath, scopesImportPath, scenarioStepMapImportPath, scenarioActorsImportPath));
|
|
94
|
+
// Rides the workflow command rather than getting its own codegen node, so
|
|
95
|
+
// it can't drift out of the two hand-maintained ordering lists.
|
|
96
|
+
await writeFileInDir(logger, config.scenarioStepMapDeclarationFile, serializeScenarioStepMap(logger, config.scenarioStepMapDeclarationFile, packageMappings, typesMap, functionState.meta));
|
|
97
|
+
// Written even for an empty registry, so `TypedScenarioActors` is always a
|
|
98
|
+
// resolvable import for the generated function types.
|
|
99
|
+
const scenarioActors = resolveScenarioActors(config.scenarios);
|
|
100
|
+
{
|
|
70
101
|
const agentMapImportPath = getFileImportRelativePath(config.scenarioActorsFile, config.agentMapDeclarationFile, packageMappings);
|
|
71
|
-
|
|
102
|
+
const exposedRpcMapImportPath = getFileImportRelativePath(config.scenarioActorsFile, config.rpcMapDeclarationFile, packageMappings);
|
|
103
|
+
await writeFileInDir(logger, config.scenarioActorsFile, serializeScenarioActors(scenarioActors, agentMapImportPath, exposedRpcMapImportPath));
|
|
72
104
|
// Fixed path getScenarioActorsMeta() reads; kept out of workflow/meta,
|
|
73
105
|
// which getWorkflowMeta() globs as workflows.
|
|
74
106
|
await writeFileInDir(logger, join(config.outDir, 'workflow', 'scenario-actors.gen.json'), JSON.stringify(scenarioActors, null, 2));
|
|
75
107
|
}
|
|
108
|
+
// Fixed path getFeaturesMeta() reads. Written even when empty so the
|
|
109
|
+
// console can tell "no features declared" from "meta not generated yet".
|
|
110
|
+
await writeFileInDir(logger, join(config.outDir, 'scenarios', 'features.gen.json'), JSON.stringify(buildFeaturesMeta(workflows.featureFiles), null, 2));
|
|
76
111
|
await writeFileInDir(logger, workflowMapDeclarationFile, bootstrap
|
|
77
112
|
? serializeWorkflowBootstrapMap(workflows.meta, workflows.graphMeta)
|
|
78
113
|
: serializeWorkflowMap(logger, workflowMapDeclarationFile, packageMappings, typesMap, functionState.meta, workflows.meta, workflows.graphMeta, visitState.rpc?.wireAddonDeclarations));
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { PikkuCLIConfig } from '../../../../types/config.js';
|
|
2
|
-
export declare const serializeScenarioActors: (actors: NonNullable<NonNullable<PikkuCLIConfig["scenarios"]>["actors"]>, agentMapImportPath: string) => string;
|
|
2
|
+
export declare const serializeScenarioActors: (actors: NonNullable<NonNullable<PikkuCLIConfig["scenarios"]>["actors"]>, agentMapImportPath: string, rpcMapImportPath: string) => string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const serializeScenarioActors = (actors, agentMapImportPath) => {
|
|
1
|
+
export const serializeScenarioActors = (actors, agentMapImportPath, rpcMapImportPath) => {
|
|
2
2
|
return `/** Scenario actors declared in pikku.config.json (\`scenarios.actors\`) */
|
|
3
3
|
import {
|
|
4
4
|
createHttpScenarioActors,
|
|
@@ -7,15 +7,26 @@ import {
|
|
|
7
7
|
type ScenarioActorConfig,
|
|
8
8
|
} from '@pikku/core/services'
|
|
9
9
|
import type { AgentMap } from '${agentMapImportPath}'
|
|
10
|
+
import type { FlattenedRPCMap } from '${rpcMapImportPath}'
|
|
10
11
|
|
|
11
12
|
export const scenarioActorConfigs = ${JSON.stringify(actors, null, 2)} as const satisfies Record<string, ScenarioActorConfig>
|
|
12
13
|
|
|
13
14
|
export type ScenarioActorName = keyof typeof scenarioActorConfigs
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* The same registry as a list, widened to \`ScenarioActorConfig\`. Iterating
|
|
18
|
+
* \`scenarioActorConfigs\` directly yields the literal type of each entry, on
|
|
19
|
+
* which an optional field an actor did not declare does not exist — so a seed
|
|
20
|
+
* reading \`scopes\`/\`roles\` off every actor needs this instead.
|
|
21
|
+
*/
|
|
22
|
+
export const scenarioActorList: ScenarioActorConfig[] = Object.values(
|
|
23
|
+
scenarioActorConfigs
|
|
24
|
+
)
|
|
25
|
+
|
|
15
26
|
export type AgentName = keyof AgentMap & string
|
|
16
27
|
export type TypedScenarioActors = Record<
|
|
17
28
|
ScenarioActorName,
|
|
18
|
-
ScenarioActor<AgentName>
|
|
29
|
+
ScenarioActor<AgentName, FlattenedRPCMap>
|
|
19
30
|
>
|
|
20
31
|
|
|
21
32
|
export const createScenarioActors = (
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type TypesMap } from '@pikku/inspector';
|
|
2
|
+
import type { FunctionsMeta } from '@pikku/core';
|
|
3
|
+
import type { Logger } from '@pikku/core/services';
|
|
4
|
+
export declare const serializeScenarioStepMap: (logger: Logger, relativeToPath: string, packageMappings: Record<string, string>, typesMap: TypesMap, functionsMeta: FunctionsMeta) => string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate the scenario step map: the typed set of names a scenario may pass to
|
|
3
|
+
* `scenario.step/given/when/then`.
|
|
4
|
+
*
|
|
5
|
+
* Steps are referenced by string, exactly like `workflow.do` references an RPC,
|
|
6
|
+
* so type safety has to come from a generated map rather than from importing
|
|
7
|
+
* the step's const. This is what makes `scenario.given('…', 'buysAnApple', …)`
|
|
8
|
+
* autocomplete and type-check its data.
|
|
9
|
+
*/
|
|
10
|
+
import { serializeImportMap } from '../../../utils/serialize-import-map.js';
|
|
11
|
+
import { generateCustomTypes } from '@pikku/inspector';
|
|
12
|
+
const SCENARIO_STEP_WRAPPER = 'pikkuScenarioStep';
|
|
13
|
+
export const serializeScenarioStepMap = (logger, relativeToPath, packageMappings, typesMap, functionsMeta) => {
|
|
14
|
+
const requiredTypes = new Set();
|
|
15
|
+
const steps = Object.entries(functionsMeta).filter(([, meta]) => meta.funcWrapper === SCENARIO_STEP_WRAPPER);
|
|
16
|
+
const resolveType = (name) => {
|
|
17
|
+
if (!name) {
|
|
18
|
+
return 'void';
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
return typesMap.getTypeMeta(name).uniqueName;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return name;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
let stepsStr = 'export type FlattenedScenarioStepMap = {\n';
|
|
28
|
+
for (const [stepName, meta] of steps) {
|
|
29
|
+
const inputType = resolveType(meta.inputs?.[0]);
|
|
30
|
+
const outputType = resolveType(meta.outputs?.[0]);
|
|
31
|
+
requiredTypes.add(inputType);
|
|
32
|
+
requiredTypes.add(outputType);
|
|
33
|
+
stepsStr += ` readonly '${stepName}': ScenarioStepHandler<${inputType}, ${outputType}>,\n`;
|
|
34
|
+
}
|
|
35
|
+
stepsStr += '};';
|
|
36
|
+
const hasAny = steps.length > 0;
|
|
37
|
+
const serializedCustomTypes = hasAny
|
|
38
|
+
? generateCustomTypes(typesMap, requiredTypes)
|
|
39
|
+
: '';
|
|
40
|
+
const serializedImportMap = hasAny
|
|
41
|
+
? serializeImportMap(logger, relativeToPath, packageMappings, typesMap, requiredTypes)
|
|
42
|
+
: '';
|
|
43
|
+
const serializedCustomTypesDeclarationsOnly = serializedCustomTypes
|
|
44
|
+
.split('\n')
|
|
45
|
+
.filter((line) => !line.startsWith('import '))
|
|
46
|
+
.join('\n');
|
|
47
|
+
return `/**
|
|
48
|
+
* Scenario step map: the names \`scenario.step/given/when/then\` accepts, with
|
|
49
|
+
* the input and output types of each step.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
${serializedImportMap}
|
|
53
|
+
${serializedCustomTypesDeclarationsOnly}
|
|
54
|
+
|
|
55
|
+
interface ScenarioStepHandler<I, O> {
|
|
56
|
+
input: I;
|
|
57
|
+
output: O;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
${stepsStr}
|
|
61
|
+
`;
|
|
62
|
+
};
|
|
@@ -1,16 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A workflow or graph node name is free-form prose written by a human, so an
|
|
3
|
+
* apostrophe or a backslash in it is ordinary. `JSON.stringify` emits a valid,
|
|
4
|
+
* escaped TypeScript string literal; interpolating the raw name into a quoted
|
|
5
|
+
* key terminates the string and the whole `.d.ts` stops parsing.
|
|
6
|
+
*/
|
|
7
|
+
const key = (name) => JSON.stringify(name);
|
|
1
8
|
export const serializeWorkflowBootstrapMap = (workflowsMeta, graphMeta) => {
|
|
2
9
|
const workflowEntries = Object.keys(workflowsMeta)
|
|
3
10
|
.sort()
|
|
4
|
-
.map((workflowName) => ` readonly
|
|
11
|
+
.map((workflowName) => ` readonly ${key(workflowName)}: WorkflowHandler<unknown, unknown>,`)
|
|
5
12
|
.join('\n');
|
|
6
13
|
const graphEntries = Object.entries(graphMeta)
|
|
7
14
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
8
15
|
.map(([graphName, graph]) => {
|
|
9
16
|
const nodeEntries = Object.keys(graph.nodes)
|
|
10
17
|
.sort()
|
|
11
|
-
.map((nodeId) => ` readonly
|
|
18
|
+
.map((nodeId) => ` readonly ${key(nodeId)}: GraphNodeHandler<unknown>,`)
|
|
12
19
|
.join('\n');
|
|
13
|
-
return ` readonly
|
|
20
|
+
return ` readonly ${key(graphName)}: {\n${nodeEntries}\n },`;
|
|
14
21
|
})
|
|
15
22
|
.join('\n');
|
|
16
23
|
return `/**
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { serializeImportMap } from '../../../utils/serialize-import-map.js';
|
|
2
2
|
import { generateCustomTypes } from '@pikku/inspector';
|
|
3
3
|
import { parseVersionedId } from '@pikku/core';
|
|
4
|
+
/**
|
|
5
|
+
* A workflow or graph node name is free-form prose written by a human, so an
|
|
6
|
+
* apostrophe or a backslash in it is ordinary. `JSON.stringify` emits a valid,
|
|
7
|
+
* escaped TypeScript string literal; interpolating the raw name into a quoted
|
|
8
|
+
* key terminates the string and the whole `.d.ts` stops parsing.
|
|
9
|
+
*/
|
|
10
|
+
const key = (name) => JSON.stringify(name);
|
|
4
11
|
export const serializeWorkflowMap = (logger, relativeToPath, packageMappings, typesMap, functionsMeta, workflowsMeta, graphMeta, wireAddonDeclarations) => {
|
|
5
12
|
const requiredTypes = new Set();
|
|
6
13
|
const serializedWorkflows = generateWorkflows(workflowsMeta, functionsMeta, typesMap, requiredTypes);
|
|
@@ -84,7 +91,7 @@ function generateWorkflows(workflowsMeta, functionsMeta, typesMap, requiredTypes
|
|
|
84
91
|
}
|
|
85
92
|
let workflowsStr = 'export type WorkflowMap = {\n';
|
|
86
93
|
for (const [workflowName, handler] of Object.entries(workflowsObj)) {
|
|
87
|
-
workflowsStr += ` readonly
|
|
94
|
+
workflowsStr += ` readonly ${key(workflowName)}: WorkflowHandler<${handler.inputType}, ${handler.outputType}>,\n`;
|
|
88
95
|
}
|
|
89
96
|
workflowsStr += '};';
|
|
90
97
|
return workflowsStr;
|
|
@@ -121,9 +128,9 @@ function generateGraphs(graphMeta, functionsMeta, typesMap, requiredTypes) {
|
|
|
121
128
|
}
|
|
122
129
|
let graphsStr = 'export type GraphsMap = {\n';
|
|
123
130
|
for (const [graphName, nodes] of Object.entries(graphsObj)) {
|
|
124
|
-
graphsStr += ` readonly
|
|
131
|
+
graphsStr += ` readonly ${key(graphName)}: {\n`;
|
|
125
132
|
for (const [nodeId, handler] of Object.entries(nodes)) {
|
|
126
|
-
graphsStr += ` readonly
|
|
133
|
+
graphsStr += ` readonly ${key(nodeId)}: GraphNodeHandler<${handler.inputType}>,\n`;
|
|
127
134
|
}
|
|
128
135
|
graphsStr += ' },\n';
|
|
129
136
|
}
|