@sverka/playground 0.2.26 → 0.2.28

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/pipeline.ts","../src/runner.ts","../src/share.ts","../src/embed.ts","../src/engine.ts","../src/transpile.ts","../src/gallery.ts"],"mappings":";;KAGY;;UAGK;EACf;EACA;EACA;EACA;EACA;;;UAIe;EACf;EACA;EACA;EACA,UAAU;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;EACR;;;;UAKe;EACf;EACA;EACA;EACA,UAAU;EACV;EACA;EACA;;;UAIe;EACf;EACA;EACA,UAAU;EACV;EACA;;;UAIe;EACf,OAAO;EACP,UAAU;EACV;EACA;;;;;uBCrDoB;WACX;WACA,OAAO;EAEJ,YAAA,OAAO,kBAAkB;;MAMjC;;;qBASO,gBAAgB;WAClB,WAAW;EAER,YAAA;;EAKZ,YAAY,UAAU;;;qBAMX,iBAAiB;WACnB,OAAO;WACP,SAAS;EAEN,YAAA,SAAS,SAAS;;EAM9B,QAAQ,MAAM;;EAKd,SAAS,OAAO;;;uBAMI,aAAa;WACxB;EAEG,YAAA,UAAU,UAAU,YAAY;;WAOnC,WAAW,QAAQ,uBAAuB;;;qBAIxC,qBAAqB;WACvB,UAAU,QAAQ,uBAAuB;EAGhD,YAAA,UAAU,UACV,YACA;IACE,UAAU,QAAQ,uBAAuB;IACzC;;EAOJ,WAAW,QAAQ,uBAAuB;;;;;;;;qBAW/B,kBAAkB;WACpB;EAGP,YAAA,UAAU,UACV,YACA;IACE;IACA;IACA;IACA;IACA;;EAOJ,WAAW;;;qBAgBA;WACF,UAAU;WACV;WACA;IAAW;;WACX;EAGP,YAAA,UAAU,UACV,YACA;IAAW;MAAW;;IAAgB;;;;;;;;;;;;;wBC7DpB,YAAY,SAAS,UAAU,QAAQ;;;UC3E5C;WACN;WACA;WACA,oBAAoB;;KAGnB;qBAGC,wBAAwB;WAC1B,MAAM;EACH,YAAA,MAAM,qBAAqB;;;qBAQ5B;;;qBAIA;;;wBAkFG,gBAAgB,KAAK,cAAc;;;;wBAkCnC,gBAAgB,eAAe;;;UCnI9B;;WAEN;;WAEA;;;;WAIA;;;WAGA,SAAS,QAAQ;;UAGX;;EAEf;;;;;;;;wBAkKc,YACd,IAAI,aACJ,OAAM,qBACL;;;;qBCnJU;;;;;;;;;;;;wBAmWG,iBAAiB,eAAe;;;;;;wBA6C1B,uBACpB,SAAS,SACT,oBACC,QAAQ,QAAQ,kBAAkB;;;;;;;;;;;;;;;;wBCfrB,eAAe;;;UC/ad;;WAEN;;WAEA;;WAEA;;;;wBAYK,aACd,SAAS,kCACC"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/pipeline.ts","../src/runner.ts","../src/share.ts","../src/embed.ts","../src/engine.ts","../src/transpile.ts","../src/gallery.ts"],"mappings":";;KAGY;;UAGK;EACf;EACA;EACA;EACA;EACA;;;UAIe;EACf;EACA;EACA;EACA,UAAU;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;EACR;;;;UAKe;EACf;EACA;EACA;EACA,UAAU;EACV;EACA;EACA;;;UAIe;EACf;EACA;EACA,UAAU;EACV;EACA;;;UAIe;EACf,OAAO;EACP,UAAU;EACV;EACA;;;;;uBCrDoB;WACX;WACA,OAAO;EAEJ,YAAA,OAAO,kBAAkB;;MAMjC;;;qBASO,gBAAgB;WAClB,WAAW;EAER,YAAA;;EAKZ,YAAY,UAAU;;;qBAMX,iBAAiB;WACnB,OAAO;WACP,SAAS;EAEN,YAAA,SAAS,SAAS;;EAM9B,QAAQ,MAAM;;EAKd,SAAS,OAAO;;;uBAMI,aAAa;WACxB;EAEG,YAAA,UAAU,UAAU,YAAY;;WAOnC,WAAW,QAAQ,uBAAuB;;;qBAIxC,qBAAqB;WACvB,UAAU,QAAQ,uBAAuB;EAGhD,YAAA,UAAU,UACV,YACA;IACE,UAAU,QAAQ,uBAAuB;IACzC;;EAOJ,WAAW,QAAQ,uBAAuB;;;;;;;;qBAW/B,kBAAkB;WACpB;EAGP,YAAA,UAAU,UACV,YACA;IACE;IACA;IACA;IACA;IACA;;EAOJ,WAAW;;;qBAgBA;WACF,UAAU;WACV;WACA;IAAW;;WACX;EAGP,YAAA,UAAU,UACV,YACA;IAAW;MAAW;;IAAgB;;;;;;;;;;;;;wBC7DpB,YAAY,SAAS,UAAU,QAAQ;;;UC3E5C;WACN;WACA;WACA,oBAAoB;;KAGnB;qBAGC,wBAAwB;WAC1B,MAAM;EACH,YAAA,MAAM,qBAAqB;;;qBAQ5B;;;qBAIA;;;wBAkFG,gBAAgB,KAAK,cAAc;;;;wBAkCnC,gBAAgB,eAAe;;;UCnI9B;;WAEN;;WAEA;;;;WAIA;;;WAGA,SAAS,QAAQ;;UAGX;;EAEf;;;;;;;;wBAkKc,YACd,IAAI,aACJ,OAAM,qBACL;;;;qBC9IU;;;;;;;;;;;;wBAsbG,iBAAiB,eAAe;;;;;;wBA6C1B,uBACpB,SAAS,SACT,oBACC,QAAQ,QAAQ,kBAAkB;;;;;;;;;;;;;;;;wBCtFrB,eAAe;;;UChcd;;WAEN;;WAEA;;WAEA;;;;wBAYK,aACd,SAAS,kCACC"}
package/dist/index.mjs CHANGED
@@ -323,6 +323,11 @@ function isWsChar(c) {
323
323
  if (n >= 9 && n <= 13 || n === 32) return true;
324
324
  return n >= 8192 && n <= 8202 || WS_POINTS.has(n);
325
325
  }
326
+ /** ECMAScript LineTerminators: LF, CR, U+2028, U+2029. Line comments and
327
+ * unterminated regex literals end at any of them — not only at LF. */
328
+ function isLineTerminator(c) {
329
+ return c === "\n" || c === "\r" || c === "\u2028" || c === "\u2029";
330
+ }
326
331
  /** ASCII letter-or-digit — digits and both letter cases. */
