@shipfox/expression 1.2.1 → 2.0.0

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 (41) hide show
  1. package/README.md +25 -12
  2. package/dist/expression/create-workflow-expression.d.ts.map +1 -1
  3. package/dist/expression/create-workflow-expression.js +5 -0
  4. package/dist/expression/create-workflow-expression.js.map +1 -1
  5. package/dist/index.d.ts +4 -3
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +3 -2
  8. package/dist/index.js.map +1 -1
  9. package/dist/plan/context-key-access.d.ts +2 -0
  10. package/dist/plan/context-key-access.d.ts.map +1 -1
  11. package/dist/plan/context-key-access.js +3 -0
  12. package/dist/plan/context-key-access.js.map +1 -1
  13. package/dist/plan/evaluate-planned-predicate.d.ts.map +1 -1
  14. package/dist/plan/evaluate-planned-predicate.js +2 -2
  15. package/dist/plan/evaluate-planned-predicate.js.map +1 -1
  16. package/dist/run/hoist-run-command.d.ts +8 -0
  17. package/dist/run/hoist-run-command.d.ts.map +1 -1
  18. package/dist/run/hoist-run-command.js +28 -3
  19. package/dist/run/hoist-run-command.js.map +1 -1
  20. package/dist/run/shell-code-position.d.ts +21 -0
  21. package/dist/run/shell-code-position.d.ts.map +1 -0
  22. package/dist/run/shell-code-position.js +623 -0
  23. package/dist/run/shell-code-position.js.map +1 -0
  24. package/dist/run/shell-quoting.d.ts +5 -1
  25. package/dist/run/shell-quoting.d.ts.map +1 -1
  26. package/dist/run/shell-quoting.js +37 -6
  27. package/dist/run/shell-quoting.js.map +1 -1
  28. package/dist/tsconfig.test.tsbuildinfo +1 -1
  29. package/dist/workflow-context/workflow-context-docs.d.ts +185 -0
  30. package/dist/workflow-context/workflow-context-docs.d.ts.map +1 -0
  31. package/dist/workflow-context/workflow-context-docs.js +168 -0
  32. package/dist/workflow-context/workflow-context-docs.js.map +1 -0
  33. package/dist/workflow-context/workflow-context.d.ts +99 -114
  34. package/dist/workflow-context/workflow-context.d.ts.map +1 -1
  35. package/dist/workflow-context/workflow-context.js +201 -85
  36. package/dist/workflow-context/workflow-context.js.map +1 -1
  37. package/package.json +2 -2
  38. package/dist/template/extract-cel-untrusted-path-accesses.d.ts +0 -5
  39. package/dist/template/extract-cel-untrusted-path-accesses.d.ts.map +0 -1
  40. package/dist/template/extract-cel-untrusted-path-accesses.js +0 -140
  41. package/dist/template/extract-cel-untrusted-path-accesses.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { outputDeclarationsToExpressionFields } from '../outputs/output-declarations.js';
2
2
  export const workflowContextNames = [
3
+ 'workflow',
3
4
  'run',
4
5
  'trigger',
5
6
  'event',
@@ -50,29 +51,48 @@ export const workflowContextReservedRoots = {
50
51
  host: 'runner'
51
52
  }
52
53
  };
53
- export const workflowContextTrustTiers = [
54
- 'trusted',
55
- 'untrusted'
56
- ];
54
+ // `workflow` holds definition facts and `run` holds instance facts, mirroring
55
+ // the `job` and `execution` pair one level down.
56
+ const workflowTypeEnvironment = {
57
+ workflow: {
58
+ kind: 'object',
59
+ fields: {
60
+ id: 'string',
61
+ name: 'string'
62
+ }
63
+ }
64
+ };
57
65
  const runTypeEnvironment = {
58
66
  run: {
59
67
  kind: 'object',
60
68
  fields: {
61
69
  id: 'string',
70
+ number: 'int',
62
71
  name: 'string',
63
- definition_id: 'string',
64
72
  project_id: 'string',
65
73
  workspace_id: 'string',
66
74
  created_at: 'timestamp'
67
75
  }
68
76
  }
69
77
  };
