@pikku/cli 0.12.93 → 0.12.96
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/console-app/assets/{index-CmAplHjZ.js → index-CeQodYfg.js} +149 -149
- package/console-app/assets/{index-BYj7ZbUU.css → index-CyYHTGWZ.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.d.ts +13 -15
- package/dist/.pikku/cli/pikku-cli-channel.js +62 -3
- package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +14 -4
- package/dist/.pikku/cli/pikku-cli-client.gen.js +20 -10
- 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-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 +129 -2
- 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 +55 -20
- package/dist/.pikku/function/pikku-function-types.gen.js +27 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.json +138 -13
- package/dist/.pikku/function/pikku-functions.gen.js +11 -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 +1 -1
- package/dist/.pikku/pikku-bootstrap-scenarios.gen.js +1 -1
- 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 +7 -0
- package/dist/.pikku/scenarios/pikku-scenario-functions-meta.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-functions.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-wirings-meta.gen.js +1 -1
- package/dist/.pikku/scenarios/pikku-scenario-wirings.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/schemas/register.gen.d.ts +1 -1
- package/dist/.pikku/scenarios/schemas/register.gen.js +1 -1
- 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 +13 -3
- package/dist/.pikku/schemas/schemas/PersonaRunInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/PersonaSyncInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/PikkuPersonasInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/PikkuRolesInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/RolesAuditInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/RolesPruneInput.schema.json +1 -0
- package/dist/.pikku/scopes/pikku-personas-meta.gen.json +1 -0
- package/dist/.pikku/scopes/pikku-personas.gen.d.ts +36 -0
- package/dist/.pikku/scopes/pikku-personas.gen.js +17 -0
- package/dist/.pikku/scopes/pikku-roles-meta.gen.json +1 -0
- package/dist/.pikku/scopes/pikku-roles.gen.d.ts +30 -0
- package/dist/.pikku/scopes/pikku-roles.gen.js +18 -0
- package/dist/.pikku/scopes/pikku-scope-types.gen.d.ts +4 -2
- package/dist/.pikku/scopes/pikku-scope-types.gen.js +3 -2
- package/dist/.pikku/scopes/pikku-scopes.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +2 -2
- package/dist/.pikku/secrets/pikku-secret-types.gen.js +2 -2
- 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 +2 -2
- package/dist/.pikku/variables/pikku-variable-types.gen.js +2 -2
- 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/meta/allWorkflow.gen.json +27 -3
- package/dist/.pikku/workflow/pikku-personas.gen.d.ts +15 -0
- package/dist/.pikku/workflow/pikku-personas.gen.js +17 -0
- package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +96 -19
- package/dist/.pikku/workflow/pikku-workflow-types.gen.js +22 -2
- 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/bin/pikku.js +0 -0
- package/dist/src/cli.wiring.js +101 -2
- package/dist/src/deploy/analyzer/analyzer.js +14 -0
- package/dist/src/deploy/analyzer/manifest.d.ts +11 -0
- package/dist/src/fabric/functions/validate.function.js +4 -4
- package/dist/src/functions/commands/bootstrap.js +3 -0
- package/dist/src/functions/commands/dev-ai-runner.d.ts +16 -4
- package/dist/src/functions/commands/dev-ai-runner.js +63 -29
- package/dist/src/functions/commands/dev.js +9 -4
- package/dist/src/functions/commands/enable.js +15 -19
- package/dist/src/functions/commands/{scenario-environment.d.ts → environment.d.ts} +9 -6
- package/dist/src/functions/commands/{scenario-environment.js → environment.js} +5 -4
- package/dist/src/functions/commands/new-addon.js +12 -12
- package/dist/src/functions/commands/persona-sync.d.ts +33 -0
- package/dist/src/functions/commands/persona-sync.js +151 -0
- package/dist/src/functions/commands/persona.d.ts +58 -0
- package/dist/src/functions/commands/persona.js +217 -0
- package/dist/src/functions/commands/roles-audit.d.ts +1 -0
- package/dist/src/functions/commands/roles-audit.js +37 -0
- package/dist/src/functions/commands/roles-prune.d.ts +16 -0
- package/dist/src/functions/commands/roles-prune.js +52 -0
- package/dist/src/functions/commands/roles-shared.d.ts +40 -0
- package/dist/src/functions/commands/roles-shared.js +77 -0
- package/dist/src/functions/commands/scenario-browser.d.ts +3 -3
- package/dist/src/functions/commands/scenario-browser.js +1 -1
- package/dist/src/functions/commands/scenario-ladder.d.ts +44 -2
- package/dist/src/functions/commands/scenario-ladder.js +51 -2
- package/dist/src/functions/commands/scenario.d.ts +7 -0
- package/dist/src/functions/commands/scenario.js +79 -17
- package/dist/src/functions/commands/scopes-shared.d.ts +8 -0
- package/dist/src/functions/commands/scopes-shared.js +1 -1
- package/dist/src/functions/commands/serve.js +7 -3
- package/dist/src/functions/commands/virtual-user-formatter.d.ts +30 -0
- package/dist/src/functions/commands/virtual-user-formatter.js +65 -0
- package/dist/src/functions/db/local-db.js +38 -1
- package/dist/src/functions/validate/workspace-validate.js +1 -1
- package/dist/src/functions/wirings/ai-agent/pikku-command-public-agent.js +2 -1
- package/dist/src/functions/wirings/ai-agent/serialize-public-agent.js +1 -0
- package/dist/src/functions/wirings/auth/serialize-auth-gen.d.ts +2 -2
- package/dist/src/functions/wirings/auth/serialize-auth-gen.js +8 -8
- package/dist/src/functions/wirings/cli/serialize-channel-cli-client.d.ts +7 -3
- package/dist/src/functions/wirings/cli/serialize-channel-cli-client.js +32 -18
- package/dist/src/functions/wirings/cli/serialize-channel-cli.js +63 -6
- package/dist/src/functions/wirings/console/serialize-console-functions.js +4 -7
- package/dist/src/functions/wirings/functions/serialize-function-types.js +88 -28
- package/dist/src/functions/wirings/middleware/serialize-middleware-imports.js +25 -9
- package/dist/src/functions/wirings/package/serialize-package-types.js +5 -3
- package/dist/src/functions/wirings/personas/pikku-command-personas.d.ts +7 -0
- package/dist/src/functions/wirings/personas/pikku-command-personas.js +43 -0
- package/dist/src/functions/wirings/personas/serialize-personas-types.d.ts +18 -0
- package/dist/src/functions/wirings/personas/serialize-personas-types.js +76 -0
- package/dist/src/functions/wirings/realtime/pikku-command-events-scaffold.js +2 -1
- package/dist/src/functions/wirings/roles/pikku-command-roles.d.ts +7 -0
- package/dist/src/functions/wirings/roles/pikku-command-roles.js +33 -0
- package/dist/src/functions/wirings/roles/serialize-roles-types.d.ts +14 -0
- package/dist/src/functions/wirings/roles/serialize-roles-types.js +55 -0
- package/dist/src/functions/wirings/rpc/pikku-command-public-rpc.js +2 -1
- package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.d.ts +3 -3
- package/dist/src/functions/wirings/scenarios/register-scenario-instrumentation.js +3 -3
- package/dist/src/functions/wirings/scopes/serialize-scopes-types.d.ts +1 -1
- package/dist/src/functions/wirings/scopes/serialize-scopes-types.js +1 -1
- package/dist/src/functions/wirings/user-admin/pikku-command-user-admin-functions.js +2 -1
- package/dist/src/functions/wirings/user-admin/serialize-user-admin-functions.js +2 -2
- package/dist/src/functions/wirings/workflow/pikku-command-workflow-routes.js +2 -1
- package/dist/src/functions/wirings/workflow/pikku-command-workflow.js +13 -13
- package/dist/src/functions/wirings/workflow/serialize-personas.d.ts +10 -0
- package/dist/src/functions/wirings/workflow/serialize-personas.js +46 -0
- package/dist/src/functions/wirings/workflow/serialize-scenario-step-map.js +11 -2
- package/dist/src/functions/wirings/workflow/serialize-workflow-types.js +165 -29
- package/dist/src/functions/workflows/all.workflow.js +7 -1
- package/dist/src/server/content-signing-jwt.d.ts +13 -0
- package/dist/src/server/content-signing-jwt.js +51 -0
- package/dist/src/server/dev-server-runner.interface.d.ts +10 -2
- package/dist/src/server/node-server-runner.d.ts +2 -2
- package/dist/src/server/node-server-runner.js +2 -1
- package/dist/src/services.d.ts +3 -1
- package/dist/src/services.js +2 -1
- package/dist/src/utils/pikku-cli-config.js +45 -2
- package/dist/src/utils/resolve-personas.d.ts +32 -0
- package/dist/src/utils/resolve-personas.js +46 -0
- package/dist/src/utils/resolve-scaffold-feature.d.ts +19 -0
- package/dist/src/utils/resolve-scaffold-feature.js +49 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +17 -14
- package/dist/.pikku/schemas/schemas/ScenarioRunInput.schema.json +0 -1
- package/dist/.pikku/workflow/pikku-scenario-actors.gen.d.ts +0 -19
- package/dist/.pikku/workflow/pikku-scenario-actors.gen.js +0 -17
- package/dist/src/functions/wirings/workflow/serialize-scenario-actors.d.ts +0 -2
- package/dist/src/functions/wirings/workflow/serialize-scenario-actors.js +0 -40
- package/dist/src/utils/resolve-scenario-actors.d.ts +0 -23
- package/dist/src/utils/resolve-scenario-actors.js +0 -74
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { pikkuSessionlessFunc } from '#pikku';
|
|
2
|
+
import { writeFileInDir } from '../../../utils/file-writer.js';
|
|
3
|
+
import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
|
|
4
|
+
import { serializeRolesTypes } from './serialize-roles-types.js';
|
|
5
|
+
import { validateAndBuildSystemRoleDefinitionsMeta } from '@pikku/core/role';
|
|
6
|
+
export const pikkuRoles = pikkuSessionlessFunc({
|
|
7
|
+
func: async ({ logger, config, getInspectorState }, data) => {
|
|
8
|
+
const { rolesFile, rolesMetaJsonFile } = config;
|
|
9
|
+
if (!rolesFile) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
// Same cold-start reasoning as Scopes: on a bare .pikku this runs before
|
|
13
|
+
// pikku-types.gen.ts exists, so it takes the zero state. The file only has
|
|
14
|
+
// to exist so personas can import SystemRoleName; the real Roles step
|
|
15
|
+
// regenerates it with the declarations once setup has run.
|
|
16
|
+
const bootstrap = data?.bootstrap ?? false;
|
|
17
|
+
const state = await getInspectorState(false, bootstrap, bootstrap);
|
|
18
|
+
const content = serializeRolesTypes({
|
|
19
|
+
definitions: state.systemRoles.definitions,
|
|
20
|
+
});
|
|
21
|
+
await writeFileInDir(logger, rolesFile, content);
|
|
22
|
+
if (rolesMetaJsonFile) {
|
|
23
|
+
const meta = validateAndBuildSystemRoleDefinitionsMeta(state.systemRoles.definitions);
|
|
24
|
+
await writeFileInDir(logger, rolesMetaJsonFile, JSON.stringify(meta, null, 2));
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
middleware: [
|
|
28
|
+
logCommandInfoAndTime({
|
|
29
|
+
commandStart: 'Creating PikkuRoles types',
|
|
30
|
+
commandEnd: 'Created PikkuRoles types',
|
|
31
|
+
}),
|
|
32
|
+
],
|
|
33
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SystemRoleDefinitions } from '@pikku/core/role';
|
|
2
|
+
export interface SerializeRolesOptions {
|
|
3
|
+
definitions: SystemRoleDefinitions;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Generates the `SystemRoleName` union and role metadata from
|
|
7
|
+
* `defineSystemRole` declarations.
|
|
8
|
+
*
|
|
9
|
+
* The union is what makes a persona's `roles: ['buyer']` checkable. Custom
|
|
10
|
+
* roles are deliberately absent: an admin can delete one from the console, so
|
|
11
|
+
* a persona pinned to it would silently stop testing what it claims to — the
|
|
12
|
+
* failure would be a scatter of 403s rather than a build error.
|
|
13
|
+
*/
|
|
14
|
+
export declare const serializeRolesTypes: ({ definitions }: SerializeRolesOptions) => string;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const quote = (value) => `'${value.replace(/'/g, "\\'")}'`;
|
|
2
|
+
/**
|
|
3
|
+
* Generates the `SystemRoleName` union and role metadata from
|
|
4
|
+
* `defineSystemRole` declarations.
|
|
5
|
+
*
|
|
6
|
+
* The union is what makes a persona's `roles: ['buyer']` checkable. Custom
|
|
7
|
+
* roles are deliberately absent: an admin can delete one from the console, so
|
|
8
|
+
* a persona pinned to it would silently stop testing what it claims to — the
|
|
9
|
+
* failure would be a scatter of 403s rather than a build error.
|
|
10
|
+
*/
|
|
11
|
+
export const serializeRolesTypes = ({ definitions }) => {
|
|
12
|
+
const names = [...new Set(definitions.map((d) => d.name))].sort();
|
|
13
|
+
const union = names.length > 0
|
|
14
|
+
? `\n${names.map((name) => ` | ${quote(name)}`).join('\n')}`
|
|
15
|
+
: ' never';
|
|
16
|
+
return `/**
|
|
17
|
+
* This file was generated by the @pikku/cli
|
|
18
|
+
*/
|
|
19
|
+
import type { SystemRole, SystemRoleDefinitionsMeta } from '@pikku/core/role'
|
|
20
|
+
import { flattenSystemRoleDefinitions } from '@pikku/core/role'
|
|
21
|
+
import definitions from './pikku-roles-meta.gen.json' with { type: 'json' }
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Every role that ships with this application.
|
|
25
|
+
*
|
|
26
|
+
* Roles an admin composes in the console are not here and cannot be: they do
|
|
27
|
+
* not exist at build time, and can be deleted after it.
|
|
28
|
+
*/
|
|
29
|
+
export type SystemRoleName =${union}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The declared role set, for a ScopeService to sync into its store.
|
|
33
|
+
*
|
|
34
|
+
* Derived from the metadata sidecar rather than inlined, so the import above
|
|
35
|
+
* forces tsc to emit the .json alongside this file — an addon publishes only
|
|
36
|
+
* its compiled output, and a host reads that .json to merge the addon's roles
|
|
37
|
+
* into its own union.
|
|
38
|
+
*
|
|
39
|
+
* Additive on removal: a deleted declaration leaves an inert row rather than
|
|
40
|
+
* revoking everyone's grant on deploy. Use \`pikku roles prune\` to remove them.
|
|
41
|
+
*/
|
|
42
|
+
export const SYSTEM_ROLES: SystemRole[] = flattenSystemRoleDefinitions(
|
|
43
|
+
Object.values(definitions as SystemRoleDefinitionsMeta)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
/** Declared roles, keyed by name. Rendered by the console when granting. */
|
|
47
|
+
export const SYSTEM_ROLES_META: Record<string, SystemRole> = Object.fromEntries(
|
|
48
|
+
SYSTEM_ROLES.map((role) => [role.name, role])
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
/** Whether a role name is declared in code, and therefore not editable. */
|
|
52
|
+
export const isSystemRole = (name: string): boolean =>
|
|
53
|
+
Object.prototype.hasOwnProperty.call(SYSTEM_ROLES_META, name)
|
|
54
|
+
`;
|
|
55
|
+
};
|
|
@@ -4,6 +4,7 @@ import { writeFileInDir } from '../../../utils/file-writer.js';
|
|
|
4
4
|
import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
|
|
5
5
|
import { removeLegacyScaffoldFile } from '../../../utils/remove-legacy-scaffold-file.js';
|
|
6
6
|
import { serializePublicRPC } from './serialize-public-rpc.js';
|
|
7
|
+
import { resolveScaffoldFeature } from '../../../utils/resolve-scaffold-feature.js';
|
|
7
8
|
export const pikkuPublicRPC = pikkuSessionlessFunc({
|
|
8
9
|
func: async ({ logger, config, variables }) => {
|
|
9
10
|
const deployCodegenFlag = await variables.get('PIKKU_DEPLOY_CODEGEN');
|
|
@@ -12,7 +13,7 @@ export const pikkuPublicRPC = pikkuSessionlessFunc({
|
|
|
12
13
|
}
|
|
13
14
|
if (config.scaffold?.rpc && config.publicRpcSchemasFile) {
|
|
14
15
|
const pathToPikkuTypes = getFileImportRelativePath(config.publicRpcFile, config.typesDeclarationFile, config.packageMappings);
|
|
15
|
-
const { schemas, functions } = serializePublicRPC(pathToPikkuTypes, config.scaffold.rpc
|
|
16
|
+
const { schemas, functions } = serializePublicRPC(pathToPikkuTypes, resolveScaffoldFeature('rpc', config.scaffold.rpc).auth, config.globalHTTPPrefix || '');
|
|
16
17
|
await writeFileInDir(logger, config.publicRpcSchemasFile, schemas);
|
|
17
18
|
await writeFileInDir(logger, config.publicRpcFile, functions);
|
|
18
19
|
await removeLegacyScaffoldFile(config.publicRpcFile);
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* scenario runner can reach it over `/rpc/<name>` exactly as before.
|
|
4
4
|
*
|
|
5
5
|
* They are registered sessionless: `auth` decides whether a session is
|
|
6
|
-
* *required*, and a sessioned function would demand one regardless of the flag
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* *required*, and a sessioned function would demand one regardless of the flag.
|
|
7
|
+
* `scaffold.scenarios: true` therefore requires a session; `{ auth: false }` is
|
|
8
|
+
* what opens them.
|
|
9
9
|
*/
|
|
10
10
|
export declare const registerScenarioInstrumentation: (requireAuth: boolean) => void;
|
|
@@ -79,9 +79,9 @@ const instrumentation = {
|
|
|
79
79
|
* scenario runner can reach it over `/rpc/<name>` exactly as before.
|
|
80
80
|
*
|
|
81
81
|
* They are registered sessionless: `auth` decides whether a session is
|
|
82
|
-
* *required*, and a sessioned function would demand one regardless of the flag
|
|
83
|
-
*
|
|
84
|
-
*
|
|
82
|
+
* *required*, and a sessioned function would demand one regardless of the flag.
|
|
83
|
+
* `scaffold.scenarios: true` therefore requires a session; `{ auth: false }` is
|
|
84
|
+
* what opens them.
|
|
85
85
|
*/
|
|
86
86
|
export const registerScenarioInstrumentation = (requireAuth) => {
|
|
87
87
|
const meta = pikkuState(null, 'function', 'meta');
|
|
@@ -3,7 +3,7 @@ export interface SerializeScopesOptions {
|
|
|
3
3
|
definitions: ScopeDefinitions;
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
|
-
* Generates the `ScopeId` union and scope metadata from `
|
|
6
|
+
* Generates the `ScopeId` union and scope metadata from `defineScope`
|
|
7
7
|
* declarations.
|
|
8
8
|
*
|
|
9
9
|
* The union carries every declared node plus a wildcard form for each node that
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { flattenScopeDefinitions } from '@pikku/core/scope';
|
|
2
2
|
const quote = (value) => `'${value.replace(/'/g, "\\'")}'`;
|
|
3
3
|
/**
|
|
4
|
-
* Generates the `ScopeId` union and scope metadata from `
|
|
4
|
+
* Generates the `ScopeId` union and scope metadata from `defineScope`
|
|
5
5
|
* declarations.
|
|
6
6
|
*
|
|
7
7
|
* The union carries every declared node plus a wildcard form for each node that
|
|
@@ -3,6 +3,7 @@ import { getFileImportRelativePath } from '../../../utils/file-import-path.js';
|
|
|
3
3
|
import { writeFileInDir } from '../../../utils/file-writer.js';
|
|
4
4
|
import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
|
|
5
5
|
import { serializeUserAdminFunctions } from './serialize-user-admin-functions.js';
|
|
6
|
+
import { resolveScaffoldFeature } from '../../../utils/resolve-scaffold-feature.js';
|
|
6
7
|
export const pikkuUserAdminFunctions = pikkuSessionlessFunc({
|
|
7
8
|
func: async ({ logger, config, getInspectorState }) => {
|
|
8
9
|
if (!config.scaffold?.userAdmin ||
|
|
@@ -35,7 +36,7 @@ export const pikkuUserAdminFunctions = pikkuSessionlessFunc({
|
|
|
35
36
|
`Then re-run codegen. Or remove "scaffold.userAdmin" from pikku.config.json.`);
|
|
36
37
|
}
|
|
37
38
|
const pathToPikkuTypes = getFileImportRelativePath(config.userAdminFunctionsFile, config.typesDeclarationFile, config.packageMappings);
|
|
38
|
-
const { schemas, functions } = serializeUserAdminFunctions(pathToPikkuTypes, config.scaffold.userAdmin
|
|
39
|
+
const { schemas, functions } = serializeUserAdminFunctions(pathToPikkuTypes, resolveScaffoldFeature('userAdmin', config.scaffold.userAdmin).auth);
|
|
39
40
|
await writeFileInDir(logger, config.userAdminSchemasFile, schemas);
|
|
40
41
|
await writeFileInDir(logger, config.userAdminFunctionsFile, functions);
|
|
41
42
|
return true;
|
|
@@ -86,7 +86,7 @@ export const Success = z.object({
|
|
|
86
86
|
* Auto-generated user management functions
|
|
87
87
|
* Do not edit manually - regenerate with 'npx pikku'
|
|
88
88
|
*/
|
|
89
|
-
import { pikkuFunc,
|
|
89
|
+
import { pikkuFunc, defineScope } from '${pathToPikkuTypes}'
|
|
90
90
|
import { callAdminApi } from '@pikku/better-auth'
|
|
91
91
|
import {
|
|
92
92
|
CreateUserInput,
|
|
@@ -103,7 +103,7 @@ import {
|
|
|
103
103
|
// this is the whole \`admin\` tree — not just the leaves gated below. It must stay
|
|
104
104
|
// byte-identical to ADMIN_SCOPE_TREE in @pikku/better-auth and to the copy in
|
|
105
105
|
// @pikku/addon-console, or codegen fails with conflicting declarations.
|
|
106
|
-
|
|
106
|
+
defineScope({
|
|
107
107
|
admin: {
|
|
108
108
|
displayName: 'Administration',
|
|
109
109
|
description: 'Capabilities that act on the application as a whole',
|
|
@@ -4,13 +4,14 @@ import { writeFileInDir } from '../../../utils/file-writer.js';
|
|
|
4
4
|
import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
|
|
5
5
|
import { removeLegacyScaffoldFile } from '../../../utils/remove-legacy-scaffold-file.js';
|
|
6
6
|
import { serializeWorkflowRoutes } from './serialize-workflow-routes.js';
|
|
7
|
+
import { resolveScaffoldFeature } from '../../../utils/resolve-scaffold-feature.js';
|
|
7
8
|
export const pikkuWorkflowRoutes = pikkuSessionlessFunc({
|
|
8
9
|
func: async ({ logger, config }) => {
|
|
9
10
|
if (config.scaffold?.workflow &&
|
|
10
11
|
config.workflowRoutesFile &&
|
|
11
12
|
config.workflowRoutesSchemasFile) {
|
|
12
13
|
const pathToPikkuTypes = getFileImportRelativePath(config.workflowRoutesFile, config.typesDeclarationFile, config.packageMappings);
|
|
13
|
-
const { schemas, functions } = serializeWorkflowRoutes(pathToPikkuTypes, config.scaffold?.workflow
|
|
14
|
+
const { schemas, functions } = serializeWorkflowRoutes(pathToPikkuTypes, resolveScaffoldFeature('workflow', config.scaffold?.workflow).auth);
|
|
14
15
|
await writeFileInDir(logger, config.workflowRoutesSchemasFile, schemas);
|
|
15
16
|
await writeFileInDir(logger, config.workflowRoutesFile, functions);
|
|
16
17
|
await removeLegacyScaffoldFile(config.workflowRoutesFile);
|
|
@@ -3,8 +3,8 @@ import { ErrorCode } from '@pikku/inspector';
|
|
|
3
3
|
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
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { serializePersonas } from './serialize-personas.js';
|
|
7
|
+
import { resolvePersonas } from '../../../utils/resolve-personas.js';
|
|
8
8
|
import { serializeWorkflowRegistration } from './serialize-workflow-registration.js';
|
|
9
9
|
import { serializeWorkflowMap } from './serialize-workflow-map.js';
|
|
10
10
|
import { serializeScenarioStepMap } from './serialize-scenario-step-map.js';
|
|
@@ -89,21 +89,21 @@ export const pikkuWorkflow = pikkuSessionlessFunc({
|
|
|
89
89
|
const agentMapImportPath = getFileImportRelativePath(workflowTypesFile, config.agentMapDeclarationFile, packageMappings);
|
|
90
90
|
const scopesImportPath = getFileImportRelativePath(workflowTypesFile, config.scopesFile, packageMappings);
|
|
91
91
|
const scenarioStepMapImportPath = getFileImportRelativePath(workflowTypesFile, config.scenarioStepMapDeclarationFile, packageMappings);
|
|
92
|
-
const
|
|
93
|
-
await writeFileInDir(logger, workflowTypesFile, serializeWorkflowTypes(functionTypesImportPath, rpcMapImportPath, workflowMapImportPath, agentMapImportPath, scopesImportPath, scenarioStepMapImportPath,
|
|
92
|
+
const personasImportPath = getFileImportRelativePath(workflowTypesFile, config.personasWiringFile, packageMappings);
|
|
93
|
+
await writeFileInDir(logger, workflowTypesFile, serializeWorkflowTypes(functionTypesImportPath, rpcMapImportPath, workflowMapImportPath, agentMapImportPath, scopesImportPath, scenarioStepMapImportPath, personasImportPath));
|
|
94
94
|
// Rides the workflow command rather than getting its own codegen node, so
|
|
95
95
|
// it can't drift out of the two hand-maintained ordering lists.
|
|
96
96
|
await writeFileInDir(logger, config.scenarioStepMapDeclarationFile, serializeScenarioStepMap(logger, config.scenarioStepMapDeclarationFile, packageMappings, typesMap, functionState.meta));
|
|
97
|
-
// Written even
|
|
98
|
-
// resolvable import for the generated function types.
|
|
99
|
-
const
|
|
97
|
+
// Written even when nobody declared a persona, so `TypedPersonas` is always
|
|
98
|
+
// a resolvable import for the generated function types.
|
|
99
|
+
const personas = resolvePersonas(visitState.personas.definitions, config.scenarios?.emailDomain);
|
|
100
100
|
{
|
|
101
|
-
const agentMapImportPath = getFileImportRelativePath(config.
|
|
102
|
-
const exposedRpcMapImportPath = getFileImportRelativePath(config.
|
|
103
|
-
await writeFileInDir(logger, config.
|
|
104
|
-
// Fixed path
|
|
105
|
-
//
|
|
106
|
-
await writeFileInDir(logger, join(config.outDir, 'workflow', '
|
|
101
|
+
const agentMapImportPath = getFileImportRelativePath(config.personasWiringFile, config.agentMapDeclarationFile, packageMappings);
|
|
102
|
+
const exposedRpcMapImportPath = getFileImportRelativePath(config.personasWiringFile, config.rpcMapDeclarationFile, packageMappings);
|
|
103
|
+
await writeFileInDir(logger, config.personasWiringFile, serializePersonas(personas, agentMapImportPath, exposedRpcMapImportPath));
|
|
104
|
+
// Fixed path getPersonasMeta() reads; kept out of workflow/meta, which
|
|
105
|
+
// getWorkflowMeta() globs as workflows.
|
|
106
|
+
await writeFileInDir(logger, join(config.outDir, 'workflow', 'personas.gen.json'), JSON.stringify(personas, null, 2));
|
|
107
107
|
}
|
|
108
108
|
// Fixed path getFeaturesMeta() reads. Written even when empty so the
|
|
109
109
|
// console can tell "no features declared" from "meta not generated yet".
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ResolvedPersona } from '@pikku/core/services';
|
|
2
|
+
/**
|
|
3
|
+
* The runtime half of `definePersonas()`.
|
|
4
|
+
*
|
|
5
|
+
* Generated rather than re-exported because the addresses are computed: nobody
|
|
6
|
+
* writes one down, and a seed, a scenario run and a virtual-user run all have
|
|
7
|
+
* to arrive at the same one. Baking them here is what makes that a single fact
|
|
8
|
+
* rather than three implementations of the same rule.
|
|
9
|
+
*/
|
|
10
|
+
export declare const serializePersonas: (personas: Record<string, ResolvedPersona>, agentMapImportPath: string, rpcMapImportPath: string) => string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The runtime half of `definePersonas()`.
|
|
3
|
+
*
|
|
4
|
+
* Generated rather than re-exported because the addresses are computed: nobody
|
|
5
|
+
* writes one down, and a seed, a scenario run and a virtual-user run all have
|
|
6
|
+
* to arrive at the same one. Baking them here is what makes that a single fact
|
|
7
|
+
* rather than three implementations of the same rule.
|
|
8
|
+
*/
|
|
9
|
+
export const serializePersonas = (personas, agentMapImportPath, rpcMapImportPath) => {
|
|
10
|
+
return `/** Personas declared with definePersonas() */
|
|
11
|
+
import { createHttpPersonas } from '@pikku/core/persona'
|
|
12
|
+
import type {
|
|
13
|
+
HttpPersonasConfig,
|
|
14
|
+
ScenarioPersona,
|
|
15
|
+
ResolvedPersona,
|
|
16
|
+
} from '@pikku/core/services'
|
|
17
|
+
import type { AgentMap } from '${agentMapImportPath}'
|
|
18
|
+
import type { FlattenedRPCMap } from '${rpcMapImportPath}'
|
|
19
|
+
|
|
20
|
+
export const personaConfigs = ${JSON.stringify(personas, null, 2)} as const satisfies Record<string, ResolvedPersona>
|
|
21
|
+
|
|
22
|
+
export type PersonaName = keyof typeof personaConfigs
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The same personas as a list, widened to \`ResolvedPersona\`. Iterating
|
|
26
|
+
* \`personaConfigs\` directly yields the literal type of each entry, on which an
|
|
27
|
+
* optional field a persona did not declare does not exist — so a seed reading
|
|
28
|
+
* \`roles\` off every persona needs this instead.
|
|
29
|
+
*/
|
|
30
|
+
export const personaList: ResolvedPersona[] = Object.values(personaConfigs)
|
|
31
|
+
|
|
32
|
+
export type AgentName = keyof AgentMap & string
|
|
33
|
+
export type TypedPersonas = Record<
|
|
34
|
+
PersonaName,
|
|
35
|
+
ScenarioPersona<AgentName, FlattenedRPCMap>
|
|
36
|
+
>
|
|
37
|
+
|
|
38
|
+
export const createPersonas = (
|
|
39
|
+
options: Omit<HttpPersonasConfig, 'personas'>
|
|
40
|
+
): TypedPersonas =>
|
|
41
|
+
createHttpPersonas({
|
|
42
|
+
...options,
|
|
43
|
+
personas: personaConfigs,
|
|
44
|
+
}) as TypedPersonas
|
|
45
|
+
`;
|
|
46
|
+
};
|
|
@@ -9,10 +9,19 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { serializeImportMap } from '../../../utils/serialize-import-map.js';
|
|
11
11
|
import { generateCustomTypes } from '@pikku/inspector';
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* All three step wrappers. A scenario calls a platform or addon step by name
|
|
14
|
+
* exactly as it calls a persona one — who acts changes what the step may do, not
|
|
15
|
+
* how it is referenced — so all three belong in the map.
|
|
16
|
+
*/
|
|
17
|
+
const SCENARIO_STEP_WRAPPERS = new Set([
|
|
18
|
+
'pikkuScenarioStep',
|
|
19
|
+
'pikkuPlatformScenarioStep',
|
|
20
|
+
'pikkuAddonScenarioStep',
|
|
21
|
+
]);
|
|
13
22
|
export const serializeScenarioStepMap = (logger, relativeToPath, packageMappings, typesMap, functionsMeta) => {
|
|
14
23
|
const requiredTypes = new Set();
|
|
15
|
-
const steps = Object.entries(functionsMeta).filter(([, meta]) => meta.funcWrapper
|
|
24
|
+
const steps = Object.entries(functionsMeta).filter(([, meta]) => !!meta.funcWrapper && SCENARIO_STEP_WRAPPERS.has(meta.funcWrapper));
|
|
16
25
|
const resolveType = (name) => {
|
|
17
26
|
if (!name) {
|
|
18
27
|
return 'void';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const serializeWorkflowTypes = (functionTypesImportPath, rpcMapImportPath, workflowMapImportPath, agentMapImportPath, scopesImportPath, scenarioStepMapImportPath = './pikku-scenario-step-map.gen.js', scenarioActorsImportPath = './pikku-
|
|
1
|
+
export const serializeWorkflowTypes = (functionTypesImportPath, rpcMapImportPath, workflowMapImportPath, agentMapImportPath, scopesImportPath, scenarioStepMapImportPath = './pikku-scenario-step-map.gen.js', scenarioActorsImportPath = './pikku-personas.gen.js') => {
|
|
2
2
|
return `import { WorkflowCancelledException } from '@pikku/core/workflow'
|
|
3
3
|
import { template } from '@pikku/core/workflow'
|
|
4
4
|
import {
|
|
@@ -14,9 +14,9 @@ import type { FlattenedRPCMap } from '${rpcMapImportPath}'
|
|
|
14
14
|
import type { FlattenedWorkflowMap } from '${workflowMapImportPath}'
|
|
15
15
|
import type { AgentMap as FlattenedAgentMap } from '${agentMapImportPath}'
|
|
16
16
|
import type { FlattenedScenarioStepMap } from '${scenarioStepMapImportPath}'
|
|
17
|
-
import type {
|
|
17
|
+
import type { TypedPersonas } from '${scenarioActorsImportPath}'
|
|
18
18
|
|
|
19
|
-
export type {
|
|
19
|
+
export type { TypedPersonas }
|
|
20
20
|
|
|
21
21
|
export { template }
|
|
22
22
|
|
|
@@ -308,16 +308,26 @@ export function pikkuFeature<const Scenarios extends readonly unknown[]>(
|
|
|
308
308
|
return config
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
+
/**
|
|
312
|
+
* One surface's implementation of a step.
|
|
313
|
+
*
|
|
314
|
+
* Each binding is typed independently, so a browser binding gets a non-optional
|
|
315
|
+
* \`wire.browser\` and a cli binding a non-optional \`wire.cli\` without either
|
|
316
|
+
* leaking into the other.
|
|
317
|
+
*/
|
|
311
318
|
export type PikkuFunctionScenarioStep<
|
|
312
319
|
In = unknown,
|
|
313
320
|
Out = never,
|
|
314
|
-
|
|
315
|
-
> = PikkuFunctionSessionless<
|
|
321
|
+
Surface extends 'browser' | 'cli' | 'default' = 'default'
|
|
322
|
+
> = PikkuFunctionSessionless<
|
|
323
|
+
In,
|
|
324
|
+
Out,
|
|
325
|
+
Surface extends 'default' ? 'scenarioStep' : 'scenarioStep' | Surface
|
|
326
|
+
>
|
|
316
327
|
|
|
317
328
|
export type PikkuScenarioStepConfigWithSchema<
|
|
318
329
|
InputSchema extends StandardSchemaV1 | undefined = undefined,
|
|
319
|
-
OutputSchema extends StandardSchemaV1 | undefined = undefined
|
|
320
|
-
B extends boolean = false
|
|
330
|
+
OutputSchema extends StandardSchemaV1 | undefined = undefined
|
|
321
331
|
> = {
|
|
322
332
|
/** Registered name — this is the string \`scenario.step()\` references. */
|
|
323
333
|
name: string
|
|
@@ -336,49 +346,175 @@ export type PikkuScenarioStepConfigWithSchema<
|
|
|
336
346
|
* sentence per call site.
|
|
337
347
|
*/
|
|
338
348
|
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
349
|
title?: string
|
|
346
350
|
tags?: string[]
|
|
347
351
|
input?: InputSchema
|
|
348
352
|
output?: OutputSchema
|
|
349
353
|
errors?: Array<typeof PikkuError>
|
|
350
|
-
|
|
354
|
+
/**
|
|
355
|
+
* Drive this step through a real browser, as a human would. The runner
|
|
356
|
+
* provisions a window for the step's actor first — which makes an actor
|
|
357
|
+
* mandatory — and \`wire.browser\` is non-optional inside it.
|
|
358
|
+
*
|
|
359
|
+
* Compose shared utilities here rather than writing raw clicks: the step says
|
|
360
|
+
* what the actor is doing, the utilities say how.
|
|
361
|
+
*/
|
|
362
|
+
browser?: PikkuFunctionScenarioStep<
|
|
363
|
+
InputSchema extends StandardSchemaV1 ? InferSchemaOutput<InputSchema> : unknown,
|
|
364
|
+
OutputSchema extends StandardSchemaV1 ? InferSchemaOutput<OutputSchema> : unknown,
|
|
365
|
+
'browser'
|
|
366
|
+
>
|
|
367
|
+
/** Drive this step remotely, over the websocket. */
|
|
368
|
+
cli?: PikkuFunctionScenarioStep<
|
|
351
369
|
InputSchema extends StandardSchemaV1 ? InferSchemaOutput<InputSchema> : unknown,
|
|
352
370
|
OutputSchema extends StandardSchemaV1 ? InferSchemaOutput<OutputSchema> : unknown,
|
|
353
|
-
|
|
371
|
+
'cli'
|
|
372
|
+
>
|
|
373
|
+
/**
|
|
374
|
+
* The server-side path — the fast suite, and the floor every other surface
|
|
375
|
+
* falls back to.
|
|
376
|
+
*
|
|
377
|
+
* On a \`then\`, this is not an alternative to the other bindings but a second
|
|
378
|
+
* **witness**: the run executes every binding it has and fails if they
|
|
379
|
+
* disagree, because "the row says paid" and "the page says paid" are different
|
|
380
|
+
* claims and the gap between them is the bug nobody catches.
|
|
381
|
+
*/
|
|
382
|
+
default?: PikkuFunctionScenarioStep<
|
|
383
|
+
InputSchema extends StandardSchemaV1 ? InferSchemaOutput<InputSchema> : unknown,
|
|
384
|
+
OutputSchema extends StandardSchemaV1 ? InferSchemaOutput<OutputSchema> : unknown,
|
|
385
|
+
'default'
|
|
354
386
|
>
|
|
355
387
|
}
|
|
356
388
|
|
|
357
|
-
export type PikkuScenarioStepConfig<In, Out
|
|
358
|
-
Omit<PikkuScenarioStepConfigWithSchema<undefined, undefined
|
|
359
|
-
|
|
389
|
+
export type PikkuScenarioStepConfig<In, Out> =
|
|
390
|
+
Omit<PikkuScenarioStepConfigWithSchema<undefined, undefined>, 'browser' | 'cli' | 'default' | 'input' | 'output'> & {
|
|
391
|
+
browser?: PikkuFunctionScenarioStep<In, Out, 'browser'>
|
|
392
|
+
cli?: PikkuFunctionScenarioStep<In, Out, 'cli'>
|
|
393
|
+
default?: PikkuFunctionScenarioStep<In, Out, 'default'>
|
|
360
394
|
}
|
|
361
395
|
|
|
362
396
|
/**
|
|
363
|
-
* A named, reusable scenario step
|
|
364
|
-
*
|
|
365
|
-
*
|
|
397
|
+
* A named, reusable scenario step, declaring one implementation per surface an
|
|
398
|
+
* actor can drive it through.
|
|
399
|
+
*
|
|
400
|
+
* The step's identity is what the actor is trying to do; which surface carries
|
|
401
|
+
* it out is a binding. That is what lets one ladder run through a real browser,
|
|
402
|
+
* over the websocket, or entirely server-side — and what makes "how much of this
|
|
403
|
+
* flow can a human actually reach" a number rather than a guess.
|
|
366
404
|
*
|
|
367
405
|
* Steps are deliberately NOT registered as RPCs: a browser-driving step must
|
|
368
406
|
* never be network-callable.
|
|
369
407
|
*/
|
|
370
408
|
export function pikkuScenarioStep<
|
|
371
409
|
InputSchema extends StandardSchemaV1 | undefined = undefined,
|
|
372
|
-
OutputSchema extends StandardSchemaV1 | undefined = undefined
|
|
373
|
-
B extends boolean = false
|
|
410
|
+
OutputSchema extends StandardSchemaV1 | undefined = undefined
|
|
374
411
|
>(
|
|
375
|
-
config: PikkuScenarioStepConfigWithSchema<InputSchema, OutputSchema
|
|
376
|
-
): PikkuFunctionConfig<InputSchema extends StandardSchemaV1 ? InferSchemaOutput<InputSchema> : unknown, OutputSchema extends StandardSchemaV1 ? InferSchemaOutput<OutputSchema> : unknown,
|
|
377
|
-
export function pikkuScenarioStep<In, Out = unknown
|
|
378
|
-
config: PikkuScenarioStepConfig<In, Out
|
|
379
|
-
): PikkuFunctionConfig<In, Out,
|
|
412
|
+
config: PikkuScenarioStepConfigWithSchema<InputSchema, OutputSchema>
|
|
413
|
+
): PikkuFunctionConfig<InputSchema extends StandardSchemaV1 ? InferSchemaOutput<InputSchema> : unknown, OutputSchema extends StandardSchemaV1 ? InferSchemaOutput<OutputSchema> : unknown, 'scenarioStep' | 'browser' | 'cli', PikkuFunctionScenarioStep<InputSchema extends StandardSchemaV1 ? InferSchemaOutput<InputSchema> : unknown, OutputSchema extends StandardSchemaV1 ? InferSchemaOutput<OutputSchema> : unknown, 'browser' | 'cli' | 'default'>, InputSchema, OutputSchema>
|
|
414
|
+
export function pikkuScenarioStep<In, Out = unknown>(
|
|
415
|
+
config: PikkuScenarioStepConfig<In, Out>
|
|
416
|
+
): PikkuFunctionConfig<In, Out, 'scenarioStep' | 'browser' | 'cli'>
|
|
380
417
|
export function pikkuScenarioStep(config: any) {
|
|
381
|
-
|
|
418
|
+
const surfaces = (['browser', 'cli', 'default'] as const).filter(
|
|
419
|
+
(surface) => typeof config[surface] === 'function'
|
|
420
|
+
)
|
|
421
|
+
if (surfaces.length === 0) {
|
|
422
|
+
throw new Error(
|
|
423
|
+
\`[scenario] step '\${config.name}' declares no surface bindings. Add at least a \\\`default\\\` (server-side) implementation.\`
|
|
424
|
+
)
|
|
425
|
+
}
|
|
426
|
+
return {
|
|
427
|
+
...config,
|
|
428
|
+
surfaces,
|
|
429
|
+
// The runner sets the surface on the wire and calls this once per binding it
|
|
430
|
+
// resolved — every witness of a \`then\`, exactly one for an action step.
|
|
431
|
+
func: (services: any, data: any, wire: any) => {
|
|
432
|
+
const surface = wire?.scenarioStep?.surface ?? 'default'
|
|
433
|
+
const binding = config[surface] ?? config.default
|
|
434
|
+
return binding(services, data, wire)
|
|
435
|
+
},
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* What a step declares when nobody drives it: one implementation, and no
|
|
441
|
+
* surfaces to choose between.
|
|
442
|
+
*
|
|
443
|
+
* \`func\` rather than \`default\` is the point. \`default\` means *the fallback when
|
|
444
|
+
* no other surface applies*, which implies others could exist; \`func\` says
|
|
445
|
+
* structurally that there is one way this happens. That also keeps the phase
|
|
446
|
+
* rule coherent — an assertion runs every witness it has and fails if they
|
|
447
|
+
* disagree, and this has exactly one by construction.
|
|
448
|
+
*/
|
|
449
|
+
export type PikkuSubjectScenarioStepConfigWithSchema<
|
|
450
|
+
InputSchema extends StandardSchemaV1 | undefined = undefined,
|
|
451
|
+
OutputSchema extends StandardSchemaV1 | undefined = undefined
|
|
452
|
+
> = Omit<
|
|
453
|
+
PikkuScenarioStepConfigWithSchema<InputSchema, OutputSchema>,
|
|
454
|
+
'browser' | 'cli' | 'default'
|
|
455
|
+
> & {
|
|
456
|
+
func: PikkuFunctionScenarioStep<
|
|
457
|
+
InputSchema extends StandardSchemaV1 ? InferSchemaOutput<InputSchema> : unknown,
|
|
458
|
+
OutputSchema extends StandardSchemaV1 ? InferSchemaOutput<OutputSchema> : unknown,
|
|
459
|
+
'default'
|
|
460
|
+
>
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
export type PikkuSubjectScenarioStepConfig<In, Out> =
|
|
464
|
+
Omit<PikkuScenarioStepConfig<In, Out>, 'browser' | 'cli' | 'default'> & {
|
|
465
|
+
func: PikkuFunctionScenarioStep<In, Out, 'default'>
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* A step in which the app acts on itself — "Given the platform has expired the
|
|
470
|
+
* trial".
|
|
471
|
+
*
|
|
472
|
+
* The grammatical subject of that sentence is not a user of your app; it **is**
|
|
473
|
+
* your app, which is why it is its own declaration rather than a persona with an
|
|
474
|
+
* asterisk. A persona is a person.
|
|
475
|
+
*
|
|
476
|
+
* Local-test-only, and never in a virtual user's catalogue: a virtual user that
|
|
477
|
+
* could expire its own trial is manufacturing the outcome it exists to discover.
|
|
478
|
+
*/
|
|
479
|
+
export function pikkuPlatformScenarioStep<
|
|
480
|
+
InputSchema extends StandardSchemaV1 | undefined = undefined,
|
|
481
|
+
OutputSchema extends StandardSchemaV1 | undefined = undefined
|
|
482
|
+
>(
|
|
483
|
+
config: PikkuSubjectScenarioStepConfigWithSchema<InputSchema, OutputSchema>
|
|
484
|
+
): PikkuFunctionConfig<InputSchema extends StandardSchemaV1 ? InferSchemaOutput<InputSchema> : unknown, OutputSchema extends StandardSchemaV1 ? InferSchemaOutput<OutputSchema> : unknown, 'scenarioStep', PikkuFunctionScenarioStep<InputSchema extends StandardSchemaV1 ? InferSchemaOutput<InputSchema> : unknown, OutputSchema extends StandardSchemaV1 ? InferSchemaOutput<OutputSchema> : unknown, 'default'>, InputSchema, OutputSchema>
|
|
485
|
+
export function pikkuPlatformScenarioStep<In, Out = unknown>(
|
|
486
|
+
config: PikkuSubjectScenarioStepConfig<In, Out>
|
|
487
|
+
): PikkuFunctionConfig<In, Out, 'scenarioStep'>
|
|
488
|
+
export function pikkuPlatformScenarioStep(config: any) {
|
|
489
|
+
return { ...config, surfaces: ['default'] }
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* A step in which a third-party system acts — "Given Stripe's webhook arrives",
|
|
494
|
+
* "When Mailgun bounces it".
|
|
495
|
+
*
|
|
496
|
+
* \`addon\` names the addon that wraps that service, the same name its
|
|
497
|
+
* \`wireAddon\` declares. These steps *are* the mock its consumers currently
|
|
498
|
+
* hand-write: shipped by the addon author, maintained with the addon, and the
|
|
499
|
+
* same artifact that appears in the prose.
|
|
500
|
+
*
|
|
501
|
+
* Note that arranging and asserting are different — "Stripe's webhook arrives"
|
|
502
|
+
* stubs, "Then Stripe was charged" asserts, and only the first is a stub.
|
|
503
|
+
*
|
|
504
|
+
* Local-test-only, and never in a virtual user's catalogue: one that could
|
|
505
|
+
* invoke this would forge its own payment success.
|
|
506
|
+
*/
|
|
507
|
+
export function pikkuAddonScenarioStep<
|
|
508
|
+
InputSchema extends StandardSchemaV1 | undefined = undefined,
|
|
509
|
+
OutputSchema extends StandardSchemaV1 | undefined = undefined
|
|
510
|
+
>(
|
|
511
|
+
config: PikkuSubjectScenarioStepConfigWithSchema<InputSchema, OutputSchema> & { addon: string }
|
|
512
|
+
): PikkuFunctionConfig<InputSchema extends StandardSchemaV1 ? InferSchemaOutput<InputSchema> : unknown, OutputSchema extends StandardSchemaV1 ? InferSchemaOutput<OutputSchema> : unknown, 'scenarioStep', PikkuFunctionScenarioStep<InputSchema extends StandardSchemaV1 ? InferSchemaOutput<InputSchema> : unknown, OutputSchema extends StandardSchemaV1 ? InferSchemaOutput<OutputSchema> : unknown, 'default'>, InputSchema, OutputSchema>
|
|
513
|
+
export function pikkuAddonScenarioStep<In, Out = unknown>(
|
|
514
|
+
config: PikkuSubjectScenarioStepConfig<In, Out> & { addon: string }
|
|
515
|
+
): PikkuFunctionConfig<In, Out, 'scenarioStep'>
|
|
516
|
+
export function pikkuAddonScenarioStep(config: any) {
|
|
517
|
+
return { ...config, surfaces: ['default'] }
|
|
382
518
|
}
|
|
383
519
|
|
|
384
520
|
type TypedRef<T> = { $ref: string; path?: string } & { __phantomType?: T }
|
|
@@ -53,6 +53,10 @@ export const allWorkflow = pikkuWorkflowComplexFunc({
|
|
|
53
53
|
// schemas, for one — fails until these two files exist.
|
|
54
54
|
await workflow.do('Bootstrap scope definition types', 'pikkuScopeDefinitionTypes', null);
|
|
55
55
|
await workflow.do('Bootstrap scopes', 'pikkuScopes', { bootstrap: true });
|
|
56
|
+
await workflow.do('Bootstrap roles', 'pikkuRoles', { bootstrap: true });
|
|
57
|
+
await workflow.do('Bootstrap personas', 'pikkuPersonas', {
|
|
58
|
+
bootstrap: true,
|
|
59
|
+
});
|
|
56
60
|
await workflow.do('Bootstrap function types split', 'pikkuFunctionTypesSplit', { bootstrap: true });
|
|
57
61
|
// Pre-write a stub auth.types.ts (if the project uses better-auth) so the
|
|
58
62
|
// pikkuBetterAuth re-export resolves before any user file is imported.
|
|
@@ -155,6 +159,8 @@ export const allWorkflow = pikkuWorkflowComplexFunc({
|
|
|
155
159
|
workflow.do('Credentials', 'pikkuCredentials', null),
|
|
156
160
|
workflow.do('Scope definition types', 'pikkuScopeDefinitionTypes', null),
|
|
157
161
|
workflow.do('Scopes', 'pikkuScopes', {}),
|
|
162
|
+
workflow.do('Roles', 'pikkuRoles', {}),
|
|
163
|
+
workflow.do('Personas', 'pikkuPersonas', {}),
|
|
158
164
|
workflow.do('Variable definition types', 'pikkuVariableDefinitionTypes', null),
|
|
159
165
|
workflow.do('Variables', 'pikkuVariables', null),
|
|
160
166
|
workflow.do('Addon types', 'pikkuAddonTypes', null),
|
|
@@ -165,7 +171,7 @@ export const allWorkflow = pikkuWorkflowComplexFunc({
|
|
|
165
171
|
if (agents || !config.addon) {
|
|
166
172
|
await workflow.do('Re-inspect after agents', async () => getInspectorState(true));
|
|
167
173
|
}
|
|
168
|
-
// pikkuAuth generates auth-secrets.gen.ts (with
|
|
174
|
+
// pikkuAuth generates auth-secrets.gen.ts (with defineSecret calls) inside the
|
|
169
175
|
// Promise.all above, but pikkuSecrets runs concurrently from the pre-auth
|
|
170
176
|
// inspector state. Re-run secret/credential/variable codegen now so that the
|
|
171
177
|
// freshly-generated auth-secrets.gen.ts (just picked up by Re-inspect above)
|