327
332
  function isAsciiLetterOrDigit(n) {
328
333
  return n >= 48 && n <= 57 || n >= 65 && n <= 90 || n >= 97 && n <= 122;
@@ -340,14 +345,22 @@ function isIdentChar(c) {
340
345
  function isQuote(c) {
341
346
  return c === "\"" || c === "'" || c === "`";
342
347
  }
348
+ /** ASCII digit. */
349
+ function isDigit(c) {
350
+ return c !== void 0 && c >= "0" && c <= "9";
351
+ }
343
352
  function skipIdent(code, i) {
344
353
  while (i < code.length && isIdentChar(code[i])) i++;
345
354
  return i;
346
355
  }
347
- /** Index of the `\n` ending the `//` comment at `at` (code.length if none). */
356
+ /** Index of the first LineTerminator ending the `//` comment at `at`
357
+ * (code.length if none). CR, U+2028 and U+2029 terminate a line comment
358
+ * just like LF — a `//` note ending at `\r` must release the code that
359
+ * follows it. */
348
360
  function lineCommentEnd(code, at) {
349
- const end = code.indexOf("\n", at + 2);
350
- return end === -1 ? code.length : end;
361
+ let i = at + 2;
362
+ while (i < code.length && !isLineTerminator(code[i])) i++;
363
+ return i;
351
364
  }
352
365
  /** Index just past the closing star-slash ending the block comment at
353
366
  * `at` — the literal marker is never written inside this doc comment
@@ -392,9 +405,41 @@ function scanString(code, at, depth = 0) {
392
405
  }
393
406
  return i;
394
407
  }
408
+ /** True when `at` begins a numeric literal — a digit, or `.` followed by
409
+ * a digit (`.5`). Member access never puts a digit right after the dot,
410
+ * so this is unambiguous at operand-scan positions. */
411
+ function numberAt(code, at) {
412
+ return isDigit(code[at]) || code[at] === "." && isDigit(code[at + 1]);
413
+ }
414
+ /** End of the digit run at `i` — digits and `_` separators. */
415
+ function skipDigits(code, i) {
416
+ while (isDigit(code[i]) || code[i] === "_") i++;
417
+ return i;
418
+ }
419
+ /** End of a `e[+-]?digits` exponent at `i`, or `i` when absent. */
420
+ function scanExponent(code, i) {
421
+ if (code[i] !== "e" && code[i] !== "E") return i;
422
+ let j = i + 1;
423
+ if (code[j] === "+" || code[j] === "-") j++;
424
+ return isDigit(code[j]) ? skipDigits(code, j) : i;
425
+ }
426
+ /** End index of the numeric literal at `at` — fraction, exponent, `_`
427
+ * separators, the bigint `n`, and radix digits (`0x`/`0o`/`0b`, which
428
+ * live in ident space) are all consumed, so `5. / 2` still ends the
429
+ * operand rather than leaving `/` to scan as a regex opener. */
430
+ function scanNumber(code, at) {
431
+ let i = at;
432
+ if (code[i] === "0" && "xXoObB".includes(code[i + 1] ?? "")) return skipIdent(code, i + 2);
433
+ i = skipDigits(code, i);
434
+ if (code[i] === ".") i = skipDigits(code, i + 1);
435
+ i = scanExponent(code, i);
436
+ if (code[i] === "n") i++;
437
+ return i;
438
+ }
395
439
  /** End index of a regex literal starting at `at` (`/`): closes at the
396
- * first unescaped `/` outside a `[]` class, then consumes flags. Stops at
397
- * a newline or EOF when unterminated — the evaluator reports the syntax. */
440
+ * first unescaped `/` outside a `[]` class, then consumes flags. Stops
441
+ * at any LineTerminator or EOF when unterminated — the evaluator
442
+ * reports the syntax. */
398
443
  function scanRegex(code, at) {
399
444
  let i = at + 1;
400
445
  let inClass = false;
@@ -407,7 +452,7 @@ function scanRegex(code, at) {
407
452
  if (c === "[") inClass = true;
408
453
  else if (c === "]") inClass = false;
409
454
  else if (c === "/" && !inClass) return skipIdent(code, i + 1);
410
- else if (c === "\n") return i;
455
+ else if (isLineTerminator(c)) return i;
411
456
  i++;
412
457
  }
413
458
  return i;
@@ -433,12 +478,14 @@ function scanOpaqueEnd(code, i, depth = 0) {
433
478
  isString: false
434
479
  };
435
480
  }
436
- /** Reserved words after which `/` opens a regex rather than divides —
437
- * they can never end an operand. Contextual keywords (`of`, `as`,
438
- * `from`, `get`, `set`, `type`, `async`, `declare`) are NOT listed:
439
- * they act as ordinary identifiers in most positions, so a `/` after
440
- * them divides. Everything else — plain identifiers plus literals such
441
- * as `this`/`true` — ends an operand, making `/` a division. */
481
+ /** Reserved words and true operators after which `/` opens a regex
482
+ * rather than divides. Everything else — plain identifiers, literals
483
+ * such as `this`/`true`, and the contextual keywords `async`/`get`/`set`/
484
+ * `of`/`from`/`as`/`type`/`declare`/`static` — ends an operand, making
485
+ * `/` a division: `async / 2` divides because `async` is a valid
486
+ * identifier. The only contextual exception lives one level up: `of`
487
+ * inside a `for (` header is the for-of keyword, handled by
488
+ * wordOperandEnd via the paren stack. */
442
489
  const NON_OPERAND_WORDS = /* @__PURE__ */ new Set([
443
490
  "await",
444
491
  "break",
@@ -471,7 +518,6 @@ const NON_OPERAND_WORDS = /* @__PURE__ */ new Set([
471
518
  "protected",
472
519
  "public",
473
520
  "return",
474
- "static",
475
521
  "switch",
476
522
  "throw",
477
523
  "try",
@@ -486,80 +532,161 @@ const NON_OPERAND_WORDS = /* @__PURE__ */ new Set([
486
532
  function operandAfterWord(word) {
487
533
  return !NON_OPERAND_WORDS.has(word);
488
534
  }
489
- /** Consume one token at `i` for the brace-matching scans: opaque regions
490
- * and regex literals advance wholesale, identifiers update operand
491
- * state from the keyword table, braces report a depth delta. */
492
- function scanToken(code, i, operandEnd, depth = 0) {
535
+ /** Words whose `(` opens a control-flow header, not a grouping or call:
536
+ * after the matching `)` a statement follows, so a `/` may open a regex
537
+ * — `if (x) /re/`. `function` is deliberately absent: its `)` precedes
538
+ * the body `{`, and `function() {} / 2` divides. */
539
+ const CONTROL_WORDS = /* @__PURE__ */ new Set([
540
+ "catch",
541
+ "for",
542
+ "if",
543
+ "switch",
544
+ "while",
545
+ "with"
546
+ ]);
547
+ /** Fresh operand-scan state starting in operand-expected position. */
548
+ function newOperandScan() {
549
+ return {
550
+ operandEnd: false,
551
+ parens: [],
552
+ pendingCtl: null
553
+ };
554
+ }
555
+ /** Operand context after `word`: `of` inside a `for (` header introduces
556
+ * the iterated expression (`for (x of /re/)`), everywhere else it's an
557
+ * identifier; all other words resolve through the keyword table. */
558
+ function wordOperandEnd(word, parens) {
559
+ if (word === "of" && parens.at(-1) === "for") return false;
560
+ return operandAfterWord(word);
561
+ }
562
+ /** True when `i` starts a `++` or `--` pair — postfix ends the operand,
563
+ * prefix still expects one; either way the state survives. */
564
+ function doubleSignAt(code, i) {
565
+ const c = code[i];
566
+ return (c === "+" || c === "-") && code[i + 1] === c;
567
+ }
568
+ /** `/` after an operand divides (one char); at operand-expected
569
+ * position it opens a regex literal, consumed whole. */
570
+ function slashTokenEnd(code, i, st) {
571
+ st.pendingCtl = null;
572
+ if (st.operandEnd) {
573
+ st.operandEnd = false;
574
+ return i + 1;
575
+ }
576
+ st.operandEnd = true;
577
+ return scanRegex(code, i);
578
+ }
579
+ /** End of a `.prop` member access at the `.` — skips trivia, then the
580
+ * property ident when one follows. `?.` chains and each `.` of `...`
581
+ * land here too, where the leading pair simply finds no ident. */
582
+ function memberPropEnd(code, i) {
583
+ const prop = skipTrivia(code, i + 1);
584
+ return isIdentChar(code[prop]) && !isDigit(code[prop]) ? skipIdent(code, prop) : i + 1;
585
+ }
586
+ /** End of the identifier/keyword run at `i`, updating operand state:
587
+ * keyword-ness decides whether a following `/` divides, and control
588
+ * words arm `pendingCtl` for their header `(`. `await` is the one word
589
+ * allowed between `for` and its `(` — `for await (x of y)` keeps the
590
+ * control context. */
591
+ function wordTokenEnd(code, i, st) {
592
+ const wend = skipIdent(code, i);
593
+ const word = code.slice(i, wend);
594
+ st.operandEnd = wordOperandEnd(word, st.parens);
595
+ st.pendingCtl = word === "await" && st.pendingCtl === "for" ? "for" : CONTROL_WORDS.has(word) ? word : null;
596
+ return wend;
597
+ }
598
+ /** Shared operand-token scan: opaque spans (strings end operands,
599
+ * comments pass context through), `++`/`--` pairs, `/` (regex unless an
600
+ * operand just ended), numbers, identifier runs, and whitespace
601
+ * (consume-and-preserve). Returns the token end, or null for
602
+ * bracket/separator punctuation the caller's own loop owns. */
603
+ function operandToken(code, i, st, depth) {
493
604
  const opaque = scanOpaqueEnd(code, i, depth);
494
- if (opaque.end > i + 1) return {
495
- end: opaque.end,
496
- operandEnd: opaque.isString || operandEnd,
605
+ if (opaque.end > i + 1) {
606
+ if (opaque.isString) {
607
+ st.operandEnd = true;
608
+ st.pendingCtl = null;
609
+ }
610
+ return opaque.end;
611
+ }
612
+ const c = code[i];
613
+ if (doubleSignAt(code, i)) {
614
+ st.pendingCtl = null;
615
+ return i + 2;
616
+ }
617
+ if (c === "/") return slashTokenEnd(code, i, st);
618
+ if (isWsChar(c)) return i + 1;
619
+ if (numberAt(code, i)) {
620
+ st.operandEnd = true;
621
+ st.pendingCtl = null;
622
+ return scanNumber(code, i);
623
+ }
624
+ if (c === ".") {
625
+ st.pendingCtl = null;
626
+ st.operandEnd = true;
627
+ return memberPropEnd(code, i);
628
+ }
629
+ if (isIdentChar(c)) return wordTokenEnd(code, i, st);
630
+ return null;
631
+ }
632
+ /** Consume one token at `i` for the brace-matching scans, mutating `st`:
633
+ * operand tokens defer to operandToken, `(` pushes its control context,
634
+ * `)` reports operand-expected when it closed a control header, and
635
+ * braces report a depth delta. */
636
+ function scanToken(code, i, st, depth = 0) {
637
+ const end = operandToken(code, i, st, depth);
638
+ if (end !== null) return {
639
+ end,
497
640
  brace: 0
498
641
  };
499
642
  const ch = code[i];
500
- if (ch === "/" && !operandEnd) return {
501
- end: scanRegex(code, i),
502
- operandEnd: true,
503
- brace: 0
504
- };
505
- if (isWsChar(ch)) return {
506
- end: i + 1,
507
- operandEnd,
508
- brace: 0
509
- };
510
- if (isIdentChar(ch)) {
511
- const wend = skipIdent(code, i);
643
+ const ctl = st.pendingCtl;
644
+ st.pendingCtl = null;
645
+ if (ch === "(") {
646
+ st.parens.push(ctl);
647
+ st.operandEnd = false;
512
648
  return {
513
- end: wend,
514
- operandEnd: operandAfterWord(code.slice(i, wend)),
649
+ end: i + 1,
515
650
  brace: 0
516
651
  };
517
652
  }
518
- if (ch === "{") return {
519
- end: i + 1,
520
- operandEnd: false,
521
- brace: 1
522
- };
523
- if (ch === "}") return {
524
- end: i + 1,
525
- operandEnd: true,
526
- brace: -1
527
- };
653
+ if (ch === ")") {
654
+ const kind = st.parens.pop();
655
+ st.operandEnd = kind === void 0 || kind === null;
656
+ return {
657
+ end: i + 1,
658
+ brace: 0
659
+ };
660
+ }
661
+ if (ch === "{") {
662
+ st.operandEnd = false;
663
+ return {
664
+ end: i + 1,
665
+ brace: 1
666
+ };
667
+ }
668
+ if (ch === "}") {
669
+ st.operandEnd = true;
670
+ return {
671
+ end: i + 1,
672
+ brace: -1
673
+ };
674
+ }
675
+ st.operandEnd = ch === "]";
528
676
  return {
529
677
  end: i + 1,
530
- operandEnd: false,
531
678
  brace: 0
532
679
  };
533
680
  }
534
681
  /**
535
- * Scan one operand-context token — an opaque span, a regex-or-division,
536
- * or an identifier run — returning where it ends and whether it leaves
537
- * an operand behind. Comments are transparent (operand context passes
538
- * through unchanged). Returns null for punctuation/whitespace the
539
- * caller's own loop owns.
682
+ * Scan one operand-context token — an opaque span, a `++`/`--` pair, a
683
+ * regex-or-division, a number, or an identifier run — mutating `st` and
684
+ * returning where the token ends. Comments are transparent (operand
685
+ * context and a pending control word pass through unchanged). Returns
686
+ * null for punctuation the caller's own loop owns.
540
687
  */
541
- function scanOperand(code, i, operandEnd) {
542
- const opaque = scanOpaqueEnd(code, i);
543
- if (opaque.end > i + 1) return {
544
- end: opaque.end,
545
- operandEnd: operandEnd || opaque.isString
546
- };
547
- const c = code[i];
548
- if (c === "/") return operandEnd ? {
549
- end: i + 1,
550
- operandEnd: false
551
- } : {
552
- end: scanRegex(code, i),
553
- operandEnd: true
554
- };
555
- if (isIdentChar(c)) {
556
- const wend = skipIdent(code, i);
557
- return {
558
- end: wend,
559
- operandEnd: operandAfterWord(code.slice(i, wend))
560
- };
561
- }
562
- return null;
688
+ function scanOperand(code, i, st) {
689
+ return operandToken(code, i, st, 0);
563
690
  }
564
691
  /**
565
692
  * End index of the `${` expression whose `{` is at `at`. Strings,
@@ -570,11 +697,10 @@ function scanOperand(code, i, operandEnd) {
570
697
  function scanTemplateExpr(code, at, depth = 0) {
571
698
  let braces = 1;
572
699
  let i = at + 1;
573
- let operandEnd = false;
700
+ const st = newOperandScan();
574
701
  while (i < code.length && braces > 0) {
575
- const step = scanToken(code, i, operandEnd, depth);
702
+ const step = scanToken(code, i, st, depth);
576
703
  braces += step.brace;
577
- operandEnd = step.operandEnd;
578
704
  i = step.end;
579
705
  }
580
706
  return i;
@@ -587,11 +713,10 @@ function scanTemplateExpr(code, at, depth = 0) {
587
713
  function matchBrace(code, open) {
588
714
  let depth = 0;
589
715
  let i = open;
590
- let operandEnd = false;
716
+ const st = newOperandScan();
591
717
  while (i < code.length) {
592
- const step = scanToken(code, i, operandEnd);
718
+ const step = scanToken(code, i, st);
593
719
  depth += step.brace;
594
- operandEnd = step.operandEnd;
595
720
  i = step.end;
596
721
  if (step.brace === -1 && depth === 0) return i - 1;
597
722
  }
@@ -690,7 +815,7 @@ function bracketDelta(c) {
690
815
  * newline after the module-specifier string (left in place). */
691
816
  function stmtTerminator(c, depth, specSeen) {
692
817
  if (c === ";" && depth <= 0) return "past";
693
- if (c === "\n" && depth <= 0 && specSeen) return "at";
818
+ if (isLineTerminator(c) && depth <= 0 && specSeen) return "at";
694
819
  return null;
695
820
  }
696
821
  /** End index of the statement starting at `at`: first top-level `;`, or
@@ -767,6 +892,7 @@ function copyOpaque(code, st, out) {
767
892
  if (opaque.isString) {
768
893
  st.stmtStart = false;
769
894
  st.operandEnd = true;
895
+ st.pendingCtl = null;
770
896
  }
771
897
  st.i = opaque.end;
772
898
  return true;
@@ -783,29 +909,40 @@ function applyBracket(code, st, out) {
783
909
  const popped = st.stack.pop();
784
910
  st.stmtStart = popped === "block";
785
911
  st.operandEnd = popped === "expr";
786
- } else if (c === "(" || c === "[") {
912
+ } else if (c === "(") {
913
+ st.stack.push(st.pendingCtl !== null ? { ctl: st.pendingCtl } : "expr");
914
+ st.stmtStart = false;
915
+ st.operandEnd = false;
916
+ } else if (c === "[") {
787
917
  st.stack.push("expr");
788
918
  st.stmtStart = false;
789
919
  st.operandEnd = false;
790
- } else if (c === ")" || c === "]") {
920
+ } else if (c === ")") {
921
+ st.stmtStart = typeof st.stack.pop() === "object";
922
+ st.operandEnd = !st.stmtStart;
923
+ } else if (c === "]") {
791
924
  st.stack.pop();
792
925
  st.stmtStart = false;
793
926
  st.operandEnd = true;
794
927
  } else return false;
928
+ st.pendingCtl = null;
795
929
  out.push(c ?? "");
796
930
  st.i++;
797
931
  return true;
798
932
  }
799
- /** Handle `\n`, `;`, and whitespace — statement boundaries at statement
800
- * level only (a `;` inside `for (;;)` doesn't count). A newline never
801
- * resets operand context: `foo\n/bar/` divides, per ASI rules. */
933
+ /** Handle `;`, LineTerminators, and whitespace — statement boundaries at
934
+ * statement level only (a `;` inside `for (;;)` doesn't count). A line
935
+ * terminator never resets operand context: `foo\n/bar/` divides, per
936
+ * ASI rules. It also keeps a pending control word: `if\n(x)` still
937
+ * parses as a control paren. */
802
938
  function applyTerminator(code, st, out) {
803
939
  const c = code[st.i];
804
- if (c !== ";" && c !== "\n" && !isWsChar(c)) return false;
940
+ if (c !== ";" && !isWsChar(c)) return false;
805
941
  if (c === ";") {
806
942
  if (atStmtLevel(st)) st.stmtStart = true;
807
943
  st.operandEnd = false;
808
- } else if (c === "\n" && atStmtLevel(st)) st.stmtStart = true;
944
+ st.pendingCtl = null;
945
+ } else if (isLineTerminator(c) && atStmtLevel(st)) st.stmtStart = true;
809
946
  out.push(c ?? "");
810
947
  st.i++;
811
948
  return true;
@@ -815,6 +952,7 @@ function applyTerminator(code, st, out) {
815
952
  * already consumed them). */
816
953
  function applySlash(code, st, out) {
817
954
  if (code[st.i] !== "/" || commentAt(code, st.i)) return false;
955
+ st.pendingCtl = null;
818
956
  if (st.operandEnd) {
819
957
  out.push("/");
820
958
  st.i++;
@@ -833,6 +971,7 @@ function applySlash(code, st, out) {
833
971
  * sit between the keyword and the paren. */
834
972
  function tryImport(code, st, out) {
835
973
  if (!st.stmtStart || !keywordAt(code, st.i, "import")) return false;
974
+ st.pendingCtl = null;
836
975
  const nc = code[skipTrivia(code, st.i + 6)];
837
976
  if (nc === "(" || nc === ".") {
838
977
  out.push("import");
@@ -851,6 +990,7 @@ function tryImport(code, st, out) {
851
990
  * body, where `export const` would be a syntax error). */
852
991
  function tryExport(code, st, out) {
853
992
  if (!st.stmtStart || !keywordAt(code, st.i, "export")) return false;
993
+ st.pendingCtl = null;
854
994
  const j = skipTrivia(code, st.i + 6);
855
995
  if (!st.exportDefaultDone && code.startsWith("default", j) && !isIdentChar(code[j + 7])) {
856
996
  out.push("return ");
@@ -871,13 +1011,46 @@ function tryExport(code, st, out) {
871
1011
  st.operandEnd = false;
872
1012
  return true;
873
1013
  }
1014
+ /** Emit a numeric literal — numbers end operands, so `5. / 2` divides
1015
+ * even though a bare `.` would leave `/` looking like a regex opener. */
1016
+ function tryNumber(code, st, out) {
1017
+ if (!numberAt(code, st.i)) return false;
1018
+ const end = scanNumber(code, st.i);
1019
+ out.push(code.slice(st.i, end));
1020
+ st.i = end;
1021
+ st.operandEnd = true;
1022
+ st.pendingCtl = null;
1023
+ st.stmtStart = false;
1024
+ return true;
1025
+ }
1026
+ /** `.` opens member access — the word after it is a property name, not
1027
+ * a keyword, so `obj.return / 2` divides. `.5` is a number taken by
1028
+ * tryNumber; `?.` reaches here through its `.`; each `.` of `...`
1029
+ * lands here and the leading pair finds no ident to consume. */
1030
+ function tryDot(code, st, out) {
1031
+ if (code[st.i] !== ".") return false;
1032
+ let end = st.i + 1;
1033
+ const prop = skipTrivia(code, end);
1034
+ if (isIdentChar(code[prop]) && !isDigit(code[prop])) end = skipIdent(code, prop);
1035
+ out.push(code.slice(st.i, end));
1036
+ st.i = end;
1037
+ st.operandEnd = true;
1038
+ st.pendingCtl = null;
1039
+ st.stmtStart = false;
1040
+ return true;
1041
+ }
874
1042
  /** Emit an identifier run — keyword table decides whether `/` after it
875
- * divides (`foo /x/`) or opens a regex (`return /x/`). */
1043
+ * divides (`foo /x/`) or opens a regex (`return /x/`). `of` is the one
1044
+ * contextual exception: inside a `for (` header it introduces the
1045
+ * iterated expression — `for (x of /re/)`. */
876
1046
  function tryWord(code, st, out) {
877
1047
  if (!isIdentChar(code[st.i])) return false;
878
1048
  const wend = skipIdent(code, st.i);
879
- out.push(code.slice(st.i, wend));
880
- st.operandEnd = operandAfterWord(code.slice(st.i, wend));
1049
+ const word = code.slice(st.i, wend);
1050
+ out.push(word);
1051
+ const top = st.stack.at(-1);
1052
+ st.operandEnd = word === "of" && typeof top === "object" && top.ctl === "for" ? false : operandAfterWord(word);
1053
+ st.pendingCtl = word === "await" && st.pendingCtl === "for" ? "for" : CONTROL_WORDS.has(word) ? word : null;
881
1054
  st.stmtStart = false;
882
1055
  st.i = wend;
883
1056
  return true;
@@ -891,14 +1064,23 @@ const HANDLERS = [
891
1064
  applySlash,
892
1065
  tryImport,
893
1066
  tryExport,
1067
+ tryNumber,
1068
+ tryDot,
894
1069
  tryWord
895
1070
  ];
896
1071
  function advanceScan(code, st, out) {
897
1072
  for (const handle of HANDLERS) if (handle(code, st, out)) return;
1073
+ const c = code[st.i];
1074
+ if ((c === "+" || c === "-") && code[st.i + 1] === c) {
1075
+ out.push((c ?? "") + (c ?? ""));
1076
+ st.i += 2;
1077
+ } else {
1078
+ st.operandEnd = false;
1079
+ out.push(c ?? "");
1080
+ st.i++;
1081
+ }
898
1082
  st.stmtStart = false;
899
- st.operandEnd = false;
900
- out.push(code[st.i] ?? "");
901
- st.i++;
1083
+ st.pendingCtl = null;
902
1084
  }
903
1085
  /**
904
1086
  * Strip import/export statements from user code for eval — a linear
@@ -922,7 +1104,8 @@ function preprocessCode(code) {
922
1104
  stmtStart: true,
923
1105
  exportDefaultDone: false,
924
1106
  stack: [],
925
- operandEnd: false
1107
+ operandEnd: false,
1108
+ pendingCtl: null
926
1109
  };
927
1110
  while (st.i < code.length) {
928
1111
  if (code[st.i] === void 0) break;
@@ -1146,31 +1329,38 @@ function skipWs(code, i) {
1146
1329
  return i;
1147
1330
  }
1148
1331
  /** One token of an operand-tracking scan — identifiers (with `word`),
1149
- * opaque spans, and regex-vs-division resolve through the shared
1150
- * scanner; whitespace passes through. Null when `i` sits on
1151
- * punctuation the caller's own depth/branch logic owns. */
1152
- function propToken(s, i, operandEnd) {
1153
- if (isWsChar(s[i])) return {
1154
- end: i + 1,
1155
- operandEnd
1156
- };
1157
- const op = scanOperand(s, i, operandEnd);
1158
- if (op === null) return null;
1332
+ * opaque spans, `++`/`--`, numbers, and regex-vs-division resolve
1333
+ * through the shared scanner; whitespace passes through. Null when `i`
1334
+ * sits on punctuation the caller's own depth/branch logic owns. */
1335
+ function propToken(s, i, st) {
1336
+ const end = scanOperand(s, i, st);
1337
+ if (end === null) return null;
1159
1338
  if (isIdentChar(s[i])) return {
1160
- ...op,
1161
- word: s.slice(i, op.end)
1339
+ end,
1340
+ word: s.slice(i, end)
1162
1341
  };
1163
- return op;
1342
+ return { end };
1164
1343
  }
1165
- /** Apply a bracket/other punctuation char to depth + operand state. */
1344
+ /** Apply a bracket/other punctuation char to depth + operand state.
1345
+ * `(` records whether a control word opened it — `if (x) /re/` may
1346
+ * open a regex after `)` while `(x) / 2` divides. */
1166
1347
  function applyPunctuation(c, st) {
1167
1348
  if (isOpenBracket(c)) {
1168
1349
  st.depth++;
1350
+ if (c === "(") st.parens.push(st.pendingCtl);
1351
+ st.pendingCtl = null;
1169
1352
  st.operandEnd = false;
1170
1353
  } else if (isCloseBracket(c)) {
1171
1354
  st.depth--;
1172
- st.operandEnd = true;
1173
- } else st.operandEnd = false;
1355
+ st.pendingCtl = null;
1356
+ if (c === ")") {
1357
+ const kind = st.parens.pop();
1358
+ st.operandEnd = kind === void 0 || kind === null;
1359
+ } else st.operandEnd = true;
1360
+ } else {
1361
+ st.pendingCtl = null;
1362
+ st.operandEnd = false;
1363
+ }
1174
1364
  }
1175
1365
  /**
1176
1366
  * Index of the `:` following a depth-0 `key` in a props object body, or -1.
@@ -1178,12 +1368,12 @@ function applyPunctuation(c, st) {
1178
1368
  */
1179
1369
  function findPropKey(props, key) {
1180
1370
  const st = {
1181
- depth: 0,
1182
- operandEnd: false
1371
+ ...newOperandScan(),
1372
+ depth: 0
1183
1373
  };
1184
1374
  let i = 0;
1185
1375
  while (i < props.length) {
1186
- const tok = propToken(props, i, st.operandEnd);
1376
+ const tok = propToken(props, i, st);
1187
1377
  if (tok === null) {
1188
1378
  applyPunctuation(props[i], st);
1189
1379
  i++;
@@ -1193,7 +1383,6 @@ function findPropKey(props, key) {
1193
1383
  const hit = skipWs(props, tok.end);
1194
1384
  if (props[hit] === ":") return hit;
1195
1385
  }
1196
- st.operandEnd = tok.operandEnd;
1197
1386
  i = tok.end;
1198
1387
  }
1199
1388
  return -1;
@@ -1209,20 +1398,23 @@ function isTailComment(props, k, after) {
1209
1398
  * or the end of the props body, with strings/comments/brackets balanced. */
1210
1399
  function propValueEnd(props, j) {
1211
1400
  const st = {
1212
- depth: 0,
1213
- operandEnd: false
1401
+ ...newOperandScan(),
1402
+ depth: 0
1214
1403
  };
1215
1404
  let k = j;
1216
1405
  while (k < props.length) {
1217
1406
  if (isOpaqueStart(props, k)) {
1218
1407
  const after = scanOpaqueEnd(props, k).end;
1219
1408
  if (st.depth === 0 && isTailComment(props, k, after)) return k;
1409
+ if (isQuote(props[k])) {
1410
+ st.operandEnd = true;
1411
+ st.pendingCtl = null;
1412
+ }
1220
1413
  k = after;
1221
1414
  continue;
1222
1415
  }
1223
- const tok = propToken(props, k, st.operandEnd);
1416
+ const tok = propToken(props, k, st);
1224
1417
  if (tok !== null) {
1225
- st.operandEnd = tok.operandEnd;
1226
1418
  k = tok.end;
1227
1419
  continue;
1228
1420
  }
@@ -1244,12 +1436,16 @@ function extractProp(props, key) {
1244
1436
  const j = skipWs(props, colon + 1);
1245
1437
  return props.slice(j, propValueEnd(props, j)).trim();
1246
1438
  }
1247
- /** Consume trailing spaces/tabs plus one optional `;` — never the newline.
1248
- * Eating it would fuse the emitted import with the next line
1249
- * (`from "@sverka/workflow"const x = …`). */
1439
+ /** JS line terminators — whitespace a statement tail must not consume. */
1440
+ function isLineBreak(c) {
1441
+ return c === "\n" || c === "\r" || c === "\u2028" || c === "\u2029";
1442
+ }
1443
+ /** Consume trailing non-line whitespace plus one optional `;` — never a
1444
+ * line break. Eating it would fuse the emitted import with the next
1445
+ * line (`from "@sverka/workflow"const x = …`). */
1250
1446
  function stmtEndPos(source, i) {
1251
1447
  let end = i;
1252
- while (source[end] === " " || source[end] === " ") end++;
1448
+ while (isWsChar(source[end]) && !isLineBreak(source[end])) end++;
1253
1449
  if (source[end] === ";") end++;
1254
1450
  return end;
1255
1451
  }
@@ -1361,11 +1557,6 @@ function stepCallParen(code, afterNew, names) {
1361
1557
  }
1362
1558
  return null;
1363
1559
  }
1364
- /** Operand context after punctuation — closers end a value, everything
1365
- * else (openers, operators, separators) expects one. */
1366
- function punctuationOperandEnd(c) {
1367
- return c === ")" || c === "]" || c === "}";
1368
- }
1369
1560
  /**
1370
1561
  * Locate the next `new <name>(` call where <name> ∈ names. Whole-word
1371
1562
  * scanning makes boundary checks free — `myNew` or `newer` never match.
@@ -1374,10 +1565,13 @@ function punctuationOperandEnd(c) {
1374
1565
  * is documentation, not a call.
1375
1566
  */
1376
1567
  function findStepCall(code, names, from) {
1377
- let operandEnd = false;
1568
+ const st = {
1569
+ ...newOperandScan(),
1570
+ depth: 0
1571
+ };
1378
1572
  let i = from;
1379
1573
  while (i < code.length) {
1380
- const tok = propToken(code, i, operandEnd);
1574
+ const tok = propToken(code, i, st);
1381
1575
  if (tok !== null) {
1382
1576
  if (tok.word === "new") {
1383
1577
  const paren = stepCallParen(code, tok.end, names);
@@ -1386,11 +1580,10 @@ function findStepCall(code, names, from) {
1386
1580
  paren
1387
1581
  };
1388
1582
  }
1389
- operandEnd = tok.operandEnd;
1390
1583
  i = tok.end;
1391
1584
  continue;
1392
1585
  }
1393
- operandEnd = punctuationOperandEnd(code[i]);
1586
+ applyPunctuation(code[i], st);
1394
1587
  i++;
1395
1588
  }
1396
1589
  return null;
@@ -1399,14 +1592,13 @@ function findStepCall(code, names, from) {
1399
1592
  * or -1 when the argument list closes/ends first. */
1400
1593
  function scanScopeEnd(code, from) {
1401
1594
  const st = {
1402
- depth: 0,
1403
- operandEnd: false
1595
+ ...newOperandScan(),
1596
+ depth: 0
1404
1597
  };
1405
1598
  let k = from;
1406
1599
  while (k < code.length) {
1407
- const tok = propToken(code, k, st.operandEnd);
1600
+ const tok = propToken(code, k, st);
1408
1601
  if (tok !== null) {
1409
- st.operandEnd = tok.operandEnd;
1410
1602
  k = tok.end;
1411
1603
  continue;
1412
1604
  }
@@ -1452,11 +1644,7 @@ function rewriteStepCalls(code, names) {
1452
1644
  const call = findStepCall(code, names, cursor);
1453
1645
  if (call === null) break;
1454
1646
  const args = parseStepArgs(code, call.paren);
1455
- if (args === null) {
1456
- result += code.slice(cursor, call.start + 3);
1457
- cursor = call.start + 3;
1458
- continue;
1459
- }
1647
+ if (args === null) throw unsupportedStepError(code, call.paren);
1460
1648
  const propsClose = matchBrace(code, args.propsOpen);
1461
1649
  if (propsClose < 0) throw new PlaygroundError("TRANSPILE_FAILED", `cannot parse FunctionStep "${args.id}" — unbalanced props object`);
1462
1650
  const dependencies = extractProp(code.slice(args.propsOpen + 1, propsClose), "dependencies");
@@ -1465,15 +1653,19 @@ function rewriteStepCalls(code, names) {
1465
1653
  }
1466
1654
  return result + code.slice(cursor);
1467
1655
  }
1656
+ /** TRANSPILE_FAILED naming the scope of the step call whose `(` is at
1657
+ * `paren` — `?` when even the scope won't scan. */
1658
+ function unsupportedStepError(code, paren) {
1659
+ const scopeStart = skipWs(code, paren + 1);
1660
+ const scopeEnd = scanScopeEnd(code, scopeStart);
1661
+ return new PlaygroundError("TRANSPILE_FAILED", `cannot parse FunctionStep with scope '${scopeEnd < 0 ? "?" : code.slice(scopeStart, scopeEnd).trim() || "?"}' — unsupported expression`);
1662
+ }
1468
1663
  /** A surviving step call means a shape we could not convert — computed
1469
1664
  * scope/id or a malformed alias call. Fail loud rather than emit a
1470
1665
  * half-converted file. */
1471
1666
  function assertNoLeftoverSteps(code, names) {
1472
1667
  const call = findStepCall(code, names, 0);
1473
- if (call === null) return;
1474
- const scopeStart = skipWs(code, call.paren + 1);
1475
- const scopeEnd = scanScopeEnd(code, scopeStart);
1476
- throw new PlaygroundError("TRANSPILE_FAILED", `cannot parse FunctionStep with scope '${scopeEnd < 0 ? "?" : code.slice(scopeStart, scopeEnd).trim() || "?"}' — unsupported expression`);
1668
+ if (call !== null) throw unsupportedStepError(code, call.paren);
1477
1669
  }
1478
1670
  /**
1479
1671
  * Transpile playground source into `sverka.config.ts`.
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/pipeline.ts","../src/runner.ts","../src/share.ts","../src/scanner.ts","../src/engine.ts","../src/embed.ts","../src/transpile.ts","../src/gallery.ts"],"sourcesContent":["// @sverka/playground — minimal pipeline model. Browser-safe.\n// Mirrors @sverka/workflow Construct API but simplified for browser use.\n\nimport type { PlaygroundFinding } from \"./types.js\";\n\n/** Base class for all constructs. */\nexport abstract class Construct {\n readonly id: string;\n readonly scope: Construct | null;\n\n constructor(scope: Construct | null, id: string) {\n this.id = id;\n this.scope = scope;\n }\n\n /** Full path from root to this construct. */\n get path(): string {\n if (this.scope && this.scope.id !== \"root\") {\n return `${this.scope.path}/${this.id}`;\n }\n return this.id;\n }\n}\n\n/** Root construct representing a project. */\nexport class Project extends Construct {\n readonly pipelines: Pipeline[] = [];\n\n constructor(id: string) {\n super(null, id);\n }\n\n /** Add a pipeline to this project. */\n addPipeline(pipeline: Pipeline): void {\n this.pipelines.push(pipeline);\n }\n}\n\n/** A pipeline containing steps. */\nexport class Pipeline extends Construct {\n readonly steps: Step[] = [];\n readonly entries: Entry[] = [];\n\n constructor(project: Project, id: string) {\n super(project, id);\n project.addPipeline(this);\n }\n\n /** Add a step to this pipeline. */\n addStep(step: Step): void {\n this.steps.push(step);\n }\n\n /** Add an entry to this pipeline. */\n addEntry(entry: Entry): void {\n this.entries.push(entry);\n }\n}\n\n/** Base class for all step types. */\nexport abstract class Step extends Construct {\n readonly dependencies: string[];\n\n constructor(pipeline: Pipeline, id: string, dependencies: string[] = []) {\n super(pipeline, id);\n this.dependencies = dependencies;\n pipeline.addStep(this);\n }\n\n /** Execute this step and return findings. */\n abstract execute(): Promise<PlaygroundFinding[]> | PlaygroundFinding[];\n}\n\n/** A step that runs a JavaScript function in the browser. */\nexport class FunctionStep extends Step {\n readonly fn: () => Promise<PlaygroundFinding[]> | PlaygroundFinding[];\n\n constructor(\n pipeline: Pipeline,\n id: string,\n options: {\n fn: () => Promise<PlaygroundFinding[]> | PlaygroundFinding[];\n dependencies?: string[];\n },\n ) {\n super(pipeline, id, options.dependencies ?? []);\n this.fn = options.fn;\n }\n\n execute(): Promise<PlaygroundFinding[]> | PlaygroundFinding[] {\n return this.fn();\n }\n}\n\n/**\n * Browser stub of the real `@sverka/workflow` ShellStep. Real configs load\n * verbatim in the playground, but a shell command cannot execute in a\n * browser — execute() surfaces an info finding explaining the demo\n * boundary instead of pretending the command ran.\n */\nexport class ShellStep extends Step {\n readonly command: string;\n\n constructor(\n pipeline: Pipeline,\n id: string,\n options: {\n command: string;\n dependsOn?: string[];\n runtime?: unknown;\n outputs?: unknown;\n background?: boolean;\n },\n ) {\n super(pipeline, id, options.dependsOn ?? []);\n this.command = options.command;\n }\n\n execute(): PlaygroundFinding[] {\n return [\n {\n rule: \"playground/shell-step\",\n file: \"sverka.config.ts\",\n line: 1,\n severity: \"info\",\n // The command text is deliberately not echoed — a real command may\n // carry inline secrets, and findings end up in share links.\n message: `'${this.id}' is a shell step — the browser cannot run it. Run the exported config locally to execute it.`,\n },\n ];\n }\n}\n\n/** An entry point that triggers a pipeline. */\nexport class Entry {\n readonly pipeline: Pipeline;\n readonly id: string;\n readonly trigger: { kind: string };\n readonly roots: string[];\n\n constructor(\n pipeline: Pipeline,\n id: string,\n options: { trigger: { kind: string }; roots: string[] },\n ) {\n this.pipeline = pipeline;\n this.id = id;\n this.trigger = options.trigger;\n this.roots = options.roots;\n pipeline.addEntry(this);\n }\n}\n","// @sverka/playground — pipeline runner. Browser-safe.\n// Executes FunctionSteps and collects findings.\n\nimport type { Project } from \"./pipeline.js\";\nimport type { Pipeline } from \"./pipeline.js\";\nimport type { Step } from \"./pipeline.js\";\nimport type {\n Finding,\n PlaygroundFinding,\n PipelineResult,\n StepResult,\n} from \"./types.js\";\n\n/** Simple deterministic hash for fingerprint generation. Browser-safe. */\nfunction simpleHash(input: string): string {\n let hash = 5381;\n for (let i = 0; i < input.length; i++) {\n hash = ((hash << 5) + hash + input.charCodeAt(i)) | 0;\n }\n return (hash >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/** Convert a PlaygroundFinding to a full Finding with fingerprint and id. */\nfunction toFinding(pf: PlaygroundFinding, stepId: string): Finding {\n // Include checkId (stepId) in fingerprint so identical findings from\n // different checks get distinct fingerprints.\n const fingerprintInput = `${stepId}:${pf.rule}:${pf.file}:${pf.line}`;\n const fingerprint = simpleHash(fingerprintInput);\n const id = `${stepId}:${fingerprint}`;\n return {\n id,\n fingerprint,\n checkId: stepId,\n severity: pf.severity,\n confidence: 0.8,\n message: pf.message,\n rule: pf.rule,\n file: pf.file,\n startLine: pf.line,\n endLine: pf.line,\n source: {\n tool: pf.tool ?? stepId,\n version: null,\n format: \"custom\",\n originalRuleId: pf.rule,\n originalSeverity: null,\n },\n ...(pf.snippet ? { snippet: pf.snippet } : {}),\n };\n}\n\n/** Execute a single step and return its result. */\nasync function executeStep(step: Step): Promise<StepResult> {\n const start = Date.now();\n try {\n const playgroundFindings = await step.execute();\n const findings = playgroundFindings.map((pf) => toFinding(pf, step.id));\n return {\n stepId: step.id,\n status: \"success\",\n findings,\n durationMs: Date.now() - start,\n };\n } catch (e) {\n return {\n stepId: step.id,\n status: \"failure\",\n findings: [],\n durationMs: Date.now() - start,\n error: e instanceof Error ? e.message : String(e),\n };\n }\n}\n\n/**\n * Run all FunctionSteps in a project's pipelines and collect findings.\n * Browser-safe — no Node.js APIs, no child_process, no fs.\n *\n * If a pipeline has entries with `roots`, only steps whose id matches a\n * root are executed. If no entries exist or all entries have empty roots,\n * all steps run.\n */\nexport async function runPipeline(project: Project): Promise<PipelineResult> {\n const start = Date.now();\n const stepResults: StepResult[] = [];\n const allFindings: Finding[] = [];\n\n for (const pipeline of project.pipelines) {\n // Determine which steps to run based on entry roots. A root's\n // dependency closure comes along — a step depending on another can't\n // sensibly run without it, and its demo findings belong in the report.\n let stepsToRun = pipeline.steps;\n const entriesWithRoots = pipeline.entries.filter((e) => e.roots.length > 0);\n if (entriesWithRoots.length > 0) {\n const byId = new Map(pipeline.steps.map((s) => [s.id, s]));\n const rootSet = new Set<string>();\n const addWithDeps = (id: string): void => {\n if (rootSet.has(id)) return;\n rootSet.add(id);\n for (const dep of byId.get(id)?.dependencies ?? []) addWithDeps(dep);\n };\n for (const entry of entriesWithRoots) {\n for (const root of entry.roots) addWithDeps(root);\n }\n stepsToRun = pipeline.steps.filter((s) => rootSet.has(s.id));\n }\n\n // Execute all selected steps in parallel — dependencies are for\n // ordering, not data flow in the playground.\n const results = await Promise.all(\n stepsToRun.map((step) => executeStep(step)),\n );\n\n for (const result of results) {\n stepResults.push(result);\n allFindings.push(...result.findings);\n }\n }\n\n return {\n steps: stepResults,\n findings: allFindings,\n totalDurationMs: Date.now() - start,\n success: stepResults.every((s) => s.status === \"success\"),\n };\n}\n","// Share links (Spec 53): a ShareableRun serializes into the URL fragment —\n// `#c=` keeps payloads out of server logs and needs zero backend.\n// Encoding: JSON → deflate-raw → base64url.\n\nimport { deflateSync, Inflate } from \"fflate\";\nimport type { Finding, Severity } from \"./types.js\";\n\nexport interface ShareableRun {\n readonly schema: \"sverka.playground/v1\";\n readonly code: string;\n readonly findings?: readonly Finding[];\n}\n\nexport type PlaygroundErrorCode =\n \"TRANSPILE_FAILED\" | \"DECODE_FAILED\" | \"PAYLOAD_TOO_LARGE\";\n\nexport class PlaygroundError extends Error {\n readonly code: PlaygroundErrorCode;\n constructor(code: PlaygroundErrorCode, message: string) {\n super(message);\n this.name = \"PlaygroundError\";\n this.code = code;\n }\n}\n\n/** Fragment lengths are browser-dependent; warn past 32 KB of payload. */\nexport const SHARE_PAYLOAD_WARN_BYTES = 32 * 1024;\n\n/** Encoded payloads past 64 KB sit outside the safe URL-fragment range —\n * a link that long may arrive truncated, so decoding refuses outright. */\nexport const SHARE_PAYLOAD_MAX_BYTES = 64 * 1024;\n\n/** Hard cap on inflated share payloads — a compressed fragment is cheap to\n * craft into a deflate bomb, so decoding refuses past 1 MB of JSON. */\nconst SHARE_MAX_INFLATED_BYTES = 1024 * 1024;\n\nconst SHARE_PREFIX = \"#c=\";\n\nfunction toBase64Url(bytes: Uint8Array): string {\n let bin = \"\";\n for (const b of bytes) bin += String.fromCharCode(b);\n return btoa(bin).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nfunction fromBase64Url(text: string): Uint8Array {\n const b64 = text.replace(/-/g, \"+\").replace(/_/g, \"/\");\n // Browser atob() is strict about padding — restore what the encoder\n // stripped. (Node's atob tolerates missing padding, which is why the\n // round-trip test alone did not catch this.)\n const padded = b64 + \"=\".repeat((4 - (b64.length % 4)) % 4);\n const bin = atob(padded);\n const bytes = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);\n return bytes;\n}\n\n/** Inflate with a hard output cap — bounded memory on hostile payloads. */\nfunction inflateBounded(bytes: Uint8Array): Uint8Array {\n const chunks: Uint8Array[] = [];\n let total = 0;\n let overflow = false;\n const inflate = new Inflate((chunk) => {\n total += chunk.length;\n if (total > SHARE_MAX_INFLATED_BYTES) {\n overflow = true;\n return;\n }\n chunks.push(chunk);\n });\n inflate.push(bytes, true);\n if (overflow) {\n throw new PlaygroundError(\n \"PAYLOAD_TOO_LARGE\",\n `share link inflates past ${SHARE_MAX_INFLATED_BYTES} bytes`,\n );\n }\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.length;\n }\n return out;\n}\n\nconst SEVERITIES: readonly string[] = [\n \"info\",\n \"low\",\n \"medium\",\n \"high\",\n \"critical\",\n];\n\nconst FINDING_STRING_FIELDS = [\"rule\", \"file\", \"message\"] as const;\nconst FINDING_NUMBER_FIELDS = [\"startLine\", \"endLine\"] as const;\n\n/** Structural check — a shared finding reaches the HTML report, so every\n * field that gets rendered must be the expected scalar. */\nfunction isFindingShape(v: unknown): v is Finding {\n if (typeof v !== \"object\" || v === null) return false;\n const f = v as Record<string, unknown>;\n if (!FINDING_STRING_FIELDS.every((k) => typeof f[k] === \"string\")) {\n return false;\n }\n if (!SEVERITIES.includes(f.severity as Severity)) return false;\n return FINDING_NUMBER_FIELDS.every(\n (k) => typeof f[k] === \"number\" && Number.isFinite(f[k]),\n );\n}\n\n/** Encode a run into a share link. `base` is the page URL the link targets\n * (`location.origin + location.pathname` at runtime). */\nexport function encodeShareLink(run: ShareableRun, base = \"\"): string {\n const payload = toBase64Url(\n deflateSync(new TextEncoder().encode(JSON.stringify(run))),\n );\n return `${base}${SHARE_PREFIX}${payload}`;\n}\n\n/** Extract the payload tail from a `#c=` fragment or a bare payload. */\nfunction shareFragment(hash: string): string {\n const marker = hash.indexOf(SHARE_PREFIX);\n if (marker >= 0) return hash.slice(marker + SHARE_PREFIX.length);\n return hash.startsWith(\"#\") ? hash.slice(1) : hash;\n}\n\n/** Shape assertion — anything else is a corrupt or hostile payload. */\nfunction assertRunShape(parsed: unknown): asserts parsed is ShareableRun {\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n (parsed as ShareableRun).schema !== \"sverka.playground/v1\" ||\n typeof (parsed as ShareableRun).code !== \"string\"\n ) {\n throw new Error(\"payload is not a sverka.playground/v1 run\");\n }\n const findings = (parsed as ShareableRun).findings;\n if (findings === undefined) return;\n if (!Array.isArray(findings) || !findings.every(isFindingShape)) {\n throw new Error(\"payload findings failed shape validation\");\n }\n}\n\n/** Decode a `#c=` fragment back into a ShareableRun. Corrupt, oversized, or\n * shape-invalid payloads throw PlaygroundError — callers fall back to the\n * default template. */\nexport function decodeShareLink(hash: string): ShareableRun {\n try {\n const payload = shareFragment(hash);\n if (payload.length > SHARE_PAYLOAD_MAX_BYTES) {\n throw new PlaygroundError(\n \"PAYLOAD_TOO_LARGE\",\n `share link payload ${payload.length} bytes exceeds ${SHARE_PAYLOAD_MAX_BYTES}`,\n );\n }\n const json = new TextDecoder().decode(\n inflateBounded(fromBase64Url(payload)),\n );\n const parsed: unknown = JSON.parse(json);\n assertRunShape(parsed);\n return parsed;\n } catch (e) {\n if (e instanceof PlaygroundError) throw e;\n throw new PlaygroundError(\n \"DECODE_FAILED\",\n `share link could not be decoded: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n}\n","// @sverka/playground — shared mini-scanner primitives.\n// Char-class checks instead of regexes (a pattern evaluated against user\n// source is ReDoS surface); every helper is string/comment/regex aware so\n// quoted or literal braces never count as syntax. All scans are linear.\n\n// --- char classes --------------------------------------------------------\n\n/** Non-ASCII whitespace code points beyond the Latin-1 range. */\nconst WS_POINTS = new Set([\n 0xa0, 0x1680, 0x2028, 0x2029, 0x202f, 0x205f, 0x3000, 0xfeff,\n]);\n\n/** Whitespace per the JS grammar: \\t \\n \\v \\f \\r space, NBSP, the unicode\n * space separators (0x2000–0x200a), and ZWNBSP. */\nexport function isWsChar(c: string | undefined): boolean {\n if (c === undefined) return false;\n const n = c.codePointAt(0) ?? 0;\n if ((n >= 9 && n <= 13) || n === 32) return true;\n return (n >= 0x2000 && n <= 0x200a) || WS_POINTS.has(n);\n}\n\n/** ASCII letter-or-digit — digits and both letter cases. */\nfunction isAsciiLetterOrDigit(n: number): boolean {\n return (n >= 48 && n <= 57) || (n >= 65 && n <= 90) || (n >= 97 && n <= 122);\n}\n\n/** Identifier-part chars, approximated: ASCII ident chars plus any\n * non-ASCII char that isn't JS whitespace — `export` + a non-ASCII\n * letter keeps its boundary. Over-approximation only ever suppresses\n * a false keyword match — it never corrupts code. */\nexport function isIdentChar(c: string | undefined): boolean {\n if (c === undefined) return false;\n const n = c.codePointAt(0) ?? 0;\n if (n >= 0x80) return !isWsChar(c);\n return isAsciiLetterOrDigit(n) || c === \"_\" || c === \"$\";\n}\n\nexport function isQuote(c: string | undefined): boolean {\n return c === '\"' || c === \"'\" || c === \"`\";\n}\n\n// --- skips ---------------------------------------------------------------\n\nexport function skipIdent(code: string, i: number): number {\n while (i < code.length && isIdentChar(code[i])) i++;\n return i;\n}\n\n/** Index of the `\\n` ending the `//` comment at `at` (code.length if none). */\nexport function lineCommentEnd(code: string, at: number): number {\n const end = code.indexOf(\"\\n\", at + 2);\n return end === -1 ? code.length : end;\n}\n\n/** Index just past the closing star-slash ending the block comment at\n * `at` — the literal marker is never written inside this doc comment\n * or it would terminate the comment itself. */\nexport function blockCommentEnd(code: string, at: number): number {\n const end = code.indexOf(\"*/\", at + 2);\n return end === -1 ? code.length : end + 2;\n}\n\n/** True when `at` starts a `//` or `/*` comment. */\nexport function commentAt(code: string, at: number): boolean {\n return code[at] === \"/\" && (code[at + 1] === \"/\" || code[at + 1] === \"*\");\n}\n\n/** True when `at` opens a string literal or a comment. */\nexport function isOpaqueStart(code: string, at: number): boolean {\n return isQuote(code[at]) || commentAt(code, at);\n}\n\n/** Bound on `${}`-in-template nesting. Each level costs a scanString ↔\n * scanTemplateExpr recursion hop, so a crafted source (e.g. a share-link\n * payload) with pathological nesting could exhaust the call stack. Past\n * the cap the template is treated as unterminated — the tail is opaque\n * and the scan degrades gracefully instead of crashing. */\nconst MAX_TEMPLATE_DEPTH = 200;\n\n/** End index of the string literal starting at `at` (quote char).\n * Template literals may nest `${}` expressions — tracked by brace depth\n * up to MAX_TEMPLATE_DEPTH, where the scan bails to code.length. */\nexport function scanString(code: string, at: number, depth = 0): number {\n const quote = code[at];\n let i = at + 1;\n while (i < code.length) {\n const c = code[i];\n if (c === \"\\\\\") {\n i += 2;\n continue;\n }\n if (c === quote) return i + 1;\n if (quote === \"`\" && c === \"$\" && code[i + 1] === \"{\") {\n if (depth >= MAX_TEMPLATE_DEPTH) return code.length;\n i = scanTemplateExpr(code, i + 1, depth + 1);\n continue;\n }\n i++;\n }\n return i;\n}\n\n/** End index of a regex literal starting at `at` (`/`): closes at the\n * first unescaped `/` outside a `[]` class, then consumes flags. Stops at\n * a newline or EOF when unterminated — the evaluator reports the syntax. */\nexport function scanRegex(code: string, at: number): number {\n let i = at + 1;\n let inClass = false;\n while (i < code.length) {\n const c = code[i];\n if (c === \"\\\\\") {\n i += 2;\n continue;\n }\n if (c === \"[\") inClass = true;\n else if (c === \"]\") inClass = false;\n else if (c === \"/\" && !inClass) return skipIdent(code, i + 1);\n else if (c === \"\\n\") return i;\n i++;\n }\n return i;\n}\n\n/** End index of the opaque region at `i`, or i+1 for ordinary chars —\n * plus whether the region was a string literal (an operand). */\nexport function scanOpaqueEnd(\n code: string,\n i: number,\n depth = 0,\n): { end: number; isString: boolean } {\n const c = code[i];\n if (isQuote(c)) return { end: scanString(code, i, depth), isString: true };\n if (c === \"/\" && code[i + 1] === \"/\")\n return { end: lineCommentEnd(code, i), isString: false };\n if (c === \"/\" && code[i + 1] === \"*\")\n return { end: blockCommentEnd(code, i), isString: false };\n return { end: i + 1, isString: false };\n}\n\n/** Reserved words after which `/` opens a regex rather than divides —\n * they can never end an operand. Contextual keywords (`of`, `as`,\n * `from`, `get`, `set`, `type`, `async`, `declare`) are NOT listed:\n * they act as ordinary identifiers in most positions, so a `/` after\n * them divides. Everything else — plain identifiers plus literals such\n * as `this`/`true` — ends an operand, making `/` a division. */\nconst NON_OPERAND_WORDS = new Set([\n \"await\",\n \"break\",\n \"case\",\n \"catch\",\n \"class\",\n \"const\",\n \"continue\",\n \"debugger\",\n \"default\",\n \"delete\",\n \"do\",\n \"else\",\n \"enum\",\n \"export\",\n \"extends\",\n \"finally\",\n \"for\",\n \"function\",\n \"if\",\n \"implements\",\n \"import\",\n \"in\",\n \"instanceof\",\n \"interface\",\n \"let\",\n \"new\",\n \"package\",\n \"private\",\n \"protected\",\n \"public\",\n \"return\",\n \"static\",\n \"switch\",\n \"throw\",\n \"try\",\n \"typeof\",\n \"var\",\n \"void\",\n \"while\",\n \"with\",\n \"yield\",\n]);\n\n/** True when `word` ends an operand — a following `/` is division. */\nexport function operandAfterWord(word: string): boolean {\n return !NON_OPERAND_WORDS.has(word);\n}\n\ninterface TokenStep {\n /** Index just past the consumed token. */\n end: number;\n /** Whether the token ends an operand — a following `/` divides. */\n operandEnd: boolean;\n /** +1 for `{`, −1 for `}`, 0 otherwise. */\n brace: -1 | 0 | 1;\n}\n\n/** Consume one token at `i` for the brace-matching scans: opaque regions\n * and regex literals advance wholesale, identifiers update operand\n * state from the keyword table, braces report a depth delta. */\nfunction scanToken(\n code: string,\n i: number,\n operandEnd: boolean,\n depth = 0,\n): TokenStep {\n const opaque = scanOpaqueEnd(code, i, depth);\n if (opaque.end > i + 1) {\n return {\n end: opaque.end,\n operandEnd: opaque.isString || operandEnd,\n brace: 0,\n };\n }\n const ch = code[i];\n if (ch === \"/\" && !operandEnd) {\n return { end: scanRegex(code, i), operandEnd: true, brace: 0 };\n }\n if (isWsChar(ch)) return { end: i + 1, operandEnd, brace: 0 };\n if (isIdentChar(ch)) {\n const wend = skipIdent(code, i);\n return {\n end: wend,\n operandEnd: operandAfterWord(code.slice(i, wend)),\n brace: 0,\n };\n }\n if (ch === \"{\") return { end: i + 1, operandEnd: false, brace: 1 };\n if (ch === \"}\") return { end: i + 1, operandEnd: true, brace: -1 };\n return { end: i + 1, operandEnd: false, brace: 0 };\n}\n\n/**\n * Scan one operand-context token — an opaque span, a regex-or-division,\n * or an identifier run — returning where it ends and whether it leaves\n * an operand behind. Comments are transparent (operand context passes\n * through unchanged). Returns null for punctuation/whitespace the\n * caller's own loop owns.\n */\nexport function scanOperand(\n code: string,\n i: number,\n operandEnd: boolean,\n): { end: number; operandEnd: boolean } | null {\n const opaque = scanOpaqueEnd(code, i);\n if (opaque.end > i + 1)\n return { end: opaque.end, operandEnd: operandEnd || opaque.isString };\n const c = code[i];\n if (c === \"/\")\n return operandEnd\n ? { end: i + 1, operandEnd: false }\n : { end: scanRegex(code, i), operandEnd: true };\n if (isIdentChar(c)) {\n const wend = skipIdent(code, i);\n return {\n end: wend,\n operandEnd: operandAfterWord(code.slice(i, wend)),\n };\n }\n return null;\n}\n\n/**\n * End index of the `${` expression whose `{` is at `at`. Strings,\n * comments, nested templates, and regex literals inside the expression\n * are all skipped correctly: `` `${ x[\"`\"] }` `` and `` `${ /\"/ }` ``\n * must not treat literal text as syntax.\n */\nexport function scanTemplateExpr(code: string, at: number, depth = 0): number {\n let braces = 1;\n let i = at + 1;\n let operandEnd = false;\n while (i < code.length && braces > 0) {\n const step = scanToken(code, i, operandEnd, depth);\n braces += step.brace;\n operandEnd = step.operandEnd;\n i = step.end;\n }\n return i;\n}\n\n/**\n * Index of the `}` matching the `{` at `open`; -1 when unbalanced.\n * Operand tracking decides whether a `/` divides or opens a regex —\n * braces inside `/[{]/` never reach the depth counter.\n */\nexport function matchBrace(code: string, open: number): number {\n let depth = 0;\n let i = open;\n let operandEnd = false;\n while (i < code.length) {\n const step = scanToken(code, i, operandEnd);\n depth += step.brace;\n operandEnd = step.operandEnd;\n i = step.end;\n if (step.brace === -1 && depth === 0) return i - 1;\n }\n return -1;\n}\n\n/** `word` starting at `at` with an identifier boundary on the right. */\nexport function keywordAt(code: string, at: number, word: string): boolean {\n return code.startsWith(word, at) && !isIdentChar(code[at + word.length]);\n}\n\n/** Skip whitespace and comments (including newlines). */\nexport function skipTrivia(code: string, at: number): number {\n let i = at;\n for (;;) {\n const c = code[i];\n if (isWsChar(c)) {\n i++;\n continue;\n }\n if (c === \"/\" && code[i + 1] === \"/\") {\n i = lineCommentEnd(code, i);\n continue;\n }\n if (c === \"/\" && code[i + 1] === \"*\") {\n i = blockCommentEnd(code, i);\n continue;\n }\n return i;\n }\n}\n","// @sverka/playground — shared engine plumbing. Browser-safe.\n// Code template, user-code evaluation, and a timeout-wrapped run used by\n// both the full playground app and the embeddable mountRunner.\n\nimport {\n Project,\n Pipeline,\n FunctionStep,\n ShellStep,\n Entry,\n} from \"./pipeline.js\";\nimport { runPipeline } from \"./runner.js\";\nimport {\n commentAt,\n isIdentChar,\n isWsChar,\n keywordAt,\n matchBrace,\n operandAfterWord,\n scanOpaqueEnd,\n scanRegex,\n scanString,\n skipIdent,\n skipTrivia,\n} from \"./scanner.js\";\n\n/** Trigger helpers mirroring @sverka/workflow — real configs use\n * `trigger: push()`/`manual()`/`schedule()`/`changeRequest()`. The\n * optional filter argument is preserved, not silently discarded —\n * `push({ branches: [\"main\"] })` keeps its restriction. */\nconst push = (filter?: Record<string, unknown>) => ({\n kind: \"push\",\n ...filter,\n});\nconst changeRequest = (filter?: Record<string, unknown>) => ({\n kind: \"changeRequest\",\n ...filter,\n});\nconst manual = (filter?: Record<string, unknown>) => ({\n kind: \"manual\",\n ...filter,\n});\nconst schedule = (cron: string, timezone?: string) => ({\n kind: \"schedule\",\n cron,\n ...(timezone ? { timezone } : {}),\n});\n\n/** Default template shown in the editor. */\nexport const DEFAULT_CODE = [\n 'import { Project, Pipeline, FunctionStep, Entry } from \"@sverka/playground\";',\n \"\",\n 'const proj = new Project(\"demo\");',\n 'const checks = new Pipeline(proj, \"checks\");',\n \"\",\n 'new FunctionStep(checks, \"lint\", {',\n \" fn: () => [\",\n ' { rule: \"no-unused-vars\", file: \"src/index.ts\", line: 5, severity: \"high\", message: \"Variable \\'x\\' is declared but never used\" },',\n ' { rule: \"no-console\", file: \"src/utils.ts\", line: 12, severity: \"medium\", message: \"Unexpected console.log statement\" },',\n \" ],\",\n \"});\",\n \"\",\n 'new FunctionStep(checks, \"typecheck\", {',\n \" fn: () => [\",\n ' { rule: \"ts2322\", file: \"src/types.ts\", line: 8, severity: \"critical\", message: \"Type \\'string\\' is not assignable to type \\'number\\'\" },',\n \" ],\",\n \"});\",\n \"\",\n 'new FunctionStep(checks, \"test\", {',\n \" fn: () => [\",\n ' { rule: \"assertion-failed\", file: \"test/index.test.ts\", line: 23, severity: \"high\", message: \"Expected 5 but got 3\" },',\n ' { rule: \"assertion-failed\", file: \"test/index.test.ts\", line: 45, severity: \"low\", message: \"Expected \\'hello\\' but got \\'world\\'\" },',\n \" ],\",\n \"});\",\n \"\",\n 'new Entry(checks, \"on-push\", { trigger: { kind: \"push\" }, roots: [\"test\"] });',\n \"\",\n \"export default proj;\",\n \"\",\n].join(\"\\n\");\n\n/** Escape HTML special characters to prevent XSS. */\nexport function escapeHtml(text: string): string {\n return text\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\n/** +1/−1 for bracket chars, 0 otherwise. */\nfunction bracketDelta(c: string | undefined): number {\n if (c === \"{\" || c === \"(\" || c === \"[\") return 1;\n if (c === \"}\" || c === \")\" || c === \"]\") return -1;\n return 0;\n}\n\n/** Chars that end an import statement at depth ≤0: `;` (consumed) or a\n * newline after the module-specifier string (left in place). */\nfunction stmtTerminator(\n c: string | undefined,\n depth: number,\n specSeen: boolean,\n): \"past\" | \"at\" | null {\n if (c === \";\" && depth <= 0) return \"past\";\n if (c === \"\\n\" && depth <= 0 && specSeen) return \"at\";\n return null;\n}\n\n/** End index of the statement starting at `at`: first top-level `;`, or\n * the first newline after the module-specifier string (covers multiline\n * `import {…}\\n from \"x\"` and `import \"x\"` without a semicolon). */\nfunction scanStatementEnd(code: string, at: number): number {\n let i = at;\n let depth = 0;\n let specSeen = false;\n while (i < code.length) {\n const opaque = scanOpaqueEnd(code, i);\n if (opaque.end > i + 1) {\n specSeen = specSeen || opaque.isString;\n i = opaque.end;\n continue;\n }\n const term = stmtTerminator(code[i], depth, specSeen);\n if (term === \"past\") return i + 1;\n if (term === \"at\") return i;\n depth += bracketDelta(code[i]);\n i++;\n }\n return i;\n}\n\n/** Same-line statement tail: horizontal whitespace then an optional `;`.\n * Never eats the newline — the following line is the next statement. */\nfunction scanStmtTail(code: string, at: number): number {\n let i = at;\n while (i < code.length && (code[i] === \" \" || code[i] === \"\\t\")) i++;\n if (code[i] === \";\") i++;\n return i;\n}\n\n/** End index of the `from \"…\"` clause starting after `from`, or `at` when\n * the clause is malformed — let the evaluator complain. */\nfunction scanFromClause(code: string, next: number): number {\n const specAt = skipTrivia(code, next + 4);\n const q = code[specAt];\n if (q === '\"' || q === \"'\") {\n return scanStmtTail(code, scanString(code, specAt));\n }\n return next;\n}\n\n/** End index of `export * [as ns] [from \"…\"]` — the `*` is at `at`. */\nfunction scanExportStar(code: string, at: number): number {\n let i = skipTrivia(code, at + 1);\n if (keywordAt(code, i, \"as\")) {\n i = skipTrivia(code, i + 2);\n i = skipIdent(code, i);\n }\n const next = skipTrivia(code, i);\n if (keywordAt(code, next, \"from\")) return scanFromClause(code, next);\n return scanStmtTail(code, i);\n}\n\n/**\n * End index of `export {…}` / `export * [as ns] [from \"…\"]` starting at\n * the `{` or `*`. A bare list ends at its `;` or newline — unlike an\n * import, a newline after the closing brace must end the statement or\n * the next line would be eaten with it.\n */\nfunction scanExportListEnd(code: string, at: number): number {\n if (code[at] === \"*\") return scanExportStar(code, at);\n const close = matchBrace(code, at);\n const i = close < 0 ? code.length : close + 1;\n const next = skipTrivia(code, i);\n if (keywordAt(code, next, \"from\")) return scanFromClause(code, next);\n return scanStmtTail(code, i);\n}\n\ninterface ScanState {\n i: number;\n stmtStart: boolean;\n exportDefaultDone: boolean;\n /** `block` vs `expr` — separates block braces from object literals, so\n * `{ export: 1 }` is a key, not a statement. */\n stack: (\"block\" | \"expr\")[];\n /** True when the previous token ended an operand — `/` then divides.\n * Regex literals never update the delimiter stack, so `/[{]/` can't\n * leave an unclosed `{` that keeps `export default` alive to eval. */\n operandEnd: boolean;\n}\n\nfunction atStmtLevel(st: ScanState): boolean {\n return st.stack.length === 0 || st.stack.at(-1) === \"block\";\n}\n\n/** Copy a string literal or comment verbatim into `out`. */\nfunction copyOpaque(code: string, st: ScanState, out: string[]): boolean {\n const opaque = scanOpaqueEnd(code, st.i);\n if (opaque.end <= st.i + 1) return false;\n out.push(code.slice(st.i, opaque.end));\n if (opaque.isString) {\n st.stmtStart = false;\n st.operandEnd = true;\n }\n st.i = opaque.end;\n return true;\n}\n\n/** Handle `{}`/`()`/`[]` — pushes onto the block/expr stack and sets the\n * operand context: `}` closing an object literal ends a value, `}`\n * closing a block ends a statement. */\nfunction applyBracket(code: string, st: ScanState, out: string[]): boolean {\n const c = code[st.i];\n if (c === \"{\") {\n // A `{` at statement position opens a block; anywhere else it's an\n // object literal. Inside a block the next token is a statement;\n // inside an object it's a key — `export:` there is not a keyword.\n st.stack.push(st.stmtStart ? \"block\" : \"expr\");\n st.operandEnd = false;\n } else if (c === \"}\") {\n const popped = st.stack.pop();\n st.stmtStart = popped === \"block\";\n st.operandEnd = popped === \"expr\";\n } else if (c === \"(\" || c === \"[\") {\n st.stack.push(\"expr\");\n st.stmtStart = false;\n st.operandEnd = false;\n } else if (c === \")\" || c === \"]\") {\n st.stack.pop();\n st.stmtStart = false;\n st.operandEnd = true;\n } else {\n return false;\n }\n out.push(c ?? \"\");\n st.i++;\n return true;\n}\n\n/** Handle `\\n`, `;`, and whitespace — statement boundaries at statement\n * level only (a `;` inside `for (;;)` doesn't count). A newline never\n * resets operand context: `foo\\n/bar/` divides, per ASI rules. */\nfunction applyTerminator(code: string, st: ScanState, out: string[]): boolean {\n const c = code[st.i];\n if (c !== \";\" && c !== \"\\n\" && !isWsChar(c)) return false;\n if (c === \";\") {\n if (atStmtLevel(st)) st.stmtStart = true;\n st.operandEnd = false;\n } else if (c === \"\\n\" && atStmtLevel(st)) {\n st.stmtStart = true;\n }\n out.push(c ?? \"\");\n st.i++;\n return true;\n}\n\n/** Handle `/` — a regex literal when an operand is expected, a division\n * operator when one just ended. Comments never reach here (copyOpaque\n * already consumed them). */\nfunction applySlash(code: string, st: ScanState, out: string[]): boolean {\n if (code[st.i] !== \"/\" || commentAt(code, st.i)) return false;\n if (st.operandEnd) {\n out.push(\"/\");\n st.i++;\n st.operandEnd = false;\n } else {\n const end = scanRegex(code, st.i);\n out.push(code.slice(st.i, end));\n st.i = end;\n st.operandEnd = true;\n }\n st.stmtStart = false;\n return true;\n}\n\n/** Handle an `import` keyword at statement start. Dynamic `import (…)`\n * and `import.meta` pass through verbatim — whitespace and comments may\n * sit between the keyword and the paren. */\nfunction tryImport(code: string, st: ScanState, out: string[]): boolean {\n if (!st.stmtStart || !keywordAt(code, st.i, \"import\")) return false;\n const nc = code[skipTrivia(code, st.i + 6)];\n if (nc === \"(\" || nc === \".\") {\n out.push(\"import\");\n st.i += 6;\n st.stmtStart = false;\n st.operandEnd = true;\n return true;\n }\n st.i = scanStatementEnd(code, st.i + 6);\n st.stmtStart = false;\n st.operandEnd = false;\n return true;\n}\n\n/** Handle `export default` (→ `return`), `export {…}`/`export *` (removed)\n * and `export <decl>` (keyword dropped — the eval context is a function\n * body, where `export const` would be a syntax error). */\nfunction tryExport(code: string, st: ScanState, out: string[]): boolean {\n if (!st.stmtStart || !keywordAt(code, st.i, \"export\")) return false;\n const j = skipTrivia(code, st.i + 6);\n if (\n !st.exportDefaultDone &&\n code.startsWith(\"default\", j) &&\n !isIdentChar(code[j + 7])\n ) {\n // `return` must touch the expression — a newline between `default`\n // and the value would trigger ASI and return undefined.\n out.push(\"return \");\n st.exportDefaultDone = true;\n st.i = skipTrivia(code, j + 7);\n st.stmtStart = false;\n st.operandEnd = false;\n return true;\n }\n if (code[j] === \"{\" || code[j] === \"*\") {\n st.i = scanExportListEnd(code, j);\n st.stmtStart = false;\n st.operandEnd = false;\n return true;\n }\n st.i = j;\n st.stmtStart = false;\n st.operandEnd = false;\n return true;\n}\n\n/** Emit an identifier run — keyword table decides whether `/` after it\n * divides (`foo /x/`) or opens a regex (`return /x/`). */\nfunction tryWord(code: string, st: ScanState, out: string[]): boolean {\n if (!isIdentChar(code[st.i])) return false;\n const wend = skipIdent(code, st.i);\n out.push(code.slice(st.i, wend));\n st.operandEnd = operandAfterWord(code.slice(st.i, wend));\n st.stmtStart = false;\n st.i = wend;\n return true;\n}\n\n/** Char handlers tried in order — first true wins. Any char left over is\n * copied verbatim as a non-operand punctuation token. */\nconst HANDLERS: ((code: string, st: ScanState, out: string[]) => boolean)[] = [\n copyOpaque,\n applyBracket,\n applyTerminator,\n applySlash,\n tryImport,\n tryExport,\n tryWord,\n];\n\nfunction advanceScan(code: string, st: ScanState, out: string[]): void {\n for (const handle of HANDLERS) {\n if (handle(code, st, out)) return;\n }\n st.stmtStart = false;\n st.operandEnd = false;\n out.push(code[st.i] ?? \"\");\n st.i++;\n}\n\n/**\n * Strip import/export statements from user code for eval — a linear\n * single-pass scan, so multiline imports and comments/strings are handled\n * and pathological spacing cannot cause quadratic backtracking.\n *\n * `import …` statements are removed whole. `export default` becomes\n * `return`, `export {…}`/`export *` are removed, and `export` before a\n * declaration is dropped (the eval context is a function body, where\n * `export const` would be a syntax error).\n *\n * Note: TypeScript-specific syntax (type annotations, interfaces, enums)\n * is not stripped — the playground uses Monaco's TypeScript language mode\n * for editing, but evaluation is plain JavaScript. Users should write\n * JS-compatible code or use the `as any` escape hatch sparingly.\n */\nexport function preprocessCode(code: string): string {\n const out: string[] = [];\n const st: ScanState = {\n i: 0,\n stmtStart: true,\n exportDefaultDone: false,\n stack: [],\n operandEnd: false,\n };\n while (st.i < code.length) {\n if (code[st.i] === undefined) break;\n advanceScan(code, st, out);\n }\n return out.join(\"\");\n}\n\n/**\n * Evaluate user code and return the Project.\n *\n * SECURITY: This uses `new Function()` to execute user-provided code.\n * This is intentional — the playground is a local development tool where\n * the user writes and runs their own pipeline code. The code runs in the\n * browser's main page context (not a sandboxed iframe or worker) because\n * the playground needs to support synchronous FunctionStep execution and\n * direct access to the pipeline constructs. This is the same trust model\n * as a local REPL or `node -e`.\n */\nexport function evaluateUserCode(code: string): Project {\n const processed = preprocessCode(code);\n // Dynamic code execution is intentional for the playground sandbox.\n // SonarCloud S1523: safe — user code runs in the browser sandbox with the\n // same trust model as a local REPL or `node -e` (see comment above).\n const params = [\n \"Project\",\n \"Pipeline\",\n \"FunctionStep\",\n \"ShellStep\",\n \"Entry\",\n \"push\",\n \"changeRequest\",\n \"manual\",\n \"schedule\",\n processed,\n ];\n // NOSONAR suppresses only issues on the marker's own line — it must\n // trail the `new Function` callee, not sit inside the argument list.\n const fn = new Function(...params); // NOSONAR — intentional dynamic evaluation in sandbox\n const fnArgs = [\n Project,\n Pipeline,\n FunctionStep,\n ShellStep,\n Entry,\n push,\n changeRequest,\n manual,\n schedule,\n ];\n // NOSONAR suppresses only issues on the marker's own line — it must\n // trail the `fn(...)` invocation, not sit inside an argument list.\n const result = fn(...fnArgs); // NOSONAR — intentional dynamic evaluation in sandbox\n if (!(result instanceof Project)) {\n throw new TypeError(\"Code must export a Project instance\");\n }\n return result as Project;\n}\n\n/**\n * Run a pipeline with a timeout. If a step returns a never-resolving\n * promise, the timeout ensures the UI recovers instead of staying\n * stuck in \"Running\" state forever.\n */\nexport async function runPipelineWithTimeout(\n project: Project,\n timeoutMs: number,\n): Promise<Awaited<ReturnType<typeof runPipeline>>> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n runPipeline(project),\n new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error(`Pipeline timed out after ${timeoutMs}ms`)),\n timeoutMs,\n );\n }),\n ]);\n } finally {\n // Without this, every completed run leaves a live timer that rejects\n // into an unhandled rejection timeoutMs later.\n if (timer !== undefined) clearTimeout(timer);\n }\n}\n","// @sverka/playground — mountRunner embed API (Spec 53).\n// Docs pages mount a self-contained runner: code (editable or readonly),\n// a Run button, status line, and a findings iframe. Zero backend — the\n// runner evaluates pipeline code in-page under the same trust model as\n// the full playground (see engine.ts).\n\nimport { generateSarifHtml } from \"@sverka/sarif-viewer-web/html-generator\";\nimport type { PipelineResult } from \"./types.js\";\nimport {\n DEFAULT_CODE,\n escapeHtml,\n evaluateUserCode,\n runPipelineWithTimeout,\n} from \"./engine.js\";\n\nexport interface MountRunnerOptions {\n /** Initial editor source. Defaults to the playground template. */\n readonly code?: string;\n /** Render the source as a fixed listing instead of an editable field. */\n readonly readonly?: boolean;\n /** Run `code` immediately on mount. Defaults to false — embed code may\n * come from a share link or CMS, so mounting never executes it unless\n * the embedder opts in or the user clicks Run. */\n readonly autoRun?: boolean;\n /** Called once per completed run — unless the runner was disposed\n * first; teardown silences callbacks for runs still in flight. */\n readonly onRun?: (result: PipelineResult) => void;\n}\n\nexport interface MountedRunner {\n /** Remove the runner's DOM and listeners. */\n dispose(): void;\n}\n\nconst STYLE = `\n font-family: monospace; font-size: 12px;\n background: #0d1117; color: #c9d1d9;\n border: 1px solid #30363d; border-radius: 6px;\n display: flex; flex-direction: column; overflow: hidden;\n`;\n\nfunction createHeader(doc: Document): {\n header: HTMLElement;\n runBtn: HTMLButtonElement;\n status: HTMLSpanElement;\n} {\n const header = doc.createElement(\"div\");\n header.setAttribute(\n \"style\",\n \"display:flex;align-items:center;gap:0.5rem;padding:0.4rem 0.6rem;background:#161b22;border-bottom:1px solid #30363d;\",\n );\n const runBtn = doc.createElement(\"button\");\n runBtn.textContent = \"Run\";\n runBtn.setAttribute(\n \"style\",\n \"padding:0.2rem 0.8rem;border:1px solid #30363d;border-radius:6px;background:#1f6feb;color:#fff;cursor:pointer;font-size:0.8rem;\",\n );\n const status = doc.createElement(\"span\");\n status.setAttribute(\"style\", \"font-size:0.75rem;color:#8b949e;\");\n status.textContent = \"Ready\";\n header.append(runBtn, status);\n return { header, runBtn, status };\n}\n\nfunction createCodeEl(\n doc: Document,\n code: string,\n readonly: boolean,\n): HTMLTextAreaElement | HTMLElement {\n if (readonly) {\n const pre = doc.createElement(\"pre\");\n pre.setAttribute(\n \"style\",\n \"margin:0;padding:0.75rem;overflow:auto;max-height:16rem;white-space:pre;\",\n );\n pre.textContent = code;\n return pre;\n }\n const textarea = doc.createElement(\"textarea\");\n textarea.value = code;\n textarea.setAttribute(\n \"style\",\n \"flex:0 0 12rem;width:100%;background:#0d1117;color:#c9d1d9;border:none;border-bottom:1px solid #30363d;padding:0.75rem;resize:vertical;outline:none;font:inherit;\",\n );\n return textarea;\n}\n\nfunction createFrame(doc: Document): HTMLIFrameElement {\n const frame = doc.createElement(\"iframe\");\n // Shared findings render via srcdoc — sandboxed onto an opaque origin so a\n // crafted payload can never touch the host page; allow-scripts keeps the\n // report's own viewer interactivity (search/filter/sort) working.\n frame.setAttribute(\"sandbox\", \"allow-scripts\");\n frame.setAttribute(\n \"style\",\n \"flex:1;min-height:10rem;width:100%;border:none;background:#0d1117;\",\n );\n return frame;\n}\n\nconst BODY_DARK =\n '<body style=\"background:#0d1117;font-family:monospace;padding:1rem;';\n\n/** Failure banner HTML — string concat keeps user data flowing through\n * escapeHtml explicitly, never through template interpolation. */\nfunction failureBanner(result: PipelineResult): string {\n const failed = result.steps.filter((s) => s.status === \"failure\");\n const lines =\n failed.map((s) => `${s.stepId}: ${s.error ?? \"failed\"}`).join(\"\\n\") ||\n `${result.steps.length} steps failed`;\n return [\n '<div style=\"background:#3d1d1f;color:#f85149;padding:0.6rem 1rem;font-family:monospace;border-bottom:1px solid #30363d;\">',\n \"<strong>Run failed</strong>\",\n '<pre style=\"margin:0.4rem 0 0;white-space:pre-wrap;\">',\n escapeHtml(lines),\n \"</pre></div>\",\n ].join(\"\");\n}\n\n/** Document shown when the run failed — partial findings still render\n * underneath the banner so finished steps aren't hidden by the error. */\nfunction failureDoc(result: PipelineResult, banner: string): string {\n if (result.findings.length === 0) {\n return (\n \"<!DOCTYPE html><html>\" +\n BODY_DARK +\n 'color:#f85149;\">' +\n banner +\n \"</body></html>\"\n );\n }\n const sarif = generateSarifHtml(result.findings);\n return sarif.includes(\"<body>\")\n ? sarif.replace(\"<body>\", \"<body>\" + banner)\n : banner + sarif;\n}\n\n/** Document shown for a clean run with zero findings. */\nfunction successDoc(result: PipelineResult): string {\n return [\n \"<!DOCTYPE html><html>\",\n BODY_DARK,\n 'color:#3fb950;\">',\n \"<h3>No findings — all checks passed</h3><p>\",\n escapeHtml(String(result.steps.length)),\n \" steps in \",\n escapeHtml(String(result.totalDurationMs)),\n \"ms</p></body></html>\",\n ].join(\"\");\n}\n\n/** Document shown when the runner itself threw (eval or pipeline error). */\nfunction errorDoc(e: unknown): string {\n const msg = e instanceof Error ? e.message : String(e);\n return [\n \"<!DOCTYPE html><html>\",\n BODY_DARK,\n 'color:#f85149;\">',\n \"<h3>Error</h3><pre>\",\n escapeHtml(msg),\n \"</pre></body></html>\",\n ].join(\"\");\n}\n\nfunction statusLine(result: PipelineResult): string {\n if (result.success)\n return `${result.findings.length} findings · ${result.totalDurationMs}ms`;\n const failures = result.steps.filter((s) => s.status === \"failure\").length;\n return `${failures} step(s) failed · ${result.findings.length} findings`;\n}\n\n/** Render a completed run into the findings frame. */\nfunction renderResult(\n result: PipelineResult,\n showFindings: (html: string) => void,\n): void {\n if (!result.success) {\n showFindings(failureDoc(result, failureBanner(result)));\n return;\n }\n if (result.findings.length === 0) {\n showFindings(successDoc(result));\n return;\n }\n showFindings(generateSarifHtml(result.findings));\n}\n\n/**\n * Mount a self-contained runner inside `el`. `dispose()` tears the mount\n * down. With `autoRun: true` the runner executes `code` once on mount;\n * otherwise it waits for the Run button — embeds never auto-execute code\n * the embedder didn't explicitly allow.\n */\nexport function mountRunner(\n el: HTMLElement,\n opts: MountRunnerOptions = {},\n): MountedRunner {\n const doc = el.ownerDocument;\n const code = opts.code ?? DEFAULT_CODE;\n\n const root = doc.createElement(\"div\");\n root.setAttribute(\"style\", STYLE);\n const { header, runBtn, status } = createHeader(doc);\n const codeEl = createCodeEl(doc, code, opts.readonly === true);\n const frame = createFrame(doc);\n root.append(header, codeEl, frame);\n el.appendChild(root);\n\n // Branch on the option, not instanceof — a container owned by another\n // window would produce a cross-realm textarea that fails the check.\n const getCode = (): string =>\n opts.readonly === true ? code : (codeEl as HTMLTextAreaElement).value;\n\n // A run in flight outlives dispose() — the flag suppresses its callback.\n let disposed = false;\n\n const run = async (): Promise<void> => {\n runBtn.disabled = true;\n status.textContent = \"Running...\";\n let result: PipelineResult;\n try {\n const project = evaluateUserCode(getCode());\n result = await runPipelineWithTimeout(project, 30_000);\n renderResult(result, (html) => {\n frame.srcdoc = html;\n });\n status.textContent = statusLine(result);\n } catch (e) {\n frame.srcdoc = errorDoc(e);\n status.textContent = \"Error\";\n runBtn.disabled = false;\n return;\n }\n runBtn.disabled = false;\n // Outside the try — a throwing callback must not corrupt the findings\n // view or get misreported as a runner failure.\n if (!disposed) opts.onRun?.(result);\n };\n\n const onClick = (): void => {\n void run();\n };\n runBtn.addEventListener(\"click\", onClick);\n if (opts.autoRun === true) void run();\n\n return {\n dispose() {\n disposed = true;\n runBtn.removeEventListener(\"click\", onClick);\n root.remove();\n },\n };\n}\n","// @sverka/playground — toSverkaConfig (Spec 53): the \"take it home\" bridge.\n// Converts playground source into a real sverka.config.ts: the authoring\n// surface (Project/Pipeline/Entry/roots) transfers verbatim — only the step\n// kind differs. FunctionStep bodies cannot run as shell commands, so each\n// becomes a ShellStep carrying a placeholder echo the user replaces.\n\nimport { PlaygroundError } from \"./share.js\";\nimport {\n isIdentChar,\n isOpaqueStart,\n isWsChar,\n keywordAt,\n matchBrace,\n scanOpaqueEnd,\n scanOperand,\n scanString,\n skipIdent,\n} from \"./scanner.js\";\n\n// --- local char helpers (only transpile needs these) ----------------------\n\nfunction isOpenBracket(c: string | undefined): boolean {\n return c === \"{\" || c === \"[\" || c === \"(\";\n}\n\nfunction isCloseBracket(c: string | undefined): boolean {\n return c === \"}\" || c === \"]\" || c === \")\";\n}\n\nfunction skipWs(code: string, i: number): number {\n while (i < code.length && isWsChar(code[i])) i++;\n return i;\n}\n\n// ---------------------------------------------------------------------------\n// Every matcher below is string-, comment-, and regex-literal-aware via the\n// shared scanner — braces inside `/[{]/` or quotes never count as syntax,\n// and all scans are linear (no regexes over user source at all).\n// ---------------------------------------------------------------------------\n\n/** One token of an operand-tracking scan — identifiers (with `word`),\n * opaque spans, and regex-vs-division resolve through the shared\n * scanner; whitespace passes through. Null when `i` sits on\n * punctuation the caller's own depth/branch logic owns. */\nfunction propToken(\n s: string,\n i: number,\n operandEnd: boolean,\n): { end: number; operandEnd: boolean; word?: string } | null {\n if (isWsChar(s[i])) return { end: i + 1, operandEnd };\n const op = scanOperand(s, i, operandEnd);\n if (op === null) return null;\n if (isIdentChar(s[i])) return { ...op, word: s.slice(i, op.end) };\n return op;\n}\n\n/** Apply a bracket/other punctuation char to depth + operand state. */\nfunction applyPunctuation(\n c: string | undefined,\n st: { depth: number; operandEnd: boolean },\n): void {\n if (isOpenBracket(c)) {\n st.depth++;\n st.operandEnd = false;\n } else if (isCloseBracket(c)) {\n st.depth--;\n st.operandEnd = true;\n } else {\n st.operandEnd = false;\n }\n}\n\n/**\n * Index of the `:` following a depth-0 `key` in a props object body, or -1.\n * A `dependencies` declared inside `fn` never counts — only top-level keys.\n */\nfunction findPropKey(props: string, key: string): number {\n const st = { depth: 0, operandEnd: false };\n let i = 0;\n while (i < props.length) {\n const tok = propToken(props, i, st.operandEnd);\n if (tok === null) {\n applyPunctuation(props[i], st);\n i++;\n continue;\n }\n if (st.depth === 0 && tok.word === key) {\n const hit = skipWs(props, tok.end);\n if (props[hit] === \":\") return hit;\n }\n st.operandEnd = tok.operandEnd;\n i = tok.end;\n }\n return -1;\n}\n\n/** True when the comment at `k` is a top-level tail comment — `deps: [] //\n * note` — and therefore not part of the value. Emitting it would comment\n * out the generated `}` after `dependsOn: <value>`. */\nfunction isTailComment(props: string, k: number, after: number): boolean {\n const ahead = skipWs(props, after);\n return ahead >= props.length || props[ahead] === \",\";\n}\n\n/** End index of a prop value starting at `j` — the next top-level comma\n * or the end of the props body, with strings/comments/brackets balanced. */\nfunction propValueEnd(props: string, j: number): number {\n const st = { depth: 0, operandEnd: false };\n let k = j;\n while (k < props.length) {\n if (isOpaqueStart(props, k)) {\n const after = scanOpaqueEnd(props, k).end;\n if (st.depth === 0 && isTailComment(props, k, after)) return k;\n k = after;\n continue;\n }\n const tok = propToken(props, k, st.operandEnd);\n if (tok !== null) {\n st.operandEnd = tok.operandEnd;\n k = tok.end;\n continue;\n }\n if (props[k] === \",\" && st.depth === 0) return k;\n applyPunctuation(props[k], st);\n k++;\n }\n return k;\n}\n\n/**\n * Extract a top-level `key: <value>` span from a props object body.\n * Only depth-0 keys match — a `dependencies` declared inside `fn` never\n * counts. The value runs to the next top-level `,`, with strings,\n * comments, and nested brackets all balanced.\n */\nfunction extractProp(props: string, key: string): string | undefined {\n const colon = findPropKey(props, key);\n if (colon < 0) return undefined;\n const j = skipWs(props, colon + 1);\n return props.slice(j, propValueEnd(props, j)).trim();\n}\n\n/** Consume trailing spaces/tabs plus one optional `;` — never the newline.\n * Eating it would fuse the emitted import with the next line\n * (`from \"@sverka/workflow\"const x = …`). */\nfunction stmtEndPos(source: string, i: number): number {\n let end = i;\n while (source[end] === \" \" || source[end] === \"\\t\") end++;\n if (source[end] === \";\") end++;\n return end;\n}\n\n/** Parse one import specifier: `Name` or `Name as alias`. */\nfunction parseImportSpec(spec: string): { orig: string; local: string } {\n const a = skipWs(spec, 0);\n const wend = skipIdent(spec, a);\n const orig = spec.slice(a, wend);\n const asPos = skipWs(spec, wend);\n if (!spec.startsWith(\"as\", asPos) || isIdentChar(spec[asPos + 2])) {\n return { orig, local: orig };\n }\n const b = skipWs(spec, asPos + 2);\n const local = spec.slice(b, skipIdent(spec, b));\n return local === \"\" ? { orig, local: orig } : { orig, local };\n}\n\ninterface MappedImports {\n mapped: string;\n fnStepLocals: string[];\n}\n\n/** Map `{…}` import specifiers; FunctionStep becomes ShellStep (aliases\n * survive: `FunctionStep as Fn` → `ShellStep as Fn`). */\nfunction mapImportSpecifiers(inner: string): MappedImports {\n const mapped: string[] = [];\n const fnStepLocals: string[] = [];\n for (const spec of inner.split(\",\")) {\n const trimmed = spec.trim();\n if (trimmed === \"\") continue;\n const { orig, local } = parseImportSpec(trimmed);\n if (orig === \"FunctionStep\") {\n fnStepLocals.push(local);\n mapped.push(orig === local ? \"ShellStep\" : `ShellStep as ${local}`);\n } else {\n mapped.push(trimmed);\n }\n }\n return { mapped: mapped.join(\", \"), fnStepLocals };\n}\n\ninterface PlaygroundImport {\n stmtEnd: number;\n mapped: string;\n fnStepLocals: string[];\n}\n\n/** Parse `import {…} from \"@sverka/playground\"` at `i`, or null when the\n * statement has another shape/module. Caller guarantees \"import\" at `i`. */\nfunction parsePlaygroundImport(\n source: string,\n i: number,\n): PlaygroundImport | null {\n const j = skipWs(source, i + 6);\n if (source[j] !== \"{\") return null;\n const namesEnd = matchBrace(source, j);\n if (namesEnd < 0) return null;\n const fromKw = skipWs(source, namesEnd + 1);\n if (!keywordAt(source, fromKw, \"from\")) return null;\n const qPos = skipWs(source, fromKw + 4);\n const q = source[qPos];\n if (q !== '\"' && q !== \"'\") return null;\n const specEnd = scanString(source, qPos);\n if (source.slice(qPos + 1, specEnd - 1) !== \"@sverka/playground\") return null;\n const { mapped, fnStepLocals } = mapImportSpecifiers(\n source.slice(j + 1, namesEnd),\n );\n return { stmtEnd: stmtEndPos(source, specEnd), mapped, fnStepLocals };\n}\n\n/**\n * Rewrite `import {…} from \"@sverka/playground\"` statements linearly.\n *\n * Returns the rewritten source plus every local name bound to\n * FunctionStep, so constructor calls under an alias are transpiled too.\n */\nfunction rewritePlaygroundImports(source: string): {\n code: string;\n fnStepNames: string[];\n} {\n const fnStepNames: string[] = [];\n let out = \"\";\n let cursor = 0;\n let i = 0;\n while (i < source.length) {\n const end = scanOpaqueEnd(source, i).end;\n if (end > i + 1) {\n i = end;\n continue;\n }\n if (source.startsWith(\"import\", i) && !isIdentChar(source[i + 6])) {\n const stmt = parsePlaygroundImport(source, i);\n if (stmt !== null) {\n out += source.slice(cursor, i);\n out += `import { ${stmt.mapped} } from \"@sverka/workflow\"`;\n if (source[stmt.stmtEnd - 1] === \";\") out += \";\";\n fnStepNames.push(...stmt.fnStepLocals);\n cursor = stmt.stmtEnd;\n i = stmt.stmtEnd;\n continue;\n }\n }\n i++;\n }\n return { code: out + source.slice(cursor), fnStepNames };\n}\n\n/** Index of `(` after `new <name>` where <name> ∈ names, or null. */\nfunction stepCallParen(\n code: string,\n afterNew: number,\n names: readonly string[],\n): number | null {\n const j = skipWs(code, afterNew);\n for (const name of names) {\n const next = code[j + name.length];\n if (code.startsWith(name, j) && !isIdentChar(next)) {\n const paren = skipWs(code, j + name.length);\n if (code[paren] === \"(\") return paren;\n }\n }\n return null;\n}\n\n/** Operand context after punctuation — closers end a value, everything\n * else (openers, operators, separators) expects one. */\nfunction punctuationOperandEnd(c: string | undefined): boolean {\n return c === \")\" || c === \"]\" || c === \"}\";\n}\n\n/**\n * Locate the next `new <name>(` call where <name> ∈ names. Whole-word\n * scanning makes boundary checks free — `myNew` or `newer` never match.\n * Opaque regions and regex literals are skipped inline, so a\n * `new FunctionStep(` inside a comment, string, or `/new Fn(/` pattern\n * is documentation, not a call.\n */\nfunction findStepCall(\n code: string,\n names: readonly string[],\n from: number,\n): { start: number; paren: number } | null {\n let operandEnd = false;\n let i = from;\n while (i < code.length) {\n const tok = propToken(code, i, operandEnd);\n if (tok !== null) {\n if (tok.word === \"new\") {\n const paren = stepCallParen(code, tok.end, names);\n if (paren !== null) return { start: i, paren };\n }\n operandEnd = tok.operandEnd;\n i = tok.end;\n continue;\n }\n operandEnd = punctuationOperandEnd(code[i]);\n i++;\n }\n return null;\n}\n\n/** End of the scope expression — the first top-level comma after `from`,\n * or -1 when the argument list closes/ends first. */\nfunction scanScopeEnd(code: string, from: number): number {\n const st = { depth: 0, operandEnd: false };\n let k = from;\n while (k < code.length) {\n const tok = propToken(code, k, st.operandEnd);\n if (tok !== null) {\n st.operandEnd = tok.operandEnd;\n k = tok.end;\n continue;\n }\n const c = code[k];\n if (c === \",\" && st.depth === 0) return k;\n if (isCloseBracket(c) && st.depth === 0) return -1;\n applyPunctuation(c, st);\n k++;\n }\n return -1;\n}\n\ninterface StepArgs {\n scope: string;\n id: string;\n propsOpen: number;\n}\n\n/** Parse `(scope, \"id\", {` after the `(` at `paren`; null when the call\n * shape differs. A template id throws immediately — never half-converts. */\nfunction parseStepArgs(code: string, paren: number): StepArgs | null {\n const scopeStart = skipWs(code, paren + 1);\n const comma = scanScopeEnd(code, scopeStart);\n if (comma < 0) return null;\n const qPos = skipWs(code, comma + 1);\n const q = code[qPos];\n if (q === \"`\")\n throw new PlaygroundError(\n \"TRANSPILE_FAILED\",\n \"cannot parse FunctionStep — template-literal step id\",\n );\n if (q !== '\"' && q !== \"'\") return null;\n const idEnd = scanString(code, qPos);\n const afterId = skipWs(code, idEnd);\n if (code[afterId] !== \",\") return null;\n const propsOpen = skipWs(code, afterId + 1);\n if (code[propsOpen] !== \"{\") return null;\n return {\n scope: code.slice(scopeStart, comma).trim(),\n id: code.slice(qPos + 1, idEnd - 1),\n propsOpen,\n };\n}\n\n/** Emit the ShellStep placeholder replacing one FunctionStep call. */\nfunction shellStepCall(args: StepArgs, dependencies: string | undefined) {\n // The id lands inside shell single quotes — $()/backticks can never\n // expand. `'` is already impossible (the id is a plain string literal).\n return (\n `new ShellStep(${args.scope}, \"${args.id}\", ` +\n `{ command: \"echo 'TODO: port '${args.id}' — replace with the real shell command'\"` +\n (dependencies !== undefined ? \", dependsOn: \" + dependencies : \"\") +\n \" }\"\n );\n}\n\n/** Rewrite every `new Fn(scope, \"id\", {…})` call under the given local\n * names into ShellStep placeholders; `dependencies` → `dependsOn`. */\nfunction rewriteStepCalls(code: string, names: readonly string[]): string {\n let result = \"\";\n let cursor = 0;\n for (;;) {\n const call = findStepCall(code, names, cursor);\n if (call === null) break;\n const args = parseStepArgs(code, call.paren);\n if (args === null) {\n // A `new Fn(` whose shape we don't own — skip `new` and let the\n // leftover check name it, rather than silently dropping it.\n result += code.slice(cursor, call.start + 3);\n cursor = call.start + 3;\n continue;\n }\n const propsClose = matchBrace(code, args.propsOpen);\n if (propsClose < 0)\n throw new PlaygroundError(\n \"TRANSPILE_FAILED\",\n `cannot parse FunctionStep \"${args.id}\" — unbalanced props object`,\n );\n const dependencies = extractProp(\n code.slice(args.propsOpen + 1, propsClose),\n \"dependencies\",\n );\n result +=\n code.slice(cursor, call.start) + shellStepCall(args, dependencies);\n cursor = propsClose + 1;\n }\n return result + code.slice(cursor);\n}\n\n/** A surviving step call means a shape we could not convert — computed\n * scope/id or a malformed alias call. Fail loud rather than emit a\n * half-converted file. */\nfunction assertNoLeftoverSteps(code: string, names: readonly string[]): void {\n const call = findStepCall(code, names, 0);\n if (call === null) return;\n const scopeStart = skipWs(code, call.paren + 1);\n const scopeEnd = scanScopeEnd(code, scopeStart);\n const scope =\n scopeEnd < 0 ? \"?\" : code.slice(scopeStart, scopeEnd).trim() || \"?\";\n throw new PlaygroundError(\n \"TRANSPILE_FAILED\",\n `cannot parse FunctionStep with scope '${scope}' — unsupported expression`,\n );\n}\n\n/**\n * Transpile playground source into `sverka.config.ts`.\n *\n * Rewrites the `@sverka/playground` import to `@sverka/workflow` and each\n * `new FunctionStep(scope, \"id\", { fn, dependencies })` into a ShellStep\n * whose command echoes a placeholder — the function body has no shell\n * equivalent.\n * `dependencies` maps onto `dependsOn`. Aliased FunctionStep imports\n * (`FunctionStep as Fn`) are followed too.\n *\n * Throws `PlaygroundError(TRANSPILE_FAILED)` naming the offending step when\n * a FunctionStep cannot be parsed — a half-converted file is never emitted.\n */\nexport function toSverkaConfig(source: string): string {\n const { code, fnStepNames } = rewritePlaygroundImports(source);\n if (fnStepNames.length === 0) fnStepNames.push(\"FunctionStep\");\n const out = rewriteStepCalls(code, fnStepNames);\n assertNoLeftoverSteps(out, fnStepNames);\n return out;\n}\n","// @sverka/playground — examples gallery (Spec 53).\n// `examples/*/` mini-projects are bundled at build time by the vite entry\n// (app.ts collects them via import.meta.glob) — the picker needs no\n// network access. This module stays bundler-agnostic so the library\n// build (tsdown) never sees vite-only APIs.\n\nexport interface GalleryExample {\n /** Directory name, e.g. \"failing-checks\". */\n readonly id: string;\n /** Humanized label for the picker. */\n readonly title: string;\n /** sverka.config.ts source, bundled into the playground build. */\n readonly code: string;\n}\n\nfunction toTitle(id: string): string {\n return id\n .split(\"-\")\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(\" \");\n}\n\n/** Turn a `{ \"<path>/examples/<id>/sverka.config.ts\": source }` map into a\n * sorted picker list. */\nexport function listExamples(\n modules: Record<string, string>,\n): readonly GalleryExample[] {\n return Object.entries(modules)\n .map(([path, code]) => {\n const id =\n /examples\\/([^/]+)\\/sverka\\.config\\.ts$/.exec(path)?.[1] ?? path;\n return { id, title: toTitle(id), code };\n })\n .sort((a, b) => a.id.localeCompare(b.id));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/pipeline.ts","../src/runner.ts","../src/share.ts","../src/scanner.ts","../src/engine.ts","../src/embed.ts","../src/transpile.ts","../src/gallery.ts"],"sourcesContent":["// @sverka/playground — minimal pipeline model. Browser-safe.\n// Mirrors @sverka/workflow Construct API but simplified for browser use.\n\nimport type { PlaygroundFinding } from \"./types.js\";\n\n/** Base class for all constructs. */\nexport abstract class Construct {\n readonly id: string;\n readonly scope: Construct | null;\n\n constructor(scope: Construct | null, id: string) {\n this.id = id;\n this.scope = scope;\n }\n\n /** Full path from root to this construct. */\n get path(): string {\n if (this.scope && this.scope.id !== \"root\") {\n return `${this.scope.path}/${this.id}`;\n }\n return this.id;\n }\n}\n\n/** Root construct representing a project. */\nexport class Project extends Construct {\n readonly pipelines: Pipeline[] = [];\n\n constructor(id: string) {\n super(null, id);\n }\n\n /** Add a pipeline to this project. */\n addPipeline(pipeline: Pipeline): void {\n this.pipelines.push(pipeline);\n }\n}\n\n/** A pipeline containing steps. */\nexport class Pipeline extends Construct {\n readonly steps: Step[] = [];\n readonly entries: Entry[] = [];\n\n constructor(project: Project, id: string) {\n super(project, id);\n project.addPipeline(this);\n }\n\n /** Add a step to this pipeline. */\n addStep(step: Step): void {\n this.steps.push(step);\n }\n\n /** Add an entry to this pipeline. */\n addEntry(entry: Entry): void {\n this.entries.push(entry);\n }\n}\n\n/** Base class for all step types. */\nexport abstract class Step extends Construct {\n readonly dependencies: string[];\n\n constructor(pipeline: Pipeline, id: string, dependencies: string[] = []) {\n super(pipeline, id);\n this.dependencies = dependencies;\n pipeline.addStep(this);\n }\n\n /** Execute this step and return findings. */\n abstract execute(): Promise<PlaygroundFinding[]> | PlaygroundFinding[];\n}\n\n/** A step that runs a JavaScript function in the browser. */\nexport class FunctionStep extends Step {\n readonly fn: () => Promise<PlaygroundFinding[]> | PlaygroundFinding[];\n\n constructor(\n pipeline: Pipeline,\n id: string,\n options: {\n fn: () => Promise<PlaygroundFinding[]> | PlaygroundFinding[];\n dependencies?: string[];\n },\n ) {\n super(pipeline, id, options.dependencies ?? []);\n this.fn = options.fn;\n }\n\n execute(): Promise<PlaygroundFinding[]> | PlaygroundFinding[] {\n return this.fn();\n }\n}\n\n/**\n * Browser stub of the real `@sverka/workflow` ShellStep. Real configs load\n * verbatim in the playground, but a shell command cannot execute in a\n * browser — execute() surfaces an info finding explaining the demo\n * boundary instead of pretending the command ran.\n */\nexport class ShellStep extends Step {\n readonly command: string;\n\n constructor(\n pipeline: Pipeline,\n id: string,\n options: {\n command: string;\n dependsOn?: string[];\n runtime?: unknown;\n outputs?: unknown;\n background?: boolean;\n },\n ) {\n super(pipeline, id, options.dependsOn ?? []);\n this.command = options.command;\n }\n\n execute(): PlaygroundFinding[] {\n return [\n {\n rule: \"playground/shell-step\",\n file: \"sverka.config.ts\",\n line: 1,\n severity: \"info\",\n // The command text is deliberately not echoed — a real command may\n // carry inline secrets, and findings end up in share links.\n message: `'${this.id}' is a shell step — the browser cannot run it. Run the exported config locally to execute it.`,\n },\n ];\n }\n}\n\n/** An entry point that triggers a pipeline. */\nexport class Entry {\n readonly pipeline: Pipeline;\n readonly id: string;\n readonly trigger: { kind: string };\n readonly roots: string[];\n\n constructor(\n pipeline: Pipeline,\n id: string,\n options: { trigger: { kind: string }; roots: string[] },\n ) {\n this.pipeline = pipeline;\n this.id = id;\n this.trigger = options.trigger;\n this.roots = options.roots;\n pipeline.addEntry(this);\n }\n}\n","// @sverka/playground — pipeline runner. Browser-safe.\n// Executes FunctionSteps and collects findings.\n\nimport type { Project } from \"./pipeline.js\";\nimport type { Pipeline } from \"./pipeline.js\";\nimport type { Step } from \"./pipeline.js\";\nimport type {\n Finding,\n PlaygroundFinding,\n PipelineResult,\n StepResult,\n} from \"./types.js\";\n\n/** Simple deterministic hash for fingerprint generation. Browser-safe. */\nfunction simpleHash(input: string): string {\n let hash = 5381;\n for (let i = 0; i < input.length; i++) {\n hash = ((hash << 5) + hash + input.charCodeAt(i)) | 0;\n }\n return (hash >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/** Convert a PlaygroundFinding to a full Finding with fingerprint and id. */\nfunction toFinding(pf: PlaygroundFinding, stepId: string): Finding {\n // Include checkId (stepId) in fingerprint so identical findings from\n // different checks get distinct fingerprints.\n const fingerprintInput = `${stepId}:${pf.rule}:${pf.file}:${pf.line}`;\n const fingerprint = simpleHash(fingerprintInput);\n const id = `${stepId}:${fingerprint}`;\n return {\n id,\n fingerprint,\n checkId: stepId,\n severity: pf.severity,\n confidence: 0.8,\n message: pf.message,\n rule: pf.rule,\n file: pf.file,\n startLine: pf.line,\n endLine: pf.line,\n source: {\n tool: pf.tool ?? stepId,\n version: null,\n format: \"custom\",\n originalRuleId: pf.rule,\n originalSeverity: null,\n },\n ...(pf.snippet ? { snippet: pf.snippet } : {}),\n };\n}\n\n/** Execute a single step and return its result. */\nasync function executeStep(step: Step): Promise<StepResult> {\n const start = Date.now();\n try {\n const playgroundFindings = await step.execute();\n const findings = playgroundFindings.map((pf) => toFinding(pf, step.id));\n return {\n stepId: step.id,\n status: \"success\",\n findings,\n durationMs: Date.now() - start,\n };\n } catch (e) {\n return {\n stepId: step.id,\n status: \"failure\",\n findings: [],\n durationMs: Date.now() - start,\n error: e instanceof Error ? e.message : String(e),\n };\n }\n}\n\n/**\n * Run all FunctionSteps in a project's pipelines and collect findings.\n * Browser-safe — no Node.js APIs, no child_process, no fs.\n *\n * If a pipeline has entries with `roots`, only steps whose id matches a\n * root are executed. If no entries exist or all entries have empty roots,\n * all steps run.\n */\nexport async function runPipeline(project: Project): Promise<PipelineResult> {\n const start = Date.now();\n const stepResults: StepResult[] = [];\n const allFindings: Finding[] = [];\n\n for (const pipeline of project.pipelines) {\n // Determine which steps to run based on entry roots. A root's\n // dependency closure comes along — a step depending on another can't\n // sensibly run without it, and its demo findings belong in the report.\n let stepsToRun = pipeline.steps;\n const entriesWithRoots = pipeline.entries.filter((e) => e.roots.length > 0);\n if (entriesWithRoots.length > 0) {\n const byId = new Map(pipeline.steps.map((s) => [s.id, s]));\n const rootSet = new Set<string>();\n const addWithDeps = (id: string): void => {\n if (rootSet.has(id)) return;\n rootSet.add(id);\n for (const dep of byId.get(id)?.dependencies ?? []) addWithDeps(dep);\n };\n for (const entry of entriesWithRoots) {\n for (const root of entry.roots) addWithDeps(root);\n }\n stepsToRun = pipeline.steps.filter((s) => rootSet.has(s.id));\n }\n\n // Execute all selected steps in parallel — dependencies are for\n // ordering, not data flow in the playground.\n const results = await Promise.all(\n stepsToRun.map((step) => executeStep(step)),\n );\n\n for (const result of results) {\n stepResults.push(result);\n allFindings.push(...result.findings);\n }\n }\n\n return {\n steps: stepResults,\n findings: allFindings,\n totalDurationMs: Date.now() - start,\n success: stepResults.every((s) => s.status === \"success\"),\n };\n}\n","// Share links (Spec 53): a ShareableRun serializes into the URL fragment —\n// `#c=` keeps payloads out of server logs and needs zero backend.\n// Encoding: JSON → deflate-raw → base64url.\n\nimport { deflateSync, Inflate } from \"fflate\";\nimport type { Finding, Severity } from \"./types.js\";\n\nexport interface ShareableRun {\n readonly schema: \"sverka.playground/v1\";\n readonly code: string;\n readonly findings?: readonly Finding[];\n}\n\nexport type PlaygroundErrorCode =\n \"TRANSPILE_FAILED\" | \"DECODE_FAILED\" | \"PAYLOAD_TOO_LARGE\";\n\nexport class PlaygroundError extends Error {\n readonly code: PlaygroundErrorCode;\n constructor(code: PlaygroundErrorCode, message: string) {\n super(message);\n this.name = \"PlaygroundError\";\n this.code = code;\n }\n}\n\n/** Fragment lengths are browser-dependent; warn past 32 KB of payload. */\nexport const SHARE_PAYLOAD_WARN_BYTES = 32 * 1024;\n\n/** Encoded payloads past 64 KB sit outside the safe URL-fragment range —\n * a link that long may arrive truncated, so decoding refuses outright. */\nexport const SHARE_PAYLOAD_MAX_BYTES = 64 * 1024;\n\n/** Hard cap on inflated share payloads — a compressed fragment is cheap to\n * craft into a deflate bomb, so decoding refuses past 1 MB of JSON. */\nconst SHARE_MAX_INFLATED_BYTES = 1024 * 1024;\n\nconst SHARE_PREFIX = \"#c=\";\n\nfunction toBase64Url(bytes: Uint8Array): string {\n let bin = \"\";\n for (const b of bytes) bin += String.fromCharCode(b);\n return btoa(bin).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nfunction fromBase64Url(text: string): Uint8Array {\n const b64 = text.replace(/-/g, \"+\").replace(/_/g, \"/\");\n // Browser atob() is strict about padding — restore what the encoder\n // stripped. (Node's atob tolerates missing padding, which is why the\n // round-trip test alone did not catch this.)\n const padded = b64 + \"=\".repeat((4 - (b64.length % 4)) % 4);\n const bin = atob(padded);\n const bytes = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);\n return bytes;\n}\n\n/** Inflate with a hard output cap — bounded memory on hostile payloads. */\nfunction inflateBounded(bytes: Uint8Array): Uint8Array {\n const chunks: Uint8Array[] = [];\n let total = 0;\n let overflow = false;\n const inflate = new Inflate((chunk) => {\n total += chunk.length;\n if (total > SHARE_MAX_INFLATED_BYTES) {\n overflow = true;\n return;\n }\n chunks.push(chunk);\n });\n inflate.push(bytes, true);\n if (overflow) {\n throw new PlaygroundError(\n \"PAYLOAD_TOO_LARGE\",\n `share link inflates past ${SHARE_MAX_INFLATED_BYTES} bytes`,\n );\n }\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.length;\n }\n return out;\n}\n\nconst SEVERITIES: readonly string[] = [\n \"info\",\n \"low\",\n \"medium\",\n \"high\",\n \"critical\",\n];\n\nconst FINDING_STRING_FIELDS = [\"rule\", \"file\", \"message\"] as const;\nconst FINDING_NUMBER_FIELDS = [\"startLine\", \"endLine\"] as const;\n\n/** Structural check — a shared finding reaches the HTML report, so every\n * field that gets rendered must be the expected scalar. */\nfunction isFindingShape(v: unknown): v is Finding {\n if (typeof v !== \"object\" || v === null) return false;\n const f = v as Record<string, unknown>;\n if (!FINDING_STRING_FIELDS.every((k) => typeof f[k] === \"string\")) {\n return false;\n }\n if (!SEVERITIES.includes(f.severity as Severity)) return false;\n return FINDING_NUMBER_FIELDS.every(\n (k) => typeof f[k] === \"number\" && Number.isFinite(f[k]),\n );\n}\n\n/** Encode a run into a share link. `base` is the page URL the link targets\n * (`location.origin + location.pathname` at runtime). */\nexport function encodeShareLink(run: ShareableRun, base = \"\"): string {\n const payload = toBase64Url(\n deflateSync(new TextEncoder().encode(JSON.stringify(run))),\n );\n return `${base}${SHARE_PREFIX}${payload}`;\n}\n\n/** Extract the payload tail from a `#c=` fragment or a bare payload. */\nfunction shareFragment(hash: string): string {\n const marker = hash.indexOf(SHARE_PREFIX);\n if (marker >= 0) return hash.slice(marker + SHARE_PREFIX.length);\n return hash.startsWith(\"#\") ? hash.slice(1) : hash;\n}\n\n/** Shape assertion — anything else is a corrupt or hostile payload. */\nfunction assertRunShape(parsed: unknown): asserts parsed is ShareableRun {\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n (parsed as ShareableRun).schema !== \"sverka.playground/v1\" ||\n typeof (parsed as ShareableRun).code !== \"string\"\n ) {\n throw new Error(\"payload is not a sverka.playground/v1 run\");\n }\n const findings = (parsed as ShareableRun).findings;\n if (findings === undefined) return;\n if (!Array.isArray(findings) || !findings.every(isFindingShape)) {\n throw new Error(\"payload findings failed shape validation\");\n }\n}\n\n/** Decode a `#c=` fragment back into a ShareableRun. Corrupt, oversized, or\n * shape-invalid payloads throw PlaygroundError — callers fall back to the\n * default template. */\nexport function decodeShareLink(hash: string): ShareableRun {\n try {\n const payload = shareFragment(hash);\n if (payload.length > SHARE_PAYLOAD_MAX_BYTES) {\n throw new PlaygroundError(\n \"PAYLOAD_TOO_LARGE\",\n `share link payload ${payload.length} bytes exceeds ${SHARE_PAYLOAD_MAX_BYTES}`,\n );\n }\n const json = new TextDecoder().decode(\n inflateBounded(fromBase64Url(payload)),\n );\n const parsed: unknown = JSON.parse(json);\n assertRunShape(parsed);\n return parsed;\n } catch (e) {\n if (e instanceof PlaygroundError) throw e;\n throw new PlaygroundError(\n \"DECODE_FAILED\",\n `share link could not be decoded: ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n}\n","// @sverka/playground — shared mini-scanner primitives.\n// Char-class checks instead of regexes (a pattern evaluated against user\n// source is ReDoS surface); every helper is string/comment/regex aware so\n// quoted or literal braces never count as syntax. All scans are linear.\n\n// --- char classes --------------------------------------------------------\n\n/** Non-ASCII whitespace code points beyond the Latin-1 range. */\nconst WS_POINTS = new Set([\n 0xa0, 0x1680, 0x2028, 0x2029, 0x202f, 0x205f, 0x3000, 0xfeff,\n]);\n\n/** Whitespace per the JS grammar: \\t \\n \\v \\f \\r space, NBSP, the unicode\n * space separators (0x2000–0x200a), and ZWNBSP. */\nexport function isWsChar(c: string | undefined): boolean {\n if (c === undefined) return false;\n const n = c.codePointAt(0) ?? 0;\n if ((n >= 9 && n <= 13) || n === 32) return true;\n return (n >= 0x2000 && n <= 0x200a) || WS_POINTS.has(n);\n}\n\n/** ECMAScript LineTerminators: LF, CR, U+2028, U+2029. Line comments and\n * unterminated regex literals end at any of them — not only at LF. */\nexport function isLineTerminator(c: string | undefined): boolean {\n return c === \"\\n\" || c === \"\\r\" || c === \"\\u2028\" || c === \"\\u2029\";\n}\n\n/** ASCII letter-or-digit — digits and both letter cases. */\nfunction isAsciiLetterOrDigit(n: number): boolean {\n return (n >= 48 && n <= 57) || (n >= 65 && n <= 90) || (n >= 97 && n <= 122);\n}\n\n/** Identifier-part chars, approximated: ASCII ident chars plus any\n * non-ASCII char that isn't JS whitespace — `export` + a non-ASCII\n * letter keeps its boundary. Over-approximation only ever suppresses\n * a false keyword match — it never corrupts code. */\nexport function isIdentChar(c: string | undefined): boolean {\n if (c === undefined) return false;\n const n = c.codePointAt(0) ?? 0;\n if (n >= 0x80) return !isWsChar(c);\n return isAsciiLetterOrDigit(n) || c === \"_\" || c === \"$\";\n}\n\nexport function isQuote(c: string | undefined): boolean {\n return c === '\"' || c === \"'\" || c === \"`\";\n}\n\n/** ASCII digit. */\nexport function isDigit(c: string | undefined): boolean {\n return c !== undefined && c >= \"0\" && c <= \"9\";\n}\n\n// --- skips ---------------------------------------------------------------\n\nexport function skipIdent(code: string, i: number): number {\n while (i < code.length && isIdentChar(code[i])) i++;\n return i;\n}\n\n/** Index of the first LineTerminator ending the `//` comment at `at`\n * (code.length if none). CR, U+2028 and U+2029 terminate a line comment\n * just like LF — a `//` note ending at `\\r` must release the code that\n * follows it. */\nexport function lineCommentEnd(code: string, at: number): number {\n let i = at + 2;\n while (i < code.length && !isLineTerminator(code[i])) i++;\n return i;\n}\n\n/** Index just past the closing star-slash ending the block comment at\n * `at` — the literal marker is never written inside this doc comment\n * or it would terminate the comment itself. */\nexport function blockCommentEnd(code: string, at: number): number {\n const end = code.indexOf(\"*/\", at + 2);\n return end === -1 ? code.length : end + 2;\n}\n\n/** True when `at` starts a `//` or `/*` comment. */\nexport function commentAt(code: string, at: number): boolean {\n return code[at] === \"/\" && (code[at + 1] === \"/\" || code[at + 1] === \"*\");\n}\n\n/** True when `at` opens a string literal or a comment. */\nexport function isOpaqueStart(code: string, at: number): boolean {\n return isQuote(code[at]) || commentAt(code, at);\n}\n\n/** Bound on `${}`-in-template nesting. Each level costs a scanString ↔\n * scanTemplateExpr recursion hop, so a crafted source (e.g. a share-link\n * payload) with pathological nesting could exhaust the call stack. Past\n * the cap the template is treated as unterminated — the tail is opaque\n * and the scan degrades gracefully instead of crashing. */\nconst MAX_TEMPLATE_DEPTH = 200;\n\n/** End index of the string literal starting at `at` (quote char).\n * Template literals may nest `${}` expressions — tracked by brace depth\n * up to MAX_TEMPLATE_DEPTH, where the scan bails to code.length. */\nexport function scanString(code: string, at: number, depth = 0): number {\n const quote = code[at];\n let i = at + 1;\n while (i < code.length) {\n const c = code[i];\n if (c === \"\\\\\") {\n i += 2;\n continue;\n }\n if (c === quote) return i + 1;\n if (quote === \"`\" && c === \"$\" && code[i + 1] === \"{\") {\n if (depth >= MAX_TEMPLATE_DEPTH) return code.length;\n i = scanTemplateExpr(code, i + 1, depth + 1);\n continue;\n }\n i++;\n }\n return i;\n}\n\n/** True when `at` begins a numeric literal — a digit, or `.` followed by\n * a digit (`.5`). Member access never puts a digit right after the dot,\n * so this is unambiguous at operand-scan positions. */\nexport function numberAt(code: string, at: number): boolean {\n return isDigit(code[at]) || (code[at] === \".\" && isDigit(code[at + 1]));\n}\n\n/** End of the digit run at `i` — digits and `_` separators. */\nfunction skipDigits(code: string, i: number): number {\n while (isDigit(code[i]) || code[i] === \"_\") i++;\n return i;\n}\n\n/** End of a `e[+-]?digits` exponent at `i`, or `i` when absent. */\nfunction scanExponent(code: string, i: number): number {\n if (code[i] !== \"e\" && code[i] !== \"E\") return i;\n let j = i + 1;\n if (code[j] === \"+\" || code[j] === \"-\") j++;\n return isDigit(code[j]) ? skipDigits(code, j) : i;\n}\n\n/** End index of the numeric literal at `at` — fraction, exponent, `_`\n * separators, the bigint `n`, and radix digits (`0x`/`0o`/`0b`, which\n * live in ident space) are all consumed, so `5. / 2` still ends the\n * operand rather than leaving `/` to scan as a regex opener. */\nexport function scanNumber(code: string, at: number): number {\n let i = at;\n if (code[i] === \"0\" && \"xXoObB\".includes(code[i + 1] ?? \"\")) {\n return skipIdent(code, i + 2);\n }\n i = skipDigits(code, i);\n if (code[i] === \".\") i = skipDigits(code, i + 1);\n i = scanExponent(code, i);\n if (code[i] === \"n\") i++;\n return i;\n}\n\n/** End index of a regex literal starting at `at` (`/`): closes at the\n * first unescaped `/` outside a `[]` class, then consumes flags. Stops\n * at any LineTerminator or EOF when unterminated — the evaluator\n * reports the syntax. */\nexport function scanRegex(code: string, at: number): number {\n let i = at + 1;\n let inClass = false;\n while (i < code.length) {\n const c = code[i];\n if (c === \"\\\\\") {\n i += 2;\n continue;\n }\n if (c === \"[\") inClass = true;\n else if (c === \"]\") inClass = false;\n else if (c === \"/\" && !inClass) return skipIdent(code, i + 1);\n else if (isLineTerminator(c)) return i;\n i++;\n }\n return i;\n}\n\n/** End index of the opaque region at `i`, or i+1 for ordinary chars —\n * plus whether the region was a string literal (an operand). */\nexport function scanOpaqueEnd(\n code: string,\n i: number,\n depth = 0,\n): { end: number; isString: boolean } {\n const c = code[i];\n if (isQuote(c)) return { end: scanString(code, i, depth), isString: true };\n if (c === \"/\" && code[i + 1] === \"/\")\n return { end: lineCommentEnd(code, i), isString: false };\n if (c === \"/\" && code[i + 1] === \"*\")\n return { end: blockCommentEnd(code, i), isString: false };\n return { end: i + 1, isString: false };\n}\n\n/** Reserved words and true operators after which `/` opens a regex\n * rather than divides. Everything else — plain identifiers, literals\n * such as `this`/`true`, and the contextual keywords `async`/`get`/`set`/\n * `of`/`from`/`as`/`type`/`declare`/`static` — ends an operand, making\n * `/` a division: `async / 2` divides because `async` is a valid\n * identifier. The only contextual exception lives one level up: `of`\n * inside a `for (` header is the for-of keyword, handled by\n * wordOperandEnd via the paren stack. */\nconst NON_OPERAND_WORDS = new Set([\n \"await\",\n \"break\",\n \"case\",\n \"catch\",\n \"class\",\n \"const\",\n \"continue\",\n \"debugger\",\n \"default\",\n \"delete\",\n \"do\",\n \"else\",\n \"enum\",\n \"export\",\n \"extends\",\n \"finally\",\n \"for\",\n \"function\",\n \"if\",\n \"implements\",\n \"import\",\n \"in\",\n \"instanceof\",\n \"interface\",\n \"let\",\n \"new\",\n \"package\",\n \"private\",\n \"protected\",\n \"public\",\n \"return\",\n \"switch\",\n \"throw\",\n \"try\",\n \"typeof\",\n \"var\",\n \"void\",\n \"while\",\n \"with\",\n \"yield\",\n]);\n\n/** True when `word` ends an operand — a following `/` is division. */\nexport function operandAfterWord(word: string): boolean {\n return !NON_OPERAND_WORDS.has(word);\n}\n\n/** Words whose `(` opens a control-flow header, not a grouping or call:\n * after the matching `)` a statement follows, so a `/` may open a regex\n * — `if (x) /re/`. `function` is deliberately absent: its `)` precedes\n * the body `{`, and `function() {} / 2` divides. */\nexport const CONTROL_WORDS: ReadonlySet<string> = new Set([\n \"catch\",\n \"for\",\n \"if\",\n \"switch\",\n \"while\",\n \"with\",\n]);\n\n/** Operand-scan state for the regex-vs-division heuristic:\n *\n * - `operandEnd` — the previous token ended an operand, so `/` divides.\n * - `parens` — open `(` frames, innermost last: the control word that\n * opened each, or null for grouping/call parens. `[`/`{` never enter\n * here — `]` always ends an operand and `}` is caller-owned.\n * - `pendingCtl` — a control word awaiting its `(`; survives whitespace\n * and comments between word and paren, cleared by any real token.\n *\n * This is a bounded heuristic, not a tokenizer. Known limits: `of` is\n * the for-of keyword only inside a `for (` header (elsewhere an\n * identifier — `of / 2` divides); `await`/`yield`/`let` stay keyword-\n * guessed; a `}` closing a statement block counts as an operand end, so\n * `while (c) {} /re/` divides inside the brace-matching scans. */\nexport interface OperandScan {\n operandEnd: boolean;\n parens: (string | null)[];\n pendingCtl: string | null;\n}\n\n/** Fresh operand-scan state starting in operand-expected position. */\nexport function newOperandScan(): OperandScan {\n return { operandEnd: false, parens: [], pendingCtl: null };\n}\n\n/** Operand context after `word`: `of` inside a `for (` header introduces\n * the iterated expression (`for (x of /re/)`), everywhere else it's an\n * identifier; all other words resolve through the keyword table. */\nfunction wordOperandEnd(\n word: string,\n parens: readonly (string | null)[],\n): boolean {\n if (word === \"of\" && parens.at(-1) === \"for\") return false;\n return operandAfterWord(word);\n}\n\n/** True when `i` starts a `++` or `--` pair — postfix ends the operand,\n * prefix still expects one; either way the state survives. */\nfunction doubleSignAt(code: string, i: number): boolean {\n const c = code[i];\n return (c === \"+\" || c === \"-\") && code[i + 1] === c;\n}\n\n/** `/` after an operand divides (one char); at operand-expected\n * position it opens a regex literal, consumed whole. */\nfunction slashTokenEnd(code: string, i: number, st: OperandScan): number {\n st.pendingCtl = null;\n if (st.operandEnd) {\n st.operandEnd = false;\n return i + 1;\n }\n st.operandEnd = true;\n return scanRegex(code, i);\n}\n\n/** End of a `.prop` member access at the `.` — skips trivia, then the\n * property ident when one follows. `?.` chains and each `.` of `...`\n * land here too, where the leading pair simply finds no ident. */\nfunction memberPropEnd(code: string, i: number): number {\n const prop = skipTrivia(code, i + 1);\n return isIdentChar(code[prop]) && !isDigit(code[prop])\n ? skipIdent(code, prop)\n : i + 1;\n}\n\n/** End of the identifier/keyword run at `i`, updating operand state:\n * keyword-ness decides whether a following `/` divides, and control\n * words arm `pendingCtl` for their header `(`. `await` is the one word\n * allowed between `for` and its `(` — `for await (x of y)` keeps the\n * control context. */\nfunction wordTokenEnd(code: string, i: number, st: OperandScan): number {\n const wend = skipIdent(code, i);\n const word = code.slice(i, wend);\n st.operandEnd = wordOperandEnd(word, st.parens);\n st.pendingCtl =\n word === \"await\" && st.pendingCtl === \"for\"\n ? \"for\"\n : CONTROL_WORDS.has(word)\n ? word\n : null;\n return wend;\n}\n\n/** Shared operand-token scan: opaque spans (strings end operands,\n * comments pass context through), `++`/`--` pairs, `/` (regex unless an\n * operand just ended), numbers, identifier runs, and whitespace\n * (consume-and-preserve). Returns the token end, or null for\n * bracket/separator punctuation the caller's own loop owns. */\nfunction operandToken(\n code: string,\n i: number,\n st: OperandScan,\n depth: number,\n): number | null {\n const opaque = scanOpaqueEnd(code, i, depth);\n if (opaque.end > i + 1) {\n if (opaque.isString) {\n st.operandEnd = true;\n st.pendingCtl = null;\n }\n return opaque.end;\n }\n const c = code[i];\n if (doubleSignAt(code, i)) {\n st.pendingCtl = null;\n return i + 2;\n }\n if (c === \"/\") return slashTokenEnd(code, i, st);\n if (isWsChar(c)) return i + 1;\n if (numberAt(code, i)) {\n st.operandEnd = true;\n st.pendingCtl = null;\n return scanNumber(code, i);\n }\n if (c === \".\") {\n // Member access: a word after `.` is a property name, never a\n // keyword — `obj.return / 2` divides. `.5` was taken by numberAt.\n st.pendingCtl = null;\n st.operandEnd = true;\n return memberPropEnd(code, i);\n }\n if (isIdentChar(c)) return wordTokenEnd(code, i, st);\n return null;\n}\n\ninterface TokenStep {\n /** Index just past the consumed token. */\n end: number;\n /** +1 for `{`, −1 for `}`, 0 otherwise. */\n brace: -1 | 0 | 1;\n}\n\n/** Consume one token at `i` for the brace-matching scans, mutating `st`:\n * operand tokens defer to operandToken, `(` pushes its control context,\n * `)` reports operand-expected when it closed a control header, and\n * braces report a depth delta. */\nfunction scanToken(\n code: string,\n i: number,\n st: OperandScan,\n depth = 0,\n): TokenStep {\n const end = operandToken(code, i, st, depth);\n if (end !== null) return { end, brace: 0 };\n const ch = code[i];\n const ctl = st.pendingCtl;\n st.pendingCtl = null;\n if (ch === \"(\") {\n st.parens.push(ctl);\n st.operandEnd = false;\n return { end: i + 1, brace: 0 };\n }\n if (ch === \")\") {\n // A control-header `)` precedes a statement — `/` may open a regex;\n // a grouping or call `)` ends the operand, so `/` divides.\n const kind = st.parens.pop();\n st.operandEnd = kind === undefined || kind === null;\n return { end: i + 1, brace: 0 };\n }\n if (ch === \"{\") {\n st.operandEnd = false;\n return { end: i + 1, brace: 1 };\n }\n if (ch === \"}\") {\n st.operandEnd = true;\n return { end: i + 1, brace: -1 };\n }\n st.operandEnd = ch === \"]\";\n return { end: i + 1, brace: 0 };\n}\n\n/**\n * Scan one operand-context token — an opaque span, a `++`/`--` pair, a\n * regex-or-division, a number, or an identifier run — mutating `st` and\n * returning where the token ends. Comments are transparent (operand\n * context and a pending control word pass through unchanged). Returns\n * null for punctuation the caller's own loop owns.\n */\nexport function scanOperand(\n code: string,\n i: number,\n st: OperandScan,\n): number | null {\n return operandToken(code, i, st, 0);\n}\n\n/**\n * End index of the `${` expression whose `{` is at `at`. Strings,\n * comments, nested templates, and regex literals inside the expression\n * are all skipped correctly: `` `${ x[\"`\"] }` `` and `` `${ /\"/ }` ``\n * must not treat literal text as syntax.\n */\nexport function scanTemplateExpr(code: string, at: number, depth = 0): number {\n let braces = 1;\n let i = at + 1;\n const st = newOperandScan();\n while (i < code.length && braces > 0) {\n const step = scanToken(code, i, st, depth);\n braces += step.brace;\n i = step.end;\n }\n return i;\n}\n\n/**\n * Index of the `}` matching the `{` at `open`; -1 when unbalanced.\n * Operand tracking decides whether a `/` divides or opens a regex —\n * braces inside `/[{]/` never reach the depth counter.\n */\nexport function matchBrace(code: string, open: number): number {\n let depth = 0;\n let i = open;\n const st = newOperandScan();\n while (i < code.length) {\n const step = scanToken(code, i, st);\n depth += step.brace;\n i = step.end;\n if (step.brace === -1 && depth === 0) return i - 1;\n }\n return -1;\n}\n\n/** `word` starting at `at` with an identifier boundary on the right. */\nexport function keywordAt(code: string, at: number, word: string): boolean {\n return code.startsWith(word, at) && !isIdentChar(code[at + word.length]);\n}\n\n/** Skip whitespace and comments (including newlines). */\nexport function skipTrivia(code: string, at: number): number {\n let i = at;\n for (;;) {\n const c = code[i];\n if (isWsChar(c)) {\n i++;\n continue;\n }\n if (c === \"/\" && code[i + 1] === \"/\") {\n i = lineCommentEnd(code, i);\n continue;\n }\n if (c === \"/\" && code[i + 1] === \"*\") {\n i = blockCommentEnd(code, i);\n continue;\n }\n return i;\n }\n}\n","// @sverka/playground — shared engine plumbing. Browser-safe.\n// Code template, user-code evaluation, and a timeout-wrapped run used by\n// both the full playground app and the embeddable mountRunner.\n\nimport {\n Project,\n Pipeline,\n FunctionStep,\n ShellStep,\n Entry,\n} from \"./pipeline.js\";\nimport { runPipeline } from \"./runner.js\";\nimport {\n commentAt,\n CONTROL_WORDS,\n isDigit,\n isIdentChar,\n isLineTerminator,\n isWsChar,\n keywordAt,\n matchBrace,\n numberAt,\n operandAfterWord,\n scanNumber,\n scanOpaqueEnd,\n scanRegex,\n scanString,\n skipIdent,\n skipTrivia,\n} from \"./scanner.js\";\n\n/** Trigger helpers mirroring @sverka/workflow — real configs use\n * `trigger: push()`/`manual()`/`schedule()`/`changeRequest()`. The\n * optional filter argument is preserved, not silently discarded —\n * `push({ branches: [\"main\"] })` keeps its restriction. */\nconst push = (filter?: Record<string, unknown>) => ({\n kind: \"push\",\n ...filter,\n});\nconst changeRequest = (filter?: Record<string, unknown>) => ({\n kind: \"changeRequest\",\n ...filter,\n});\nconst manual = (filter?: Record<string, unknown>) => ({\n kind: \"manual\",\n ...filter,\n});\nconst schedule = (cron: string, timezone?: string) => ({\n kind: \"schedule\",\n cron,\n ...(timezone ? { timezone } : {}),\n});\n\n/** Default template shown in the editor. */\nexport const DEFAULT_CODE = [\n 'import { Project, Pipeline, FunctionStep, Entry } from \"@sverka/playground\";',\n \"\",\n 'const proj = new Project(\"demo\");',\n 'const checks = new Pipeline(proj, \"checks\");',\n \"\",\n 'new FunctionStep(checks, \"lint\", {',\n \" fn: () => [\",\n ' { rule: \"no-unused-vars\", file: \"src/index.ts\", line: 5, severity: \"high\", message: \"Variable \\'x\\' is declared but never used\" },',\n ' { rule: \"no-console\", file: \"src/utils.ts\", line: 12, severity: \"medium\", message: \"Unexpected console.log statement\" },',\n \" ],\",\n \"});\",\n \"\",\n 'new FunctionStep(checks, \"typecheck\", {',\n \" fn: () => [\",\n ' { rule: \"ts2322\", file: \"src/types.ts\", line: 8, severity: \"critical\", message: \"Type \\'string\\' is not assignable to type \\'number\\'\" },',\n \" ],\",\n \"});\",\n \"\",\n 'new FunctionStep(checks, \"test\", {',\n \" fn: () => [\",\n ' { rule: \"assertion-failed\", file: \"test/index.test.ts\", line: 23, severity: \"high\", message: \"Expected 5 but got 3\" },',\n ' { rule: \"assertion-failed\", file: \"test/index.test.ts\", line: 45, severity: \"low\", message: \"Expected \\'hello\\' but got \\'world\\'\" },',\n \" ],\",\n \"});\",\n \"\",\n 'new Entry(checks, \"on-push\", { trigger: { kind: \"push\" }, roots: [\"test\"] });',\n \"\",\n \"export default proj;\",\n \"\",\n].join(\"\\n\");\n\n/** Escape HTML special characters to prevent XSS. */\nexport function escapeHtml(text: string): string {\n return text\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\n/** +1/−1 for bracket chars, 0 otherwise. */\nfunction bracketDelta(c: string | undefined): number {\n if (c === \"{\" || c === \"(\" || c === \"[\") return 1;\n if (c === \"}\" || c === \")\" || c === \"]\") return -1;\n return 0;\n}\n\n/** Chars that end an import statement at depth ≤0: `;` (consumed) or a\n * newline after the module-specifier string (left in place). */\nfunction stmtTerminator(\n c: string | undefined,\n depth: number,\n specSeen: boolean,\n): \"past\" | \"at\" | null {\n if (c === \";\" && depth <= 0) return \"past\";\n if (isLineTerminator(c) && depth <= 0 && specSeen) return \"at\";\n return null;\n}\n\n/** End index of the statement starting at `at`: first top-level `;`, or\n * the first newline after the module-specifier string (covers multiline\n * `import {…}\\n from \"x\"` and `import \"x\"` without a semicolon). */\nfunction scanStatementEnd(code: string, at: number): number {\n let i = at;\n let depth = 0;\n let specSeen = false;\n while (i < code.length) {\n const opaque = scanOpaqueEnd(code, i);\n if (opaque.end > i + 1) {\n specSeen = specSeen || opaque.isString;\n i = opaque.end;\n continue;\n }\n const term = stmtTerminator(code[i], depth, specSeen);\n if (term === \"past\") return i + 1;\n if (term === \"at\") return i;\n depth += bracketDelta(code[i]);\n i++;\n }\n return i;\n}\n\n/** Same-line statement tail: horizontal whitespace then an optional `;`.\n * Never eats the newline — the following line is the next statement. */\nfunction scanStmtTail(code: string, at: number): number {\n let i = at;\n while (i < code.length && (code[i] === \" \" || code[i] === \"\\t\")) i++;\n if (code[i] === \";\") i++;\n return i;\n}\n\n/** End index of the `from \"…\"` clause starting after `from`, or `at` when\n * the clause is malformed — let the evaluator complain. */\nfunction scanFromClause(code: string, next: number): number {\n const specAt = skipTrivia(code, next + 4);\n const q = code[specAt];\n if (q === '\"' || q === \"'\") {\n return scanStmtTail(code, scanString(code, specAt));\n }\n return next;\n}\n\n/** End index of `export * [as ns] [from \"…\"]` — the `*` is at `at`. */\nfunction scanExportStar(code: string, at: number): number {\n let i = skipTrivia(code, at + 1);\n if (keywordAt(code, i, \"as\")) {\n i = skipTrivia(code, i + 2);\n i = skipIdent(code, i);\n }\n const next = skipTrivia(code, i);\n if (keywordAt(code, next, \"from\")) return scanFromClause(code, next);\n return scanStmtTail(code, i);\n}\n\n/**\n * End index of `export {…}` / `export * [as ns] [from \"…\"]` starting at\n * the `{` or `*`. A bare list ends at its `;` or newline — unlike an\n * import, a newline after the closing brace must end the statement or\n * the next line would be eaten with it.\n */\nfunction scanExportListEnd(code: string, at: number): number {\n if (code[at] === \"*\") return scanExportStar(code, at);\n const close = matchBrace(code, at);\n const i = close < 0 ? code.length : close + 1;\n const next = skipTrivia(code, i);\n if (keywordAt(code, next, \"from\")) return scanFromClause(code, next);\n return scanStmtTail(code, i);\n}\n\ninterface ScanState {\n i: number;\n stmtStart: boolean;\n exportDefaultDone: boolean;\n /** `block` vs `expr` — separates block braces from object literals, so\n * `{ export: 1 }` is a key, not a statement. A `(` following a\n * control word pushes `{ ctl }`: its `)` precedes a statement, so\n * `if (x) /re/` opens a regex while `(x) / 2` still divides. */\n stack: (\"block\" | \"expr\" | { ctl: string })[];\n /** True when the previous token ended an operand — `/` then divides.\n * Regex literals never update the delimiter stack, so `/[{]/` can't\n * leave an unclosed `{` that keeps `export default` alive to eval. */\n operandEnd: boolean;\n /** A control word (`if`/`for`/`while`/`switch`/`with`/`catch`)\n * awaiting its `(` — survives whitespace and comments, cleared by\n * any other real token. */\n pendingCtl: string | null;\n}\n\nfunction atStmtLevel(st: ScanState): boolean {\n return st.stack.length === 0 || st.stack.at(-1) === \"block\";\n}\n\n/** Copy a string literal or comment verbatim into `out`. */\nfunction copyOpaque(code: string, st: ScanState, out: string[]): boolean {\n const opaque = scanOpaqueEnd(code, st.i);\n if (opaque.end <= st.i + 1) return false;\n out.push(code.slice(st.i, opaque.end));\n if (opaque.isString) {\n st.stmtStart = false;\n st.operandEnd = true;\n st.pendingCtl = null;\n }\n st.i = opaque.end;\n return true;\n}\n\n/** Handle `{}`/`()`/`[]` — pushes onto the block/expr stack and sets the\n * operand context: `}` closing an object literal ends a value, `}`\n * closing a block ends a statement. */\nfunction applyBracket(code: string, st: ScanState, out: string[]): boolean {\n const c = code[st.i];\n if (c === \"{\") {\n // A `{` at statement position opens a block; anywhere else it's an\n // object literal. Inside a block the next token is a statement;\n // inside an object it's a key — `export:` there is not a keyword.\n st.stack.push(st.stmtStart ? \"block\" : \"expr\");\n st.operandEnd = false;\n } else if (c === \"}\") {\n const popped = st.stack.pop();\n st.stmtStart = popped === \"block\";\n st.operandEnd = popped === \"expr\";\n } else if (c === \"(\") {\n st.stack.push(st.pendingCtl !== null ? { ctl: st.pendingCtl } : \"expr\");\n st.stmtStart = false;\n st.operandEnd = false;\n } else if (c === \"[\") {\n st.stack.push(\"expr\");\n st.stmtStart = false;\n st.operandEnd = false;\n } else if (c === \")\") {\n // A control-header `)` precedes a statement — `if (x) /re/` may open\n // a regex and `if (x) {…}` opens a block; a grouping or call `)` ends\n // the operand, so `(x) / 2` divides.\n const popped = st.stack.pop();\n st.stmtStart = typeof popped === \"object\";\n st.operandEnd = !st.stmtStart;\n } else if (c === \"]\") {\n st.stack.pop();\n st.stmtStart = false;\n st.operandEnd = true;\n } else {\n return false;\n }\n st.pendingCtl = null;\n out.push(c ?? \"\");\n st.i++;\n return true;\n}\n\n/** Handle `;`, LineTerminators, and whitespace — statement boundaries at\n * statement level only (a `;` inside `for (;;)` doesn't count). A line\n * terminator never resets operand context: `foo\\n/bar/` divides, per\n * ASI rules. It also keeps a pending control word: `if\\n(x)` still\n * parses as a control paren. */\nfunction applyTerminator(code: string, st: ScanState, out: string[]): boolean {\n const c = code[st.i];\n if (c !== \";\" && !isWsChar(c)) return false;\n if (c === \";\") {\n if (atStmtLevel(st)) st.stmtStart = true;\n st.operandEnd = false;\n st.pendingCtl = null;\n } else if (isLineTerminator(c) && atStmtLevel(st)) {\n st.stmtStart = true;\n }\n out.push(c ?? \"\");\n st.i++;\n return true;\n}\n\n/** Handle `/` — a regex literal when an operand is expected, a division\n * operator when one just ended. Comments never reach here (copyOpaque\n * already consumed them). */\nfunction applySlash(code: string, st: ScanState, out: string[]): boolean {\n if (code[st.i] !== \"/\" || commentAt(code, st.i)) return false;\n st.pendingCtl = null;\n if (st.operandEnd) {\n out.push(\"/\");\n st.i++;\n st.operandEnd = false;\n } else {\n const end = scanRegex(code, st.i);\n out.push(code.slice(st.i, end));\n st.i = end;\n st.operandEnd = true;\n }\n st.stmtStart = false;\n return true;\n}\n\n/** Handle an `import` keyword at statement start. Dynamic `import (…)`\n * and `import.meta` pass through verbatim — whitespace and comments may\n * sit between the keyword and the paren. */\nfunction tryImport(code: string, st: ScanState, out: string[]): boolean {\n if (!st.stmtStart || !keywordAt(code, st.i, \"import\")) return false;\n st.pendingCtl = null;\n const nc = code[skipTrivia(code, st.i + 6)];\n if (nc === \"(\" || nc === \".\") {\n out.push(\"import\");\n st.i += 6;\n st.stmtStart = false;\n st.operandEnd = true;\n return true;\n }\n st.i = scanStatementEnd(code, st.i + 6);\n st.stmtStart = false;\n st.operandEnd = false;\n return true;\n}\n\n/** Handle `export default` (→ `return`), `export {…}`/`export *` (removed)\n * and `export <decl>` (keyword dropped — the eval context is a function\n * body, where `export const` would be a syntax error). */\nfunction tryExport(code: string, st: ScanState, out: string[]): boolean {\n if (!st.stmtStart || !keywordAt(code, st.i, \"export\")) return false;\n st.pendingCtl = null;\n const j = skipTrivia(code, st.i + 6);\n if (\n !st.exportDefaultDone &&\n code.startsWith(\"default\", j) &&\n !isIdentChar(code[j + 7])\n ) {\n // `return` must touch the expression — a newline between `default`\n // and the value would trigger ASI and return undefined.\n out.push(\"return \");\n st.exportDefaultDone = true;\n st.i = skipTrivia(code, j + 7);\n st.stmtStart = false;\n st.operandEnd = false;\n return true;\n }\n if (code[j] === \"{\" || code[j] === \"*\") {\n st.i = scanExportListEnd(code, j);\n st.stmtStart = false;\n st.operandEnd = false;\n return true;\n }\n st.i = j;\n st.stmtStart = false;\n st.operandEnd = false;\n return true;\n}\n\n/** Emit a numeric literal — numbers end operands, so `5. / 2` divides\n * even though a bare `.` would leave `/` looking like a regex opener. */\nfunction tryNumber(code: string, st: ScanState, out: string[]): boolean {\n if (!numberAt(code, st.i)) return false;\n const end = scanNumber(code, st.i);\n out.push(code.slice(st.i, end));\n st.i = end;\n st.operandEnd = true;\n st.pendingCtl = null;\n st.stmtStart = false;\n return true;\n}\n\n/** `.` opens member access — the word after it is a property name, not\n * a keyword, so `obj.return / 2` divides. `.5` is a number taken by\n * tryNumber; `?.` reaches here through its `.`; each `.` of `...`\n * lands here and the leading pair finds no ident to consume. */\nfunction tryDot(code: string, st: ScanState, out: string[]): boolean {\n if (code[st.i] !== \".\") return false;\n let end = st.i + 1;\n const prop = skipTrivia(code, end);\n if (isIdentChar(code[prop]) && !isDigit(code[prop]))\n end = skipIdent(code, prop);\n out.push(code.slice(st.i, end));\n st.i = end;\n st.operandEnd = true;\n st.pendingCtl = null;\n st.stmtStart = false;\n return true;\n}\n\n/** Emit an identifier run — keyword table decides whether `/` after it\n * divides (`foo /x/`) or opens a regex (`return /x/`). `of` is the one\n * contextual exception: inside a `for (` header it introduces the\n * iterated expression — `for (x of /re/)`. */\nfunction tryWord(code: string, st: ScanState, out: string[]): boolean {\n if (!isIdentChar(code[st.i])) return false;\n const wend = skipIdent(code, st.i);\n const word = code.slice(st.i, wend);\n out.push(word);\n const top = st.stack.at(-1);\n st.operandEnd =\n word === \"of\" && typeof top === \"object\" && top.ctl === \"for\"\n ? false\n : operandAfterWord(word);\n // `await` may sit between `for` and its `(` — `for await (x of y)`\n // keeps the control context.\n st.pendingCtl =\n word === \"await\" && st.pendingCtl === \"for\"\n ? \"for\"\n : CONTROL_WORDS.has(word)\n ? word\n : null;\n st.stmtStart = false;\n st.i = wend;\n return true;\n}\n\n/** Char handlers tried in order — first true wins. Any char left over is\n * copied verbatim as a non-operand punctuation token. */\nconst HANDLERS: ((code: string, st: ScanState, out: string[]) => boolean)[] = [\n copyOpaque,\n applyBracket,\n applyTerminator,\n applySlash,\n tryImport,\n tryExport,\n tryNumber,\n tryDot,\n tryWord,\n];\n\nfunction advanceScan(code: string, st: ScanState, out: string[]): void {\n for (const handle of HANDLERS) {\n if (handle(code, st, out)) return;\n }\n const c = code[st.i];\n // `++`/`--` keep operand state either way — postfix ends the operand\n // (`i++ / 2` divides), prefix still expects one.\n if ((c === \"+\" || c === \"-\") && code[st.i + 1] === c) {\n out.push((c ?? \"\") + (c ?? \"\"));\n st.i += 2;\n } else {\n st.operandEnd = false;\n out.push(c ?? \"\");\n st.i++;\n }\n st.stmtStart = false;\n st.pendingCtl = null;\n}\n\n/**\n * Strip import/export statements from user code for eval — a linear\n * single-pass scan, so multiline imports and comments/strings are handled\n * and pathological spacing cannot cause quadratic backtracking.\n *\n * `import …` statements are removed whole. `export default` becomes\n * `return`, `export {…}`/`export *` are removed, and `export` before a\n * declaration is dropped (the eval context is a function body, where\n * `export const` would be a syntax error).\n *\n * Note: TypeScript-specific syntax (type annotations, interfaces, enums)\n * is not stripped — the playground uses Monaco's TypeScript language mode\n * for editing, but evaluation is plain JavaScript. Users should write\n * JS-compatible code or use the `as any` escape hatch sparingly.\n */\nexport function preprocessCode(code: string): string {\n const out: string[] = [];\n const st: ScanState = {\n i: 0,\n stmtStart: true,\n exportDefaultDone: false,\n stack: [],\n operandEnd: false,\n pendingCtl: null,\n };\n while (st.i < code.length) {\n if (code[st.i] === undefined) break;\n advanceScan(code, st, out);\n }\n return out.join(\"\");\n}\n\n/**\n * Evaluate user code and return the Project.\n *\n * SECURITY: This uses `new Function()` to execute user-provided code.\n * This is intentional — the playground is a local development tool where\n * the user writes and runs their own pipeline code. The code runs in the\n * browser's main page context (not a sandboxed iframe or worker) because\n * the playground needs to support synchronous FunctionStep execution and\n * direct access to the pipeline constructs. This is the same trust model\n * as a local REPL or `node -e`.\n */\nexport function evaluateUserCode(code: string): Project {\n const processed = preprocessCode(code);\n // Dynamic code execution is intentional for the playground sandbox.\n // SonarCloud S1523: safe — user code runs in the browser sandbox with the\n // same trust model as a local REPL or `node -e` (see comment above).\n const params = [\n \"Project\",\n \"Pipeline\",\n \"FunctionStep\",\n \"ShellStep\",\n \"Entry\",\n \"push\",\n \"changeRequest\",\n \"manual\",\n \"schedule\",\n processed,\n ];\n // NOSONAR suppresses only issues on the marker's own line — it must\n // trail the `new Function` callee, not sit inside the argument list.\n const fn = new Function(...params); // NOSONAR — intentional dynamic evaluation in sandbox\n const fnArgs = [\n Project,\n Pipeline,\n FunctionStep,\n ShellStep,\n Entry,\n push,\n changeRequest,\n manual,\n schedule,\n ];\n // NOSONAR suppresses only issues on the marker's own line — it must\n // trail the `fn(...)` invocation, not sit inside an argument list.\n const result = fn(...fnArgs); // NOSONAR — intentional dynamic evaluation in sandbox\n if (!(result instanceof Project)) {\n throw new TypeError(\"Code must export a Project instance\");\n }\n return result as Project;\n}\n\n/**\n * Run a pipeline with a timeout. If a step returns a never-resolving\n * promise, the timeout ensures the UI recovers instead of staying\n * stuck in \"Running\" state forever.\n */\nexport async function runPipelineWithTimeout(\n project: Project,\n timeoutMs: number,\n): Promise<Awaited<ReturnType<typeof runPipeline>>> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n runPipeline(project),\n new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error(`Pipeline timed out after ${timeoutMs}ms`)),\n timeoutMs,\n );\n }),\n ]);\n } finally {\n // Without this, every completed run leaves a live timer that rejects\n // into an unhandled rejection timeoutMs later.\n if (timer !== undefined) clearTimeout(timer);\n }\n}\n","// @sverka/playground — mountRunner embed API (Spec 53).\n// Docs pages mount a self-contained runner: code (editable or readonly),\n// a Run button, status line, and a findings iframe. Zero backend — the\n// runner evaluates pipeline code in-page under the same trust model as\n// the full playground (see engine.ts).\n\nimport { generateSarifHtml } from \"@sverka/sarif-viewer-web/html-generator\";\nimport type { PipelineResult } from \"./types.js\";\nimport {\n DEFAULT_CODE,\n escapeHtml,\n evaluateUserCode,\n runPipelineWithTimeout,\n} from \"./engine.js\";\n\nexport interface MountRunnerOptions {\n /** Initial editor source. Defaults to the playground template. */\n readonly code?: string;\n /** Render the source as a fixed listing instead of an editable field. */\n readonly readonly?: boolean;\n /** Run `code` immediately on mount. Defaults to false — embed code may\n * come from a share link or CMS, so mounting never executes it unless\n * the embedder opts in or the user clicks Run. */\n readonly autoRun?: boolean;\n /** Called once per completed run — unless the runner was disposed\n * first; teardown silences callbacks for runs still in flight. */\n readonly onRun?: (result: PipelineResult) => void;\n}\n\nexport interface MountedRunner {\n /** Remove the runner's DOM and listeners. */\n dispose(): void;\n}\n\nconst STYLE = `\n font-family: monospace; font-size: 12px;\n background: #0d1117; color: #c9d1d9;\n border: 1px solid #30363d; border-radius: 6px;\n display: flex; flex-direction: column; overflow: hidden;\n`;\n\nfunction createHeader(doc: Document): {\n header: HTMLElement;\n runBtn: HTMLButtonElement;\n status: HTMLSpanElement;\n} {\n const header = doc.createElement(\"div\");\n header.setAttribute(\n \"style\",\n \"display:flex;align-items:center;gap:0.5rem;padding:0.4rem 0.6rem;background:#161b22;border-bottom:1px solid #30363d;\",\n );\n const runBtn = doc.createElement(\"button\");\n runBtn.textContent = \"Run\";\n runBtn.setAttribute(\n \"style\",\n \"padding:0.2rem 0.8rem;border:1px solid #30363d;border-radius:6px;background:#1f6feb;color:#fff;cursor:pointer;font-size:0.8rem;\",\n );\n const status = doc.createElement(\"span\");\n status.setAttribute(\"style\", \"font-size:0.75rem;color:#8b949e;\");\n status.textContent = \"Ready\";\n header.append(runBtn, status);\n return { header, runBtn, status };\n}\n\nfunction createCodeEl(\n doc: Document,\n code: string,\n readonly: boolean,\n): HTMLTextAreaElement | HTMLElement {\n if (readonly) {\n const pre = doc.createElement(\"pre\");\n pre.setAttribute(\n \"style\",\n \"margin:0;padding:0.75rem;overflow:auto;max-height:16rem;white-space:pre;\",\n );\n pre.textContent = code;\n return pre;\n }\n const textarea = doc.createElement(\"textarea\");\n textarea.value = code;\n textarea.setAttribute(\n \"style\",\n \"flex:0 0 12rem;width:100%;background:#0d1117;color:#c9d1d9;border:none;border-bottom:1px solid #30363d;padding:0.75rem;resize:vertical;outline:none;font:inherit;\",\n );\n return textarea;\n}\n\nfunction createFrame(doc: Document): HTMLIFrameElement {\n const frame = doc.createElement(\"iframe\");\n // Shared findings render via srcdoc — sandboxed onto an opaque origin so a\n // crafted payload can never touch the host page; allow-scripts keeps the\n // report's own viewer interactivity (search/filter/sort) working.\n frame.setAttribute(\"sandbox\", \"allow-scripts\");\n frame.setAttribute(\n \"style\",\n \"flex:1;min-height:10rem;width:100%;border:none;background:#0d1117;\",\n );\n return frame;\n}\n\nconst BODY_DARK =\n '<body style=\"background:#0d1117;font-family:monospace;padding:1rem;';\n\n/** Failure banner HTML — string concat keeps user data flowing through\n * escapeHtml explicitly, never through template interpolation. */\nfunction failureBanner(result: PipelineResult): string {\n const failed = result.steps.filter((s) => s.status === \"failure\");\n const lines =\n failed.map((s) => `${s.stepId}: ${s.error ?? \"failed\"}`).join(\"\\n\") ||\n `${result.steps.length} steps failed`;\n return [\n '<div style=\"background:#3d1d1f;color:#f85149;padding:0.6rem 1rem;font-family:monospace;border-bottom:1px solid #30363d;\">',\n \"<strong>Run failed</strong>\",\n '<pre style=\"margin:0.4rem 0 0;white-space:pre-wrap;\">',\n escapeHtml(lines),\n \"</pre></div>\",\n ].join(\"\");\n}\n\n/** Document shown when the run failed — partial findings still render\n * underneath the banner so finished steps aren't hidden by the error. */\nfunction failureDoc(result: PipelineResult, banner: string): string {\n if (result.findings.length === 0) {\n return (\n \"<!DOCTYPE html><html>\" +\n BODY_DARK +\n 'color:#f85149;\">' +\n banner +\n \"</body></html>\"\n );\n }\n const sarif = generateSarifHtml(result.findings);\n return sarif.includes(\"<body>\")\n ? sarif.replace(\"<body>\", \"<body>\" + banner)\n : banner + sarif;\n}\n\n/** Document shown for a clean run with zero findings. */\nfunction successDoc(result: PipelineResult): string {\n return [\n \"<!DOCTYPE html><html>\",\n BODY_DARK,\n 'color:#3fb950;\">',\n \"<h3>No findings — all checks passed</h3><p>\",\n escapeHtml(String(result.steps.length)),\n \" steps in \",\n escapeHtml(String(result.totalDurationMs)),\n \"ms</p></body></html>\",\n ].join(\"\");\n}\n\n/** Document shown when the runner itself threw (eval or pipeline error). */\nfunction errorDoc(e: unknown): string {\n const msg = e instanceof Error ? e.message : String(e);\n return [\n \"<!DOCTYPE html><html>\",\n BODY_DARK,\n 'color:#f85149;\">',\n \"<h3>Error</h3><pre>\",\n escapeHtml(msg),\n \"</pre></body></html>\",\n ].join(\"\");\n}\n\nfunction statusLine(result: PipelineResult): string {\n if (result.success)\n return `${result.findings.length} findings · ${result.totalDurationMs}ms`;\n const failures = result.steps.filter((s) => s.status === \"failure\").length;\n return `${failures} step(s) failed · ${result.findings.length} findings`;\n}\n\n/** Render a completed run into the findings frame. */\nfunction renderResult(\n result: PipelineResult,\n showFindings: (html: string) => void,\n): void {\n if (!result.success) {\n showFindings(failureDoc(result, failureBanner(result)));\n return;\n }\n if (result.findings.length === 0) {\n showFindings(successDoc(result));\n return;\n }\n showFindings(generateSarifHtml(result.findings));\n}\n\n/**\n * Mount a self-contained runner inside `el`. `dispose()` tears the mount\n * down. With `autoRun: true` the runner executes `code` once on mount;\n * otherwise it waits for the Run button — embeds never auto-execute code\n * the embedder didn't explicitly allow.\n */\nexport function mountRunner(\n el: HTMLElement,\n opts: MountRunnerOptions = {},\n): MountedRunner {\n const doc = el.ownerDocument;\n const code = opts.code ?? DEFAULT_CODE;\n\n const root = doc.createElement(\"div\");\n root.setAttribute(\"style\", STYLE);\n const { header, runBtn, status } = createHeader(doc);\n const codeEl = createCodeEl(doc, code, opts.readonly === true);\n const frame = createFrame(doc);\n root.append(header, codeEl, frame);\n el.appendChild(root);\n\n // Branch on the option, not instanceof — a container owned by another\n // window would produce a cross-realm textarea that fails the check.\n const getCode = (): string =>\n opts.readonly === true ? code : (codeEl as HTMLTextAreaElement).value;\n\n // A run in flight outlives dispose() — the flag suppresses its callback.\n let disposed = false;\n\n const run = async (): Promise<void> => {\n runBtn.disabled = true;\n status.textContent = \"Running...\";\n let result: PipelineResult;\n try {\n const project = evaluateUserCode(getCode());\n result = await runPipelineWithTimeout(project, 30_000);\n renderResult(result, (html) => {\n frame.srcdoc = html;\n });\n status.textContent = statusLine(result);\n } catch (e) {\n frame.srcdoc = errorDoc(e);\n status.textContent = \"Error\";\n runBtn.disabled = false;\n return;\n }\n runBtn.disabled = false;\n // Outside the try — a throwing callback must not corrupt the findings\n // view or get misreported as a runner failure.\n if (!disposed) opts.onRun?.(result);\n };\n\n const onClick = (): void => {\n void run();\n };\n runBtn.addEventListener(\"click\", onClick);\n if (opts.autoRun === true) void run();\n\n return {\n dispose() {\n disposed = true;\n runBtn.removeEventListener(\"click\", onClick);\n root.remove();\n },\n };\n}\n","// @sverka/playground — toSverkaConfig (Spec 53): the \"take it home\" bridge.\n// Converts playground source into a real sverka.config.ts: the authoring\n// surface (Project/Pipeline/Entry/roots) transfers verbatim — only the step\n// kind differs. FunctionStep bodies cannot run as shell commands, so each\n// becomes a ShellStep carrying a placeholder echo the user replaces.\n\nimport { PlaygroundError } from \"./share.js\";\nimport {\n isIdentChar,\n isOpaqueStart,\n isQuote,\n isWsChar,\n keywordAt,\n matchBrace,\n newOperandScan,\n scanOpaqueEnd,\n scanOperand,\n scanString,\n skipIdent,\n type OperandScan,\n} from \"./scanner.js\";\n\n// --- local char helpers (only transpile needs these) ----------------------\n\nfunction isOpenBracket(c: string | undefined): boolean {\n return c === \"{\" || c === \"[\" || c === \"(\";\n}\n\nfunction isCloseBracket(c: string | undefined): boolean {\n return c === \"}\" || c === \"]\" || c === \")\";\n}\n\nfunction skipWs(code: string, i: number): number {\n while (i < code.length && isWsChar(code[i])) i++;\n return i;\n}\n\n// ---------------------------------------------------------------------------\n// Every matcher below is string-, comment-, and regex-literal-aware via the\n// shared scanner — braces inside `/[{]/` or quotes never count as syntax,\n// and all scans are linear (no regexes over user source at all).\n// ---------------------------------------------------------------------------\n\n/** Scan state for the prop scans: the shared operand context plus the\n * bracket depth that scopes keys, commas, and prop values. */\ntype PropScan = OperandScan & { depth: number };\n\n/** One token of an operand-tracking scan — identifiers (with `word`),\n * opaque spans, `++`/`--`, numbers, and regex-vs-division resolve\n * through the shared scanner; whitespace passes through. Null when `i`\n * sits on punctuation the caller's own depth/branch logic owns. */\nfunction propToken(\n s: string,\n i: number,\n st: PropScan,\n): { end: number; word?: string } | null {\n const end = scanOperand(s, i, st);\n if (end === null) return null;\n if (isIdentChar(s[i])) return { end, word: s.slice(i, end) };\n return { end };\n}\n\n/** Apply a bracket/other punctuation char to depth + operand state.\n * `(` records whether a control word opened it — `if (x) /re/` may\n * open a regex after `)` while `(x) / 2` divides. */\nfunction applyPunctuation(c: string | undefined, st: PropScan): void {\n if (isOpenBracket(c)) {\n st.depth++;\n if (c === \"(\") st.parens.push(st.pendingCtl);\n st.pendingCtl = null;\n st.operandEnd = false;\n } else if (isCloseBracket(c)) {\n st.depth--;\n st.pendingCtl = null;\n if (c === \")\") {\n const kind = st.parens.pop();\n st.operandEnd = kind === undefined || kind === null;\n } else {\n st.operandEnd = true;\n }\n } else {\n st.pendingCtl = null;\n st.operandEnd = false;\n }\n}\n\n/**\n * Index of the `:` following a depth-0 `key` in a props object body, or -1.\n * A `dependencies` declared inside `fn` never counts — only top-level keys.\n */\nfunction findPropKey(props: string, key: string): number {\n const st: PropScan = { ...newOperandScan(), depth: 0 };\n let i = 0;\n while (i < props.length) {\n const tok = propToken(props, i, st);\n if (tok === null) {\n applyPunctuation(props[i], st);\n i++;\n continue;\n }\n if (st.depth === 0 && tok.word === key) {\n const hit = skipWs(props, tok.end);\n if (props[hit] === \":\") return hit;\n }\n i = tok.end;\n }\n return -1;\n}\n\n/** True when the comment at `k` is a top-level tail comment — `deps: [] //\n * note` — and therefore not part of the value. Emitting it would comment\n * out the generated `}` after `dependsOn: <value>`. */\nfunction isTailComment(props: string, k: number, after: number): boolean {\n const ahead = skipWs(props, after);\n return ahead >= props.length || props[ahead] === \",\";\n}\n\n/** End index of a prop value starting at `j` — the next top-level comma\n * or the end of the props body, with strings/comments/brackets balanced. */\nfunction propValueEnd(props: string, j: number): number {\n const st: PropScan = { ...newOperandScan(), depth: 0 };\n let k = j;\n while (k < props.length) {\n if (isOpaqueStart(props, k)) {\n const after = scanOpaqueEnd(props, k).end;\n if (st.depth === 0 && isTailComment(props, k, after)) return k;\n // A string ends the operand — `\"a\" / 2` divides; comments pass\n // the operand context through untouched.\n if (isQuote(props[k])) {\n st.operandEnd = true;\n st.pendingCtl = null;\n }\n k = after;\n continue;\n }\n const tok = propToken(props, k, st);\n if (tok !== null) {\n k = tok.end;\n continue;\n }\n if (props[k] === \",\" && st.depth === 0) return k;\n applyPunctuation(props[k], st);\n k++;\n }\n return k;\n}\n\n/**\n * Extract a top-level `key: <value>` span from a props object body.\n * Only depth-0 keys match — a `dependencies` declared inside `fn` never\n * counts. The value runs to the next top-level `,`, with strings,\n * comments, and nested brackets all balanced.\n */\nfunction extractProp(props: string, key: string): string | undefined {\n const colon = findPropKey(props, key);\n if (colon < 0) return undefined;\n const j = skipWs(props, colon + 1);\n return props.slice(j, propValueEnd(props, j)).trim();\n}\n\n/** JS line terminators — whitespace a statement tail must not consume. */\nfunction isLineBreak(c: string | undefined): boolean {\n return c === \"\\n\" || c === \"\\r\" || c === \"\\u2028\" || c === \"\\u2029\";\n}\n\n/** Consume trailing non-line whitespace plus one optional `;` — never a\n * line break. Eating it would fuse the emitted import with the next\n * line (`from \"@sverka/workflow\"const x = …`). */\nfunction stmtEndPos(source: string, i: number): number {\n let end = i;\n while (isWsChar(source[end]) && !isLineBreak(source[end])) end++;\n if (source[end] === \";\") end++;\n return end;\n}\n\n/** Parse one import specifier: `Name` or `Name as alias`. */\nfunction parseImportSpec(spec: string): { orig: string; local: string } {\n const a = skipWs(spec, 0);\n const wend = skipIdent(spec, a);\n const orig = spec.slice(a, wend);\n const asPos = skipWs(spec, wend);\n if (!spec.startsWith(\"as\", asPos) || isIdentChar(spec[asPos + 2])) {\n return { orig, local: orig };\n }\n const b = skipWs(spec, asPos + 2);\n const local = spec.slice(b, skipIdent(spec, b));\n return local === \"\" ? { orig, local: orig } : { orig, local };\n}\n\ninterface MappedImports {\n mapped: string;\n fnStepLocals: string[];\n}\n\n/** Map `{…}` import specifiers; FunctionStep becomes ShellStep (aliases\n * survive: `FunctionStep as Fn` → `ShellStep as Fn`). */\nfunction mapImportSpecifiers(inner: string): MappedImports {\n const mapped: string[] = [];\n const fnStepLocals: string[] = [];\n for (const spec of inner.split(\",\")) {\n const trimmed = spec.trim();\n if (trimmed === \"\") continue;\n const { orig, local } = parseImportSpec(trimmed);\n if (orig === \"FunctionStep\") {\n fnStepLocals.push(local);\n mapped.push(orig === local ? \"ShellStep\" : `ShellStep as ${local}`);\n } else {\n mapped.push(trimmed);\n }\n }\n return { mapped: mapped.join(\", \"), fnStepLocals };\n}\n\ninterface PlaygroundImport {\n stmtEnd: number;\n mapped: string;\n fnStepLocals: string[];\n}\n\n/** Parse `import {…} from \"@sverka/playground\"` at `i`, or null when the\n * statement has another shape/module. Caller guarantees \"import\" at `i`. */\nfunction parsePlaygroundImport(\n source: string,\n i: number,\n): PlaygroundImport | null {\n const j = skipWs(source, i + 6);\n if (source[j] !== \"{\") return null;\n const namesEnd = matchBrace(source, j);\n if (namesEnd < 0) return null;\n const fromKw = skipWs(source, namesEnd + 1);\n if (!keywordAt(source, fromKw, \"from\")) return null;\n const qPos = skipWs(source, fromKw + 4);\n const q = source[qPos];\n if (q !== '\"' && q !== \"'\") return null;\n const specEnd = scanString(source, qPos);\n if (source.slice(qPos + 1, specEnd - 1) !== \"@sverka/playground\") return null;\n const { mapped, fnStepLocals } = mapImportSpecifiers(\n source.slice(j + 1, namesEnd),\n );\n return { stmtEnd: stmtEndPos(source, specEnd), mapped, fnStepLocals };\n}\n\n/**\n * Rewrite `import {…} from \"@sverka/playground\"` statements linearly.\n *\n * Returns the rewritten source plus every local name bound to\n * FunctionStep, so constructor calls under an alias are transpiled too.\n */\nfunction rewritePlaygroundImports(source: string): {\n code: string;\n fnStepNames: string[];\n} {\n const fnStepNames: string[] = [];\n let out = \"\";\n let cursor = 0;\n let i = 0;\n while (i < source.length) {\n const end = scanOpaqueEnd(source, i).end;\n if (end > i + 1) {\n i = end;\n continue;\n }\n if (source.startsWith(\"import\", i) && !isIdentChar(source[i + 6])) {\n const stmt = parsePlaygroundImport(source, i);\n if (stmt !== null) {\n out += source.slice(cursor, i);\n out += `import { ${stmt.mapped} } from \"@sverka/workflow\"`;\n if (source[stmt.stmtEnd - 1] === \";\") out += \";\";\n fnStepNames.push(...stmt.fnStepLocals);\n cursor = stmt.stmtEnd;\n i = stmt.stmtEnd;\n continue;\n }\n }\n i++;\n }\n return { code: out + source.slice(cursor), fnStepNames };\n}\n\n/** Index of `(` after `new <name>` where <name> ∈ names, or null. */\nfunction stepCallParen(\n code: string,\n afterNew: number,\n names: readonly string[],\n): number | null {\n const j = skipWs(code, afterNew);\n for (const name of names) {\n const next = code[j + name.length];\n if (code.startsWith(name, j) && !isIdentChar(next)) {\n const paren = skipWs(code, j + name.length);\n if (code[paren] === \"(\") return paren;\n }\n }\n return null;\n}\n\n/**\n * Locate the next `new <name>(` call where <name> ∈ names. Whole-word\n * scanning makes boundary checks free — `myNew` or `newer` never match.\n * Opaque regions and regex literals are skipped inline, so a\n * `new FunctionStep(` inside a comment, string, or `/new Fn(/` pattern\n * is documentation, not a call.\n */\nfunction findStepCall(\n code: string,\n names: readonly string[],\n from: number,\n): { start: number; paren: number } | null {\n const st: PropScan = { ...newOperandScan(), depth: 0 };\n let i = from;\n while (i < code.length) {\n const tok = propToken(code, i, st);\n if (tok !== null) {\n if (tok.word === \"new\") {\n const paren = stepCallParen(code, tok.end, names);\n if (paren !== null) return { start: i, paren };\n }\n i = tok.end;\n continue;\n }\n applyPunctuation(code[i], st);\n i++;\n }\n return null;\n}\n\n/** End of the scope expression — the first top-level comma after `from`,\n * or -1 when the argument list closes/ends first. */\nfunction scanScopeEnd(code: string, from: number): number {\n const st: PropScan = { ...newOperandScan(), depth: 0 };\n let k = from;\n while (k < code.length) {\n const tok = propToken(code, k, st);\n if (tok !== null) {\n k = tok.end;\n continue;\n }\n const c = code[k];\n if (c === \",\" && st.depth === 0) return k;\n if (isCloseBracket(c) && st.depth === 0) return -1;\n applyPunctuation(c, st);\n k++;\n }\n return -1;\n}\n\ninterface StepArgs {\n scope: string;\n id: string;\n propsOpen: number;\n}\n\n/** Parse `(scope, \"id\", {` after the `(` at `paren`; null when the call\n * shape differs. A template id throws immediately — never half-converts. */\nfunction parseStepArgs(code: string, paren: number): StepArgs | null {\n const scopeStart = skipWs(code, paren + 1);\n const comma = scanScopeEnd(code, scopeStart);\n if (comma < 0) return null;\n const qPos = skipWs(code, comma + 1);\n const q = code[qPos];\n if (q === \"`\")\n throw new PlaygroundError(\n \"TRANSPILE_FAILED\",\n \"cannot parse FunctionStep — template-literal step id\",\n );\n if (q !== '\"' && q !== \"'\") return null;\n const idEnd = scanString(code, qPos);\n const afterId = skipWs(code, idEnd);\n if (code[afterId] !== \",\") return null;\n const propsOpen = skipWs(code, afterId + 1);\n if (code[propsOpen] !== \"{\") return null;\n return {\n scope: code.slice(scopeStart, comma).trim(),\n id: code.slice(qPos + 1, idEnd - 1),\n propsOpen,\n };\n}\n\n/** Emit the ShellStep placeholder replacing one FunctionStep call. */\nfunction shellStepCall(args: StepArgs, dependencies: string | undefined) {\n // The id lands inside shell single quotes — $()/backticks can never\n // expand. `'` is already impossible (the id is a plain string literal).\n return (\n `new ShellStep(${args.scope}, \"${args.id}\", ` +\n `{ command: \"echo 'TODO: port '${args.id}' — replace with the real shell command'\"` +\n (dependencies !== undefined ? \", dependsOn: \" + dependencies : \"\") +\n \" }\"\n );\n}\n\n/** Rewrite every `new Fn(scope, \"id\", {…})` call under the given local\n * names into ShellStep placeholders; `dependencies` → `dependsOn`. */\nfunction rewriteStepCalls(code: string, names: readonly string[]): string {\n let result = \"\";\n let cursor = 0;\n for (;;) {\n const call = findStepCall(code, names, cursor);\n if (call === null) break;\n const args = parseStepArgs(code, call.paren);\n // Fail fast on a shape we don't own: the leftover check throws on it\n // anyway, and continuing past `new` re-scans the nested suffix once\n // per nested `new Fn(` — quadratic on malformed input.\n if (args === null) throw unsupportedStepError(code, call.paren);\n const propsClose = matchBrace(code, args.propsOpen);\n if (propsClose < 0)\n throw new PlaygroundError(\n \"TRANSPILE_FAILED\",\n `cannot parse FunctionStep \"${args.id}\" — unbalanced props object`,\n );\n const dependencies = extractProp(\n code.slice(args.propsOpen + 1, propsClose),\n \"dependencies\",\n );\n result +=\n code.slice(cursor, call.start) + shellStepCall(args, dependencies);\n cursor = propsClose + 1;\n }\n return result + code.slice(cursor);\n}\n\n/** TRANSPILE_FAILED naming the scope of the step call whose `(` is at\n * `paren` — `?` when even the scope won't scan. */\nfunction unsupportedStepError(code: string, paren: number): PlaygroundError {\n const scopeStart = skipWs(code, paren + 1);\n const scopeEnd = scanScopeEnd(code, scopeStart);\n const scope =\n scopeEnd < 0 ? \"?\" : code.slice(scopeStart, scopeEnd).trim() || \"?\";\n return new PlaygroundError(\n \"TRANSPILE_FAILED\",\n `cannot parse FunctionStep with scope '${scope}' — unsupported expression`,\n );\n}\n\n/** A surviving step call means a shape we could not convert — computed\n * scope/id or a malformed alias call. Fail loud rather than emit a\n * half-converted file. */\nfunction assertNoLeftoverSteps(code: string, names: readonly string[]): void {\n const call = findStepCall(code, names, 0);\n if (call !== null) throw unsupportedStepError(code, call.paren);\n}\n\n/**\n * Transpile playground source into `sverka.config.ts`.\n *\n * Rewrites the `@sverka/playground` import to `@sverka/workflow` and each\n * `new FunctionStep(scope, \"id\", { fn, dependencies })` into a ShellStep\n * whose command echoes a placeholder — the function body has no shell\n * equivalent.\n * `dependencies` maps onto `dependsOn`. Aliased FunctionStep imports\n * (`FunctionStep as Fn`) are followed too.\n *\n * Throws `PlaygroundError(TRANSPILE_FAILED)` naming the offending step when\n * a FunctionStep cannot be parsed — a half-converted file is never emitted.\n */\nexport function toSverkaConfig(source: string): string {\n const { code, fnStepNames } = rewritePlaygroundImports(source);\n if (fnStepNames.length === 0) fnStepNames.push(\"FunctionStep\");\n const out = rewriteStepCalls(code, fnStepNames);\n assertNoLeftoverSteps(out, fnStepNames);\n return out;\n}\n","// @sverka/playground — examples gallery (Spec 53).\n// `examples/*/` mini-projects are bundled at build time by the vite entry\n// (app.ts collects them via import.meta.glob) — the picker needs no\n// network access. This module stays bundler-agnostic so the library\n// build (tsdown) never sees vite-only APIs.\n\nexport interface GalleryExample {\n /** Directory name, e.g. \"failing-checks\". */\n readonly id: string;\n /** Humanized label for the picker. */\n readonly title: string;\n /** sverka.config.ts source, bundled into the playground build. */\n readonly code: string;\n}\n\nfunction toTitle(id: string): string {\n return id\n .split(\"-\")\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(\" \");\n}\n\n/** Turn a `{ \"<path>/examples/<id>/sverka.config.ts\": source }` map into a\n * sorted picker list. */\nexport function listExamples(\n modules: Record<string, string>,\n): readonly GalleryExample[] {\n return Object.entries(modules)\n .map(([path, code]) => {\n const id =\n /examples\\/([^/]+)\\/sverka\\.config\\.ts$/.exec(path)?.[1] ?? path;\n return { id, title: toTitle(id), code };\n })\n .sort((a, b) => a.id.localeCompare(b.id));\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sverka/playground",
3
- "version": "0.2.26",
3
+ "version": "0.2.28",
4
4
  "description": "Sverka Playground — browser sandbox for building and running check pipelines",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -37,7 +37,7 @@
37
37
  "typecheck": "tsc --noEmit"
38
38
  },
39
39
  "dependencies": {
40
- "@sverka/sarif-viewer-web": "^0.2.26",
40
+ "@sverka/sarif-viewer-web": "^0.2.28",
41
41
  "fflate": "0.8.3"
42
42
  },
43
43
  "devDependencies": {