@pikku/core 0.12.82 → 0.12.83

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/services/index.d.ts +5 -6
  3. package/dist/services/index.js +2 -2
  4. package/dist/services/meta-service.d.ts +2 -1
  5. package/dist/types/core.types.d.ts +2 -1
  6. package/dist/types/state.types.d.ts +6 -1
  7. package/dist/wirings/ai-agent/ai-agent-prepare.js +2 -0
  8. package/dist/wirings/rpc/addon-runner.d.ts +4 -0
  9. package/dist/wirings/rpc/addon-runner.js +14 -2
  10. package/dist/wirings/rpc/rpc-runner.js +2 -0
  11. package/dist/wirings/rpc/rpc-types.d.ts +4 -0
  12. package/dist/wirings/rpc/wire-addon.d.ts +12 -0
  13. package/dist/wirings/rpc/wire-addon.js +4 -0
  14. package/dist/wirings/workflow/feature.d.ts +2 -1
  15. package/dist/wirings/workflow/index.d.ts +3 -14
  16. package/dist/wirings/workflow/index.js +0 -8
  17. package/dist/wirings/workflow/pikku-scenario-service.d.ts +10 -0
  18. package/dist/wirings/workflow/pikku-scenario-service.js +9 -0
  19. package/dist/wirings/workflow/scenario.types.d.ts +37 -0
  20. package/dist/wirings/workflow/scenario.types.js +1 -0
  21. package/dist/wirings/workflow/workflow.types.d.ts +2 -37
  22. package/package.json +1 -2
  23. package/src/public-surface.json +11 -24
  24. package/src/services/index.ts +4 -15
  25. package/src/services/meta-service.ts +2 -4
  26. package/src/types/core.types.ts +1 -1
  27. package/src/types/state.types.ts +5 -1
  28. package/src/wirings/ai-agent/ai-agent-prepare.ts +2 -0
  29. package/src/wirings/rpc/addon-runner.ts +31 -3
  30. package/src/wirings/rpc/addon-secrets.test.ts +130 -0
  31. package/src/wirings/rpc/rpc-runner.ts +2 -0
  32. package/src/wirings/rpc/rpc-types.ts +4 -0
  33. package/src/wirings/rpc/wire-addon.ts +16 -0
  34. package/src/wirings/workflow/feature.ts +2 -5
  35. package/src/wirings/workflow/index.ts +1 -49
  36. package/src/wirings/workflow/pikku-scenario-service.ts +21 -1
  37. package/src/wirings/workflow/scenario.types.ts +63 -0
  38. package/src/wirings/workflow/workflow.types.ts +1 -54
  39. package/tsconfig.tsbuildinfo +1 -1
@@ -47,9 +47,9 @@ import type {
47
47
  } from '../wirings/scheduler/scheduler.types.js'
48
48
  import type {
49
49
  CoreWorkflow,
50
- CoreFeature,
51
50
  WorkflowsRuntimeMeta,
52
51
  } from '../wirings/workflow/workflow.types.js'