78
+ const triggerReferenceFields = {
79
+ project: {
80
+ kind: 'object',
81
+ fields: {
82
+ id: 'string'
83
+ }
84
+ },
85
+ repository: 'string',
86
+ ref: 'string',
87
+ commit: 'string'
88
+ };
70
89
  const triggerTypeEnvironment = {
71
90
  trigger: {
72
91
  kind: 'object',
73
92
  fields: {
74
93
  source: 'string',
75
- event: 'string'
94
+ event: 'string',
95
+ ...triggerReferenceFields
76
96
  }
77
97
  }
78
98
  };
@@ -80,7 +100,8 @@ const jobTypeEnvironment = {
80
100
  job: {
81
101
  kind: 'object',
82
102
  fields: {
83
- key: 'string'
103
+ key: 'string',
104
+ name: 'string'
84
105
  }
85
106
  }
86
107
  };
@@ -91,6 +112,7 @@ const executionEventType = {
91
112
  event: 'string',
92
113
  delivery_id: 'string',
93
114
  received_at: 'timestamp',
115
+ ...triggerReferenceFields,
94
116
  data: {
95
117
  kind: 'object',
96
118
  fields: {}
@@ -103,7 +125,6 @@ const executionType = {
103
125
  index: 'int',
104
126
  name: 'string',
105
127
  status: 'string',
106
- failed: 'bool',
107
128
  started_at: 'timestamp',
108
129
  finished_at: 'timestamp',
109
130
  events: {
@@ -122,7 +143,13 @@ const executionsTypeEnvironment = {
122
143
  }
123
144
  };
124
145
  const executionTypeEnvironment = {
125
- execution: executionType
146
+ execution: {
147
+ kind: 'object',
148
+ fields: {
149
+ ...executionType.fields,
150
+ failed: 'bool'
151
+ }
152
+ }
126
153
  };
127
154
  const stepGateType = {
128
155
  kind: 'object',
@@ -155,7 +182,7 @@ const stepEntityType = {
155
182
  }
156
183
  }
157
184
  };
158
- const stepTypeEnvironment = {
185
+ const stepDispatchTypeEnvironment = {
159
186
  step: {
160
187
  kind: 'object',
161
188
  fields: {
@@ -167,7 +194,14 @@ const stepTypeEnvironment = {
167
194
  from: stepEntityType,
168
195
  feedback: 'string'
169
196
  }
170
- },
197
+ }
198
+ }
199
+ }
200
+ };
201
+ const stepReportTypeEnvironment = {
202
+ step: {
203
+ kind: 'object',
204
+ fields: {
171
205
  exit_code: 'int',
172
206
  status: 'string',
173
207
  outputs: {
@@ -176,6 +210,15 @@ const stepTypeEnvironment = {
176
210
  }
177
211
  }
178
212
  };
213
+ const stepTypeEnvironment = {
214
+ step: {
215
+ kind: 'object',
216
+ fields: {
217
+ ...stepDispatchTypeEnvironment.step.fields,
218
+ ...stepReportTypeEnvironment.step.fields
219
+ }
220
+ }
221
+ };
179
222
  export function buildTypedRootsEnvironment(params) {
180
223
  return {
181
224
  ...params.steps === undefined ? {} : {
@@ -193,9 +236,16 @@ export function buildTypedRootsEnvironment(params) {
193
236
  };
194
237
  }
195
238
  export const workflowContextDefinitions = {
239
+ workflow: {
240
+ availability: 'run-creation',
241
+ sensitivity: 'persistable',
242
+ host: 'server',
243
+ shape: 'known',
244
+ checkMode: 'typed',
245
+ typeEnvironment: workflowTypeEnvironment
246
+ },
196
247
  run: {
197
248
  availability: 'run-creation',
198
- trustTier: 'trusted',
199
249
  sensitivity: 'persistable',
200
250
  host: 'server',
201
251
  shape: 'known',
@@ -204,7 +254,6 @@ export const workflowContextDefinitions = {
204
254
  },
205
255
  trigger: {
206
256
  availability: 'ingest',
207
- trustTier: 'trusted',
208
257
  sensitivity: 'persistable',
209
258
  host: 'server',
210
259
  shape: 'known',
@@ -213,7 +262,6 @@ export const workflowContextDefinitions = {
213
262
  },
214
263
  event: {
215
264
  availability: 'ingest',
216
- trustTier: 'untrusted',
217
265
  sensitivity: 'persistable',
218
266
  host: 'server',
219
267
  shape: 'open',
@@ -221,7 +269,6 @@ export const workflowContextDefinitions = {
221
269
  },
222
270
  inputs: {
223
271
  availability: 'run-creation',
224
- trustTier: 'untrusted',
225
272
  sensitivity: 'persistable',
226
273
  host: 'server',
227
274
  shape: 'open',
@@ -229,7 +276,6 @@ export const workflowContextDefinitions = {
229
276
  },
230
277
  job: {
231
278
  availability: 'run-creation',
232
- trustTier: 'trusted',
233
279
  sensitivity: 'persistable',
234
280
  host: 'server',
235
281
  shape: 'known',
@@ -238,31 +284,22 @@ export const workflowContextDefinitions = {
238
284
  },
239
285
  executions: {
240
286
  availability: 'execution-creation',
241
- trustTier: 'trusted',
242
287
  sensitivity: 'persistable',
243
288
  host: 'server',
244
289
  shape: 'known',
245
290
  checkMode: 'typed',
246
- typeEnvironment: executionsTypeEnvironment,
247
- untrustedPaths: [
248
- 'events'
249
- ]
291
+ typeEnvironment: executionsTypeEnvironment
250
292
  },
251
293
  execution: {
252
294
  availability: 'execution-creation',
253
- trustTier: 'trusted',
254
295
  sensitivity: 'persistable',
255
296
  host: 'server',
256
297
  shape: 'known',
257
298
  checkMode: 'typed',
258
- typeEnvironment: executionTypeEnvironment,
259
- untrustedPaths: [
260
- 'events'
261
- ]
299
+ typeEnvironment: executionTypeEnvironment
262
300
  },
263
301
  jobs: {
264
302
  availability: 'job-activation',
265
- trustTier: 'untrusted',
266
303
  sensitivity: 'persistable',
267
304
  host: 'server',
268
305
  shape: 'open',
@@ -270,7 +307,6 @@ export const workflowContextDefinitions = {
270
307
  },
271
308
  needs: {
272
309
  availability: 'job-activation',
273
- trustTier: 'untrusted',
274
310
  sensitivity: 'persistable',
275
311
  host: 'server',
276
312
  shape: 'known',
@@ -286,7 +322,6 @@ export const workflowContextDefinitions = {
286
322
  },
287
323
  steps: {
288
324
  availability: 'step-dispatch',
289
- trustTier: 'trusted',
290
325
  sensitivity: 'persistable',
291
326
  host: 'server',
292
327
  shape: 'open',
@@ -294,19 +329,14 @@ export const workflowContextDefinitions = {
294
329
  },
295
330
  step: {
296
331
  availability: 'step-dispatch',
297
- trustTier: 'trusted',
298
332
  sensitivity: 'persistable',
299
333
  host: 'server',
300
334
  shape: 'known',
301
335
  checkMode: 'typed',
302
- typeEnvironment: stepTypeEnvironment,
303
- untrustedPaths: [
304
- 'outputs'
305
- ]
336
+ typeEnvironment: stepTypeEnvironment
306
337
  },
307
338
  vars: {
308
339
  availability: 'run-creation',
309
- trustTier: 'trusted',
310
340
  sensitivity: 'persistable',
311
341
  host: 'server',
312
342
  shape: 'open',
@@ -314,7 +344,6 @@ export const workflowContextDefinitions = {
314
344
  literalKeyOnly: true
315
345
  },
316
346
  secrets: {
317
- trustTier: 'trusted',
318
347
  sensitivity: 'ephemeral',
319
348
  host: 'runner',
320
349
  shape: 'open',
@@ -327,13 +356,6 @@ export const workflowFieldFailurePolicies = [
327
356
  'degrade',
328
357
  'fail-closed'
329
358
  ];
330
- const trustedOnlyTrustTiers = [
331
- 'trusted'
332
- ];
333
- const anyTrustTier = [
334
- 'trusted',
335
- 'untrusted'
336
- ];
337
359
  const serverOnlyHosts = [
338
360
  'server'
339
361
  ];
@@ -343,71 +365,87 @@ const anyHost = [
343
365
  ];
344
366
  export const workflowInterpolationFieldPolicies = {
345
367
  run: {
346
- acceptedTrustTiers: trustedOnlyTrustTiers,
347
368
  acceptedHosts: anyHost,
348
- failurePolicy: 'fail',
349
- renderSanitize: false
369
+ failurePolicy: 'fail'
350
370
  },
351
371
  'env.value': {
352
- acceptedTrustTiers: anyTrustTier,
353
372
  acceptedHosts: anyHost,
354
- failurePolicy: 'fail',
355
- renderSanitize: false
373
+ failurePolicy: 'fail'
356
374
  },
357
375
  'agent.prompt': {
358
- acceptedTrustTiers: anyTrustTier,
359
376
  acceptedHosts: serverOnlyHosts,
360
- failurePolicy: 'fail',
361
- renderSanitize: false
377
+ failurePolicy: 'fail'
362
378
  },
363
379
  'agent.model': {
364
- acceptedTrustTiers: trustedOnlyTrustTiers,
365
380
  acceptedHosts: serverOnlyHosts,
366
- failurePolicy: 'fail',
367
- renderSanitize: false
381
+ failurePolicy: 'fail'
368
382
  },
369
383
  'agent.provider': {
370
- acceptedTrustTiers: trustedOnlyTrustTiers,
371
384
  acceptedHosts: serverOnlyHosts,
372
- failurePolicy: 'fail',
373
- renderSanitize: false
385
+ failurePolicy: 'fail'
374
386
  },
375
387
  'agent.thinking': {
376
- acceptedTrustTiers: trustedOnlyTrustTiers,
377
388
  acceptedHosts: serverOnlyHosts,
378
- failurePolicy: 'fail',
379
- renderSanitize: false
389
+ failurePolicy: 'fail'
380
390
  },
381
391
  'job.runner': {
382
- acceptedTrustTiers: anyTrustTier,
383
392
  acceptedHosts: serverOnlyHosts,
384
- failurePolicy: 'fail',
385
- renderSanitize: false
393
+ failurePolicy: 'fail'
386
394
  },
387
395
  'job.outputs': {
388
- acceptedTrustTiers: anyTrustTier,
389
396
  acceptedHosts: serverOnlyHosts,
390
397
  failurePolicy: 'fail',
391
- renderSanitize: false,
392
398
  minimumFillTarget: 'execution-resolution'
393
399
  },
394
- 'job.name': {
395
- acceptedTrustTiers: anyTrustTier,
400
+ 'workflow.run_name': {
396
401
  acceptedHosts: serverOnlyHosts,
397
402
  failurePolicy: 'degrade',
398
- renderSanitize: true
403
+ minimumFillTarget: 'run-creation',
404
+ selfReference: {
405
+ root: 'run',
406
+ key: 'name'
407
+ }
399
408
  },
400
- 'step.name': {
401
- acceptedTrustTiers: anyTrustTier,
409
+ 'job.execution_name': {
402
410
  acceptedHosts: serverOnlyHosts,
403
411
  failurePolicy: 'degrade',
404
- renderSanitize: true
412
+ minimumFillTarget: 'execution-creation',
413
+ selfReference: {
414
+ root: 'execution',
415
+ key: 'name'
416
+ }
417
+ },
418
+ 'step.name': {
419
+ acceptedHosts: serverOnlyHosts,
420
+ failurePolicy: 'degrade'
421
+ },
422
+ 'step.working_directory': {
423
+ acceptedHosts: serverOnlyHosts,
424
+ failurePolicy: 'fail'
405
425
  },
406
426
  'step.feedback': {
407
- acceptedTrustTiers: anyTrustTier,
408
427
  acceptedHosts: serverOnlyHosts,
409
- failurePolicy: 'fail',
410
- renderSanitize: false
428
+ failurePolicy: 'fail'
429
+ },
430
+ 'checkout.project': {
431
+ acceptedHosts: serverOnlyHosts,
432
+ failurePolicy: 'fail'
433
+ },
434
+ 'checkout.connection': {
435
+ acceptedHosts: serverOnlyHosts,
436
+ failurePolicy: 'fail'
437
+ },
438
+ 'checkout.repository': {
439
+ acceptedHosts: serverOnlyHosts,
440
+ failurePolicy: 'fail'
441
+ },
442
+ 'checkout.ref': {
443
+ acceptedHosts: serverOnlyHosts,
444
+ failurePolicy: 'fail'
445
+ },
446
+ 'checkout.path': {
447
+ acceptedHosts: serverOnlyHosts,
448
+ failurePolicy: 'fail'
411
449
  }
412
450
  };
413
451
  export const workflowInterpolationFields = Object.keys(workflowInterpolationFieldPolicies);
@@ -420,6 +458,56 @@ export const workflowPredicateFields = [
420
458
  'job.if',
421
459
  'step.if'
422
460
  ];
461
+ const listenerPredicateContextRoots = [
462
+ 'event',
463
+ 'workflow',
464
+ 'run',
465
+ 'trigger',
466
+ 'inputs',
467
+ 'vars',
468
+ 'job',
469
+ 'jobs'
470
+ ];
471
+ /**
472
+ * The roots that the runtime passes to each server-evaluated predicate.
473
+ *
474
+ * This is intentionally narrower than lifecycle availability. A root can
475
+ * exist by the time a predicate runs and still not belong to that predicate's
476
+ * evaluation context.
477
+ */ export const workflowPredicateContextRoots = {
478
+ 'step.success': [
479
+ 'step',
480
+ 'vars'
481
+ ],
482
+ 'job.success': [
483
+ 'executions',
484
+ 'jobs',
485
+ 'vars'
486
+ ],
487
+ 'trigger.filter': [
488
+ 'event',
489
+ 'trigger'
490
+ ],
491
+ 'listener.on': listenerPredicateContextRoots,
492
+ 'listener.until': listenerPredicateContextRoots,
493
+ 'job.if': [
494
+ 'workflow',
495
+ 'run',
496
+ 'trigger',
497
+ 'event',
498
+ 'inputs',
499
+ 'vars',
500
+ 'jobs',
501
+ 'needs'
502
+ ],
503
+ 'step.if': [
504
+ 'vars',
505
+ 'jobs',
506
+ 'execution',
507
+ 'step',
508
+ 'steps'
509
+ ]
510
+ };
423
511
  export const workflowPredicateFieldFailurePolicy = 'fail-closed';
424
512
  const workflowPredicateFieldMinimumFillTargets = {
425
513
  'step.success': 'step-report',
@@ -430,6 +518,10 @@ const workflowPredicateFieldMinimumFillTargets = {
430
518
  'job.if': 'job-activation',
431
519
  'step.if': 'step-dispatch'
432
520
  };
521
+ const workflowPredicateFieldTypeEnvironments = {
522
+ 'step.success': stepReportTypeEnvironment,
523
+ 'step.if': stepDispatchTypeEnvironment
524
+ };
433
525
  export function getWorkflowContextDefinition(name) {
434
526
  return workflowContextDefinitions[name];
435
527
  }
@@ -472,19 +564,9 @@ export function getWorkflowContextTypeEnvironment(name) {
472
564
  const context = getWorkflowContextDefinition(name);
473
565
  return context.shape === 'known' ? context.typeEnvironment : undefined;
474
566
  }
475
- export function getWorkflowContextUntrustedPaths(name) {
476
- const context = getWorkflowContextDefinition(name);
477
- return context.shape === 'known' ? context.untrustedPaths : undefined;
478
- }
479
- export function workflowInterpolationFieldAcceptsTrustTier(field, trustTier) {
480
- return workflowInterpolationFieldPolicies[field].acceptedTrustTiers.includes(trustTier);
481
- }
482
567
  export function workflowInterpolationFieldAcceptsHost(field, host) {
483
568
  return workflowInterpolationFieldPolicies[field].acceptedHosts.includes(host);
484
569
  }
485
- export function workflowInterpolationFieldAcceptsContext(field, context) {
486
- return workflowInterpolationFieldAcceptsTrustTier(field, workflowContextDefinitions[context].trustTier);
487
- }
488
570
  export function workflowContextRootRequiresLiteralKey(root) {
489
571
  if (!isWorkflowContextName(root)) return false;
490
572
  const definition = workflowContextDefinitions[root];
@@ -493,6 +575,9 @@ export function workflowContextRootRequiresLiteralKey(root) {
493
575
  export function getWorkflowInterpolationFieldFailurePolicy(field) {
494
576
  return workflowInterpolationFieldPolicies[field].failurePolicy;
495
577
  }
578
+ export function getWorkflowInterpolationFieldSelfReference(field) {
579
+ return workflowInterpolationFieldPolicies[field].selfReference;
580
+ }
496
581
  export function getWorkflowInterpolationFieldMinimumFillTarget(field) {
497
582
  const policy = workflowInterpolationFieldPolicies[field];
498
583
  return 'minimumFillTarget' in policy ? policy.minimumFillTarget : undefined;
@@ -500,6 +585,37 @@ export function getWorkflowInterpolationFieldMinimumFillTarget(field) {
500
585
  export function getWorkflowPredicateFieldMinimumFillTarget(field) {
501
586
  return workflowPredicateFieldMinimumFillTargets[field];
502
587
  }
588
+ export function getWorkflowPredicateFieldTypeEnvironment(field, root) {
589
+ const typeEnvironment = workflowPredicateFieldTypeEnvironments[field];
590
+ if (typeEnvironment === undefined) return getWorkflowContextTypeEnvironment(root);
591
+ const rootType = typeEnvironment[root];
592
+ return rootType === undefined ? getWorkflowContextTypeEnvironment(root) : {
593
+ [root]: rootType
594
+ };
595
+ }
596
+ export function getWorkflowPredicateContextRoots(field) {
597
+ return workflowPredicateContextRoots[field];
598
+ }
599
+ /**
600
+ * The roots a field can read, whichever kind of field it is.
601
+ *
602
+ * A predicate has an enumerated contract because the runtime assembles one
603
+ * context for it. A template has no single context: each reference is filled
604
+ * where its data arrives, so its only field-level constraint is which host can
605
+ * resolve it. Callers that document or check fields should use this instead of
606
+ * choosing a mechanism themselves.
607
+ */ export function contextRootsForField(field) {
608
+ if (isWorkflowPredicateField(field)) return workflowPredicateContextRoots[field];
609
+ const { acceptedHosts } = workflowInterpolationFieldPolicies[field];
610
+ return workflowContextNames.filter((name)=>acceptedHosts.includes(workflowContextDefinitions[name].host));
611
+ }
612
+ export function isWorkflowPredicateField(field) {
613
+ return workflowPredicateFields.includes(field);
614
+ }
615
+ export function projectWorkflowPredicateContext(field, context) {
616
+ const roots = new Set(getWorkflowPredicateContextRoots(field));
617
+ return Object.fromEntries(Object.entries(context).filter(([root])=>roots.has(root)));
618
+ }
503
619
  export function workflowContextAvailabilityReference() {
504
620
  return [
505
621
  ...workflowContextNames.map((root)=>{