@lchase/yak 0.4.0 → 0.4.1

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 (2) hide show
  1. package/dist/index.js +37 -0
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -488,6 +488,7 @@ async function validateWorkflow(workflow, cwd = process.cwd(), runIsolation = "n
488
488
  checkReservedArtifactNames(flatSteps);
489
489
  checkNeedsSatisfied(workflow.steps, producerOf);
490
490
  checkNoCycles(workflow.steps, producerOf);
491
+ checkExprArtifactIdentifiers(workflow.steps, producerOf);
491
492
  checkExitCodeReads(workflow.steps, producerOf);
492
493
  checkAgentToolNames(flatSteps);
493
494
  await checkAgentSchemaKeys(flatSteps, cwd);
@@ -621,6 +622,42 @@ function checkNoCycles(steps, producerOf) {
621
622
  }
622
623
  for (const step of steps) visit(step, []);
623
624
  }
625
+ var JEXL_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
626
+ function checkExprArtifactIdentifiers(steps, producerOf) {
627
+ for (const step of steps) {
628
+ if (step.skipIf !== void 0) checkExprRefs(step.id, "skipIf", step.skipIf, producerOf);
629
+ if (step.kind === "loop") {
630
+ const localProducerOf = new Map([...producerOf, ...buildProducerMap(step.body)]);
631
+ checkExprRefs(step.id, "until", step.until, localProducerOf);
632
+ if (step.budget.noProgress) {
633
+ checkExprRefs(step.id, "noProgress.signal", step.budget.noProgress.signal, localProducerOf);
634
+ }
635
+ checkExprArtifactIdentifiers(step.body, localProducerOf);
636
+ }
637
+ if (step.kind === "map") checkExprArtifactIdentifiers([step.step], producerOf);
638
+ }
639
+ }
640
+ function checkExprRefs(stepId, field, expr, producerOf) {
641
+ if (typeof expr !== "string") return;
642
+ for (const name of producerOf.keys()) {
643
+ if (JEXL_IDENTIFIER.test(name)) continue;
644
+ const bounded = new RegExp(`(?<![A-Za-z0-9_$])${escapeRegExp(name)}(?![A-Za-z0-9_$])`);
645
+ if (!bounded.test(expr)) continue;
646
+ throw new WorkflowValidationError(
647
+ `step "${stepId}": ${field} references artifact "${name}", but "${name}" is not a valid jexl identifier \u2014 jexl reads it as an expression ("a-b" parses as "a minus b"), not a name, so the artifact is invisible to ${field}. Rename it to an identifier (e.g. "${toIdentifier(name)}").`
648
+ );
649
+ }
650
+ }
651
+ function escapeRegExp(s) {
652
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
653
+ }
654
+ function toIdentifier(name) {
655
+ const camel = name.replace(
656
+ /[^A-Za-z0-9]+([A-Za-z0-9])?/g,
657
+ (_, c) => c ? c.toUpperCase() : ""
658
+ );
659
+ return JEXL_IDENTIFIER.test(camel) ? camel : `a${camel}`;
660
+ }
624
661
  function checkExitCodeReads(steps, producerOf) {
625
662
  const stepsById = new Map(steps.map((s) => [s.id, s]));
626
663
  for (const step of steps) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lchase/yak",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "An agentic workflow engine",
5
5
  "license": "MIT",
6
6
  "type": "module",