52
+ import type { CoreFeature } from '../wirings/workflow/scenario.types.js'
53
53
  import type {
54
54
  CoreTrigger,
55
55
  CoreTriggerSource,
@@ -102,6 +102,10 @@ export interface PikkuPackageState {
102
102
  variableOverrides?: Record<string, string>
103
103
  /** Per-instance name-aliases: logical name the addon reads -> actual project credential name */
104
104
  credentialOverrides?: Record<string, string>
105
+ /** Secrets the host lends this instance, named as the addon reads them */
106
+ secretGrants?: string[]
107
+ /** Credentials the host lends this instance, named as the addon reads them */
108
+ credentialGrants?: string[]
105
109
  /** Why this instance gets the whole `SecretService` rather than one scoped to its declared secrets */
106
110
  globalSecrets?: string
107
111
  /** Why this instance gets the whole `CredentialService` rather than one scoped to its declared credentials */
@@ -565,6 +565,8 @@ export async function buildToolDefs(
565
565
  variableOverrides: capturedAddonConfig?.variableOverrides,
566
566
  credentialOverrides:
567
567
  capturedAddonConfig?.credentialOverrides,
568
+ secretGrants: capturedAddonConfig?.secretGrants,
569
+ credentialGrants: capturedAddonConfig?.credentialGrants,
568
570
  globalSecrets: capturedAddonConfig?.globalSecrets,
569
571
  globalCredentials: capturedAddonConfig?.globalCredentials,
570
572
  }
@@ -13,12 +13,32 @@ export type AddonInstance = {
13
13
  secretOverrides?: Record<string, string>
14
14
  variableOverrides?: Record<string, string>
15
15
  credentialOverrides?: Record<string, string>
16
+ /** Set by the consuming app: secrets it lends this instance, as the addon names them. */
17
+ secretGrants?: string[]
18
+ /** Set by the consuming app: credentials it lends this instance, as the addon names them. */
19
+ credentialGrants?: string[]
16
20
  /** Set by the consuming app to opt this instance out of secret scoping. */
17
21
  globalSecrets?: string
18
22
  /** Set by the consuming app to opt this instance out of credential scoping. */
19
23
  globalCredentials?: string
20
24
  }
21
25
 
26
+ /**
27
+ * What the addon declared, plus what the host lent it. Scoping runs outside the
28
+ * aliaser, so every name here is the one the addon reads — which is why an
29
+ * override's *key* grants, and its value does not.
30
+ */
31
+ const allowedNames = (
32
+ declared: string[] | null | undefined,
33
+ grants: string[] | undefined,
34
+ overrides: Record<string, string> | undefined
35
+ ): Set<string> =>
36
+ new Set([
37
+ ...(declared ?? []),
38
+ ...(grants ?? []),
39
+ ...Object.keys(overrides ?? {}),
40
+ ])
41
+
22
42
  const aliasSecretService = (
23
43
  secrets: SecretService,
24
44
  overrides: Record<string, string>
@@ -142,7 +162,11 @@ export const getOrCreatePackageSingletonServices = async (
142
162
  ...existingServices,
143
163
  secrets: new ScopedSecretService(
144
164
  existingServices.secrets,
145
- new Set(pikkuState(packageName, 'package', 'declaredSecrets') ?? [])
165
+ allowedNames(
166
+ pikkuState(packageName, 'package', 'declaredSecrets'),
167
+ addonInstance?.secretGrants,
168
+ addonInstance?.secretOverrides
169
+ )
146
170
  ),
147
171
  }
148
172
  }
@@ -151,10 +175,12 @@ export const getOrCreatePackageSingletonServices = async (
151
175
  ...existingServices,
152
176
  credentialService: new ScopedCredentialService(
153
177
  existingServices.credentialService,
154
- new Set(
178
+ allowedNames(
155
179
  Object.keys(
156
180
  pikkuState(packageName, 'package', 'credentialsMeta') ?? {}
157
- )
181
+ ),
182
+ addonInstance?.credentialGrants,
183
+ addonInstance?.credentialOverrides
158
184
  )
159
185
  ),
160
186
  }
@@ -202,6 +228,8 @@ export const addonInstanceForNamespace = (
202
228
  secretOverrides: cfg.secretOverrides,
203
229
  variableOverrides: cfg.variableOverrides,
204
230
  credentialOverrides: cfg.credentialOverrides,
231
+ secretGrants: cfg.secretGrants,
232
+ credentialGrants: cfg.credentialGrants,
205
233
  globalSecrets: cfg.globalSecrets,
206
234
  globalCredentials: cfg.globalCredentials,
207
235
  }
@@ -259,3 +259,133 @@ describe('addon credentials are scoped to what the addon declared', () => {
259
259
  assert.deepEqual(await credentials.getAllUsers(), ['user-1'])
260
260
  })
261
261
  })
262
+
263
+ /**
264
+ * An addon whose secret names come off its input rather than its own source
265
+ * declares nothing, so the host lends it names it could not have derived.
266
+ *
267
+ * knowledge: decisions/security/decision-secretless-functions.md
268
+ */
269
+ describe('the host grants an addon secrets it could not declare', () => {
270
+ beforeEach(() => {
271
+ resetPikkuState()
272
+ })
273
+
274
+ test('a granted secret is readable', async () => {
275
+ registerAddon([])
276
+ wireAddon({
277
+ name: 'example',
278
+ package: ADDON_PACKAGE,
279
+ secretGrants: ['STRIPE_KEY'],
280
+ })
281
+
282
+ const secrets = await secretsHandedToAddon('example')
283
+ assert.equal(await secrets.getSecret('STRIPE_KEY'), 'value-of-STRIPE_KEY')
284
+ })
285
+
286
+ test('a secret the host did not grant is still denied', async () => {
287
+ registerAddon([])
288
+ wireAddon({
289
+ name: 'example',
290
+ package: ADDON_PACKAGE,
291
+ secretGrants: ['STRIPE_KEY'],
292
+ })
293
+
294
+ const secrets = await secretsHandedToAddon('example')
295
+ await assert.rejects(() => secrets.getSecret('DATABASE_URL'), /denied/i)
296
+ })
297
+
298
+ test('a grant adds to what the addon declared rather than replacing it', async () => {
299
+ registerAddon(['ADDON_KEY'])
300
+ wireAddon({
301
+ name: 'example',
302
+ package: ADDON_PACKAGE,
303
+ secretGrants: ['STRIPE_KEY'],
304
+ })
305
+
306
+ const secrets = await secretsHandedToAddon('example')
307
+ assert.equal(await secrets.getSecret('ADDON_KEY'), 'value-of-ADDON_KEY')
308
+ assert.equal(await secrets.getSecret('STRIPE_KEY'), 'value-of-STRIPE_KEY')
309
+ })
310
+
311
+ test('an overridden secret is granted by the override itself', async () => {
312
+ registerAddon([])
313
+ wireAddon({
314
+ name: 'example',
315
+ package: ADDON_PACKAGE,
316
+ secretOverrides: { MAILGUN_KEY: 'PROD_EMAIL_KEY' },
317
+ })
318
+
319
+ const secrets = await secretsHandedToAddon('example')
320
+ assert.equal(
321
+ await secrets.getSecret('MAILGUN_KEY'),
322
+ 'value-of-PROD_EMAIL_KEY'
323
+ )
324
+ })
325
+
326
+ test('a grant is checked before the override renames it', async () => {
327
+ registerAddon([])
328
+ wireAddon({
329
+ name: 'example',
330
+ package: ADDON_PACKAGE,
331
+ secretGrants: ['MAILGUN_KEY'],
332
+ secretOverrides: { MAILGUN_KEY: 'PROD_EMAIL_KEY' },
333
+ })
334
+
335
+ const secrets = await secretsHandedToAddon('example')
336
+ assert.equal(
337
+ await secrets.getSecret('MAILGUN_KEY'),
338
+ 'value-of-PROD_EMAIL_KEY'
339
+ )
340
+ await assert.rejects(() => secrets.getSecret('PROD_EMAIL_KEY'), /denied/i)
341
+ })
342
+
343
+ test('a granted credential is readable', async () => {
344
+ registerAddon([], [])
345
+ wireAddon({
346
+ name: 'example',
347
+ package: ADDON_PACKAGE,
348
+ credentialGrants: ['stripe'],
349
+ })
350
+
351
+ const credentials = await credentialsHandedToAddon('example')
352
+ assert.equal(await credentials.get('stripe'), 'credential-of-stripe')
353
+ await assert.rejects(() => credentials.get('slack'), /denied/i)
354
+ })
355
+
356
+ test('a granted credential does not widen the rest of the service', async () => {
357
+ registerAddon([], [])
358
+ wireAddon({
359
+ name: 'example',
360
+ package: ADDON_PACKAGE,
361
+ credentialGrants: ['stripe'],
362
+ })
363
+
364
+ const credentials = await credentialsHandedToAddon('example')
365
+ await assert.rejects(() => credentials.getAllUsers(), /denied/i)
366
+ })
367
+
368
+ test('an addon without a singleton factory is granted too', async () => {
369
+ pikkuState(ADDON_PACKAGE, 'package', 'declaredSecrets', [])
370
+ wireAddon({
371
+ name: 'example',
372
+ package: ADDON_PACKAGE,
373
+ secretGrants: ['STRIPE_KEY'],
374
+ })
375
+
376
+ const services = await getOrCreatePackageSingletonServices(
377
+ ADDON_PACKAGE,
378
+ createParentServices(),
379
+ pikkuState(null, 'addons', 'packages').get('example') as never
380
+ )
381
+
382
+ assert.equal(
383
+ await services.secrets.getSecret('STRIPE_KEY'),
384
+ 'value-of-STRIPE_KEY'
385
+ )
386
+ await assert.rejects(
387
+ () => services.secrets.getSecret('DATABASE_URL'),
388
+ /denied/i
389
+ )
390
+ })
391
+ })
@@ -284,6 +284,8 @@ export class ContextAwareRPCService {
284
284
  secretOverrides: resolved.addonConfig?.secretOverrides,
285
285
  variableOverrides: resolved.addonConfig?.variableOverrides,
286
286
  credentialOverrides: resolved.addonConfig?.credentialOverrides,
287
+ secretGrants: resolved.addonConfig?.secretGrants,
288
+ credentialGrants: resolved.addonConfig?.credentialGrants,
287
289
  globalSecrets: resolved.addonConfig?.globalSecrets,
288
290
  globalCredentials: resolved.addonConfig?.globalCredentials,
289
291
  },
@@ -67,6 +67,10 @@ export interface ResolvedFunction {
67
67
  secretOverrides?: Record<string, string>
68
68
  variableOverrides?: Record<string, string>
69
69
  credentialOverrides?: Record<string, string>
70
+ /** Set by the consuming app: secrets it lends this instance, as the addon names them */
71
+ secretGrants?: string[]
72
+ /** Set by the consuming app: credentials it lends this instance, as the addon names them */
73
+ credentialGrants?: string[]
70
74
  /** Set by the consuming app: hand this instance the unscoped `SecretService` */
71
75
  globalSecrets?: string
72
76
  /** Set by the consuming app: hand this instance the unscoped `CredentialService` */
@@ -14,6 +14,18 @@ export type WireAddonConfig = {
14
14
  secretOverrides?: Record<string, string>
15
15
  variableOverrides?: Record<string, string>
16
16
  credentialOverrides?: Record<string, string>
17
+ /**
18
+ * Secrets this instance may read on top of the ones it declared, named as the
19
+ * addon reads them — the scope check runs before `secretOverrides` renames
20
+ * them, so an overridden secret is named here by its addon-side key. Listing
21
+ * one in `secretOverrides` grants it too.
22
+ *
23
+ * For an addon whose secret names come off its input rather than its own
24
+ * source, this is how the host lends names the addon could not declare.
25
+ */
26
+ secretGrants?: string[]
27
+ /** Credentials this instance may read on top of the ones it declared. */
28
+ credentialGrants?: string[]
17
29
  /**
18
30
  * Hands this instance the whole `SecretService` instead of one scoped to the
19
31
  * secrets it declared. The value is the reason, recorded in the deploy
@@ -45,6 +57,10 @@ export const wireAddon = (config: WireAddonConfig): void => {
45
57
  ...(config.credentialOverrides
46
58
  ? { credentialOverrides: config.credentialOverrides }
47
59
  : {}),
60
+ ...(config.secretGrants ? { secretGrants: config.secretGrants } : {}),
61
+ ...(config.credentialGrants
62
+ ? { credentialGrants: config.credentialGrants }
63
+ : {}),
48
64
  ...(config.globalSecrets ? { globalSecrets: config.globalSecrets } : {}),
49
65
  ...(config.globalCredentials
50
66
  ? { globalCredentials: config.globalCredentials }
@@ -1,9 +1,6 @@
1
1
  import { pikkuState } from '../../pikku-state.js'
2
- import type {
3
- CoreFeature,
4
- CoreWorkflow,
5
- FeaturePlanEntry,
6
- } from './workflow.types.js'
2
+ import type { CoreWorkflow } from './workflow.types.js'
3
+ import type { CoreFeature, FeaturePlanEntry } from './scenario.types.js'
7
4
 
8
5
  export const addFeature = (
9
6
  featureId: string,
@@ -28,13 +28,9 @@ export {
28
28
  reconstructStateAt,
29
29
  reconstructFinalState,
30
30
  } from './run-timeline.js'
31
- export type {
32
- RunTimeline,
33
- ReconstructedRunState,
34
- } from './run-timeline.js'
31
+ export type { RunTimeline, ReconstructedRunState } from './run-timeline.js'
35
32
 
36
33
  export { addWorkflow } from './dsl/workflow-runner.js'
37
- export { addFeature, resolveFeatureScenarios } from './feature.js'
38
34
 
39
35
  export { template, type TemplateString } from './graph/template.js'
40
36
  export {
@@ -63,10 +59,6 @@ export type {
63
59
  WorkflowRunService,
64
60
  WorkflowRunMirror,
65
61
  CoreWorkflow,
66
- CoreFeature,
67
- FeatureMeta,
68
- FeaturesMeta,
69
- FeaturePlanEntry,
70
62
  PikkuWorkflow,
71
63
  ContextVariable,
72
64
  WorkflowContext,
@@ -97,47 +89,7 @@ export type {
97
89
  SwitchStepMeta,
98
90
  FilterStepMeta,
99
91
  ArrayPredicateStepMeta,
100
- ScenarioStepInvocation,
101
- ScenarioStepMeta,
102
92
  WorkflowStepMeta,
103
93
  WorkflowStepWire,
104
94
  PikkuWorkflowWire,
105
- PikkuScenarioWire,
106
95
  } from './workflow.types.js'
107
-
108
- export type {
109
- ScenarioStepPhase,
110
- ScenarioStepKind,
111
- ScenarioStepOptions,
112
- PikkuScenarioStepWire,
113
- ScenarioEnvironment,
114
- ScenarioSurface,
115
- ScenarioSurfaceResolution,
116
- } from './scenario-step.types.js'
117
- export { SCENARIO_SURFACES } from './scenario-step.types.js'
118
-
119
- // Which of a step's bindings run: one for an action, every witness for a `then`
120
- export { resolveScenarioSurfaces } from './scenario-surface.js'
121
-
122
- export { requireActor, requireScenarioEnv } from './scenario-step-guards.js'
123
-
124
- export { pollUntil, type PollOptions } from './scenario-poll.js'
125
-
126
- export { createCookieJar } from './scenario-cookie-jar.js'
127
- export type { ScenarioCookieJar } from './scenario-cookie-jar.js'
128
-
129
- export type {
130
- ScenarioHttpResponse,
131
- ScenarioJsonRequest,
132
- } from '../../services/personas-service.js'
133
- // The readers themselves live on `@pikku/core/persona`; workflow is a production
134
- // wiring and must not pull scenario runtime in behind it.
135
-
136
- export type {
137
- PikkuBrowserWire,
138
- TestIdSelector,
139
- ScenarioBrowserProvider,
140
- ScenarioBrowserFailure,
141
- } from './scenario-step.types.js'
142
-
143
- export { composeStepProse, renderStepTemplate } from './scenario-prose.js'
@@ -27,10 +27,10 @@ import type {
27
27
  } from './scenario-step.types.js'
28
28
  import { resolveScenarioSurfaces, witnessesAgree } from './scenario-surface.js'
29
29
  import type {
30
- PikkuScenarioWire,
31
30
  PikkuWorkflowWire,
32
31
  WorkflowQueueOptions,
33
32
  } from './workflow.types.js'
33
+ import type { PikkuScenarioWire } from './scenario.types.js'
34
34
  import type {
35
35
  WorkflowExpectEventuallyOptions,
36
36
  WorkflowExpectErrorOptions,
@@ -38,6 +38,26 @@ import type {
38
38
  WorkflowExpectServiceOptions,
39
39
  } from './dsl/workflow-dsl.types.js'
40
40
 
41
+ export { addFeature, resolveFeatureScenarios } from './feature.js'
42
+ export type * from './scenario.types.js'
43
+ export { SCENARIO_SURFACES } from './scenario-step.types.js'
44
+
45
+ // Which of a step's bindings run: one for an action, every witness for a `then`
46
+ export { resolveScenarioSurfaces } from './scenario-surface.js'
47
+
48
+ export { pollUntil, type PollOptions } from './scenario-poll.js'
49
+ export { createCookieJar } from './scenario-cookie-jar.js'
50
+ export type { ScenarioCookieJar } from './scenario-cookie-jar.js'
51
+ export { composeStepProse, renderStepTemplate } from './scenario-prose.js'
52
+
53
+ export { requireActor, requireScenarioEnv } from './scenario-step-guards.js'
54
+
55
+ export type {
56
+ ScenarioHttpResponse,
57
+ ScenarioJsonRequest,
58
+ } from '../../services/personas-service.js'
59
+ // The readers themselves live on `@pikku/core/persona`
60
+
41
61
  /**
42
62
  * A workflow service with the scenario capability attached — the two lines
43
63
  * `pikku scenario run` needs, in one call so no caller has to remember that the
@@ -0,0 +1,63 @@
1
+ import type {
2
+ CorePikkuFunctionConfig,
3
+ CorePikkuFunctionHook,
4
+ } from '../../function/functions.types.js'
5
+
6
+ export type {
7
+ ScenarioStepInvocation,
8
+ ScenarioStepMeta,
9
+ PikkuScenarioWire,
10
+ } from './dsl/workflow-dsl.types.js'
11
+
12
+ export type {
13
+ ScenarioStepPhase,
14
+ ScenarioStepKind,
15
+ ScenarioStepOptions,
16
+ PikkuScenarioStepWire,
17
+ ScenarioEnvironment,
18
+ ScenarioSurface,
19
+ ScenarioSurfaceResolution,
20
+ PikkuBrowserWire,
21
+ TestIdSelector,
22
+ ScenarioBrowserProvider,
23
+ ScenarioBrowserFailure,
24
+ } from './scenario-step.types.js'
25
+
26
+ export type CoreFeatureScenario =
27
+ | CorePikkuFunctionConfig<any, any, any>
28
+ | { scenario: CorePikkuFunctionConfig<any, any, any>; data: unknown }
29
+
30
+ export type CoreFeature = {
31
+ name: string
32
+ description?: string
33
+ tags?: string[]
34
+ scenarios: readonly CoreFeatureScenario[]
35
+ before?: CorePikkuFunctionHook
36
+ after?: CorePikkuFunctionHook
37
+ }
38
+
39
+ export type FeatureMetaEntry = {
40
+ scenario: string
41
+ data?: unknown
42
+ }
43
+
44
+ export type FeatureMeta = {
45
+ id: string
46
+ name: string
47
+ description?: string
48
+ tags: string[]
49
+ entries: FeatureMetaEntry[]
50
+ unresolvedEntries: number
51
+ hasBefore: boolean
52
+ hasAfter: boolean
53
+ }
54
+
55
+ export type FeaturesMeta = Record<string, FeatureMeta>
56
+
57
+ export type FeaturePlanEntry = {
58
+ featureId: string
59
+ featureName: string
60
+ scenarioName: string
61
+ data?: unknown
62
+ tags: string[]
63
+ }
@@ -1,8 +1,5 @@
1
1
  import type { SerializedError, CommonWireMeta } from '../../types/core.types.js'
2
- import type {
3
- CorePikkuFunctionConfig,
4
- CorePikkuFunctionHook,
5
- } from '../../function/functions.types.js'
2
+ import type { CorePikkuFunctionConfig } from '../../function/functions.types.js'
6
3
  import type { GroupConcurrencyConfig } from '../queue/queue.types.js'
7
4
 
8
5
  export type { WorkflowService } from '../../services/workflow-service.js'
@@ -30,22 +27,11 @@ export type {
30
27
  SwitchStepMeta,
31
28
  FilterStepMeta,
32
29
  ArrayPredicateStepMeta,
33
- ScenarioStepInvocation,
34
- ScenarioStepMeta,
35
30
  WorkflowStepMeta,
36
31
  WorkflowStepWire,
37
32
  PikkuWorkflowWire,
38
- PikkuScenarioWire,
39
33
  } from './dsl/workflow-dsl.types.js'
40
34
 
41
- export type {
42
- ScenarioStepPhase,
43
- ScenarioStepOptions,
44
- PikkuScenarioStepWire,
45
- PikkuBrowserWire,
46
- ScenarioBrowserProvider,
47
- } from './scenario-step.types.js'
48
-
49
35
  import type { WorkflowStepMeta } from './dsl/workflow-dsl.types.js'
50
36
 
51
37
  export interface WorkflowRunWire {
@@ -238,45 +224,6 @@ export type CoreWorkflow<
238
224
  tags?: string[]
239
225
  }
240
226
 
241
- export type CoreFeatureScenario =
242
- | CorePikkuFunctionConfig<any, any, any>
243
- | { scenario: CorePikkuFunctionConfig<any, any, any>; data: unknown }
244
-
245
- export type CoreFeature = {
246
- name: string
247
- description?: string
248
- tags?: string[]
249
- scenarios: readonly CoreFeatureScenario[]
250
- before?: CorePikkuFunctionHook
251
- after?: CorePikkuFunctionHook
252
- }
253
-
254
- export type FeatureMetaEntry = {
255
- scenario: string
256
- data?: unknown
257
- }
258
-
259
- export type FeatureMeta = {
260
- id: string
261
- name: string
262
- description?: string
263
- tags: string[]
264
- entries: FeatureMetaEntry[]
265
- unresolvedEntries: number
266
- hasBefore: boolean
267
- hasAfter: boolean
268
- }
269
-
270
- export type FeaturesMeta = Record<string, FeatureMeta>
271
-
272
- export type FeaturePlanEntry = {
273
- featureId: string
274
- featureName: string
275
- scenarioName: string
276
- data?: unknown
277
- tags: string[]
278
- }
279
-
280
227
  export interface PikkuWorkflow {
281
228
  start: <I>(input: I) => Promise<{ runId: string }>
282
229
  getRun: (runId: string) => Promise<WorkflowRun>