@exodus/xqa 6.0.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -265,8 +265,8 @@ var require_index_cjs = __commonJS({
265
265
  return new Ok(res.value);
266
266
  })));
267
267
  }
268
- match(ok5, _err) {
269
- return this._promise.then((res) => res.match(ok5, _err));
268
+ match(ok6, _err) {
269
+ return this._promise.then((res) => res.match(ok6, _err));
270
270
  }
271
271
  unwrapOr(t) {
272
272
  return this._promise.then((res) => res.unwrapOr(t));
@@ -305,17 +305,17 @@ var require_index_cjs = __commonJS({
305
305
  function okAsync(value) {
306
306
  return new ResultAsync2(Promise.resolve(new Ok(value)));
307
307
  }
308
- function errAsync2(err5) {
309
- return new ResultAsync2(Promise.resolve(new Err(err5)));
308
+ function errAsync2(err6) {
309
+ return new ResultAsync2(Promise.resolve(new Err(err6)));
310
310
  }
311
311
  var fromPromise = ResultAsync2.fromPromise;
312
312
  var fromSafePromise = ResultAsync2.fromSafePromise;
313
313
  var fromAsyncThrowable = ResultAsync2.fromThrowable;
314
314
  var combineResultList = (resultList) => {
315
- let acc = ok4([]);
315
+ let acc = ok5([]);
316
316
  for (const result of resultList) {
317
317
  if (result.isErr()) {
318
- acc = err4(result.error);
318
+ acc = err5(result.error);
319
319
  break;
320
320
  } else {
321
321
  acc.map((list) => list.push(result.value));
@@ -325,12 +325,12 @@ var require_index_cjs = __commonJS({
325
325
  };
326
326
  var combineResultAsyncList = (asyncResultList) => ResultAsync2.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultList);
327
327
  var combineResultListWithAllErrors = (resultList) => {
328
- let acc = ok4([]);
328
+ let acc = ok5([]);
329
329
  for (const result of resultList) {
330
330
  if (result.isErr() && acc.isErr()) {
331
331
  acc.error.push(result.error);
332
332
  } else if (result.isErr() && acc.isOk()) {
333
- acc = err4([result.error]);
333
+ acc = err5([result.error]);
334
334
  } else if (result.isOk() && acc.isOk()) {
335
335
  acc.value.push(result.value);
336
336
  }
@@ -344,9 +344,9 @@ var require_index_cjs = __commonJS({
344
344
  return (...args) => {
345
345
  try {
346
346
  const result = fn(...args);
347
- return ok4(result);
347
+ return ok5(result);
348
348
  } catch (e) {
349
- return err4(errorFn ? errorFn(e) : e);
349
+ return err5(errorFn ? errorFn(e) : e);
350
350
  }
351
351
  };
352
352
  }
@@ -360,11 +360,11 @@ var require_index_cjs = __commonJS({
360
360
  }
361
361
  Result.combineWithAllErrors = combineWithAllErrors;
362
362
  })(exports.Result || (exports.Result = {}));
363
- function ok4(value) {
363
+ function ok5(value) {
364
364
  return new Ok(value);
365
365
  }
366
- function err4(err5) {
367
- return new Err(err5);
366
+ function err5(err6) {
367
+ return new Err(err6);
368
368
  }
369
369
  function safeTry(body) {
370
370
  const n = body().next();
@@ -384,11 +384,11 @@ var require_index_cjs = __commonJS({
384
384
  return !this.isOk();
385
385
  }
386
386
  map(f) {
387
- return ok4(f(this.value));
387
+ return ok5(f(this.value));
388
388
  }
389
389
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
390
390
  mapErr(_f) {
391
- return ok4(this.value);
391
+ return ok5(this.value);
392
392
  }
393
393
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
394
394
  andThen(f) {
@@ -403,14 +403,14 @@ var require_index_cjs = __commonJS({
403
403
  f(this.value);
404
404
  } catch (e) {
405
405
  }
406
- return ok4(this.value);
406
+ return ok5(this.value);
407
407
  }
408
408
  orTee(_f) {
409
- return ok4(this.value);
409
+ return ok5(this.value);
410
410
  }
411
411
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
412
412
  orElse(_f) {
413
- return ok4(this.value);
413
+ return ok5(this.value);
414
414
  }
415
415
  asyncAndThen(f) {
416
416
  return f(this.value);
@@ -427,8 +427,8 @@ var require_index_cjs = __commonJS({
427
427
  return this.value;
428
428
  }
429
429
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
430
- match(ok5, _err) {
431
- return ok5(this.value);
430
+ match(ok6, _err) {
431
+ return ok6(this.value);
432
432
  }
433
433
  safeUnwrap() {
434
434
  const value = this.value;
@@ -459,27 +459,27 @@ var require_index_cjs = __commonJS({
459
459
  }
460
460
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
461
461
  map(_f) {
462
- return err4(this.error);
462
+ return err5(this.error);
463
463
  }
464
464
  mapErr(f) {
465
- return err4(f(this.error));
465
+ return err5(f(this.error));
466
466
  }
467
467
  andThrough(_f) {
468
- return err4(this.error);
468
+ return err5(this.error);
469
469
  }
470
470
  andTee(_f) {
471
- return err4(this.error);
471
+ return err5(this.error);
472
472
  }
473
473
  orTee(f) {
474
474
  try {
475
475
  f(this.error);
476
476
  } catch (e) {
477
477
  }
478
- return err4(this.error);
478
+ return err5(this.error);
479
479
  }
480
480
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
481
481
  andThen(_f) {
482
- return err4(this.error);
482
+ return err5(this.error);
483
483
  }
484
484
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
485
485
  orElse(f) {
@@ -499,13 +499,13 @@ var require_index_cjs = __commonJS({
499
499
  unwrapOr(v) {
500
500
  return v;
501
501
  }
502
- match(_ok, err5) {
503
- return err5(this.error);
502
+ match(_ok, err6) {
503
+ return err6(this.error);
504
504
  }
505
505
  safeUnwrap() {
506
506
  const error48 = this.error;
507
507
  return (function* () {
508
- yield err4(error48);
508
+ yield err5(error48);
509
509
  throw new Error("Do not use this generator out of `safeTry`");
510
510
  })();
511
511
  }
@@ -525,13 +525,13 @@ var require_index_cjs = __commonJS({
525
525
  exports.Err = Err;
526
526
  exports.Ok = Ok;
527
527
  exports.ResultAsync = ResultAsync2;
528
- exports.err = err4;
528
+ exports.err = err5;
529
529
  exports.errAsync = errAsync2;
530
530
  exports.fromAsyncThrowable = fromAsyncThrowable;
531
531
  exports.fromPromise = fromPromise;
532
532
  exports.fromSafePromise = fromSafePromise;
533
533
  exports.fromThrowable = fromThrowable3;
534
- exports.ok = ok4;
534
+ exports.ok = ok5;
535
535
  exports.okAsync = okAsync;
536
536
  exports.safeTry = safeTry;
537
537
  }
@@ -5324,8 +5324,8 @@ var require_resolve_flow_scalar = __commonJS({
5324
5324
  };
5325
5325
  function parseCharCode(source, offset, length, onError) {
5326
5326
  const cc = source.substr(offset, length);
5327
- const ok4 = cc.length === length && /^[0-9a-fA-F]+$/.test(cc);
5328
- const code = ok4 ? parseInt(cc, 16) : NaN;
5327
+ const ok5 = cc.length === length && /^[0-9a-fA-F]+$/.test(cc);
5328
+ const code = ok5 ? parseInt(cc, 16) : NaN;
5329
5329
  if (isNaN(code)) {
5330
5330
  const raw = source.substr(offset - 2, length + 2);
5331
5331
  onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`);
@@ -21729,7 +21729,7 @@ function redactCause(value) {
21729
21729
 
21730
21730
  // ../../packages/shared/dist/spec.js
21731
21731
  var import_neverthrow3 = __toESM(require_index_cjs(), 1);
21732
- var ID_REGEX = /^[a-z0-9][a-z0-9-]*(\.[a-z0-9-]+)*$/;
21732
+ var ID_REGEX = /^[a-z0-9][a-z0-9-]*(\.[a-z0-9][a-z0-9-]*)*$/;
21733
21733
  var toSpecId = (value) => ID_REGEX.test(value) ? (0, import_neverthrow3.ok)(value) : (0, import_neverthrow3.err)({ type: "CATALOG_INVALID_ID", kind: "spec", id: value });
21734
21734
  var toSuiteId = (value) => ID_REGEX.test(value) ? (0, import_neverthrow3.ok)(value) : (0, import_neverthrow3.err)({ type: "CATALOG_INVALID_ID", kind: "suite", id: value });
21735
21735
 
@@ -21737,8 +21737,88 @@ var toSuiteId = (value) => ID_REGEX.test(value) ? (0, import_neverthrow3.ok)(val
21737
21737
  var CATALOG_INTERFACE_VERSION = 1;
21738
21738
 
21739
21739
  // ../../packages/shared/dist/parse-spec-shared.js
21740
- var import_neverthrow4 = __toESM(require_index_cjs(), 1);
21740
+ var import_neverthrow5 = __toESM(require_index_cjs(), 1);
21741
21741
  var yaml = __toESM(require_dist(), 1);
21742
+
21743
+ // ../../packages/shared/dist/parse-spec-steps.js
21744
+ var import_neverthrow4 = __toESM(require_index_cjs(), 1);
21745
+ var STEP_LINE_PATTERN = /^\d+\.\s+(.*)$/;
21746
+ var UNICODE_ARROW = " \u2192 ";
21747
+ var ASCII_ARROW = " -> ";
21748
+ var OPTIONAL_PREFIX = "(optional) ";
21749
+ var HINT_PATTERN = /\s*\[hint:\s*(?:"([^"]*)"|([^\]]+?))\s*\]\s*$/i;
21750
+ var ASSERTION_BULLET = "- ";
21751
+ function splitHint(text) {
21752
+ const match = HINT_PATTERN.exec(text);
21753
+ if (match === null) {
21754
+ return { text: text.trim() };
21755
+ }
21756
+ const hintValue = (match[1] ?? match[2] ?? "").trim();
21757
+ const stripped = text.slice(0, text.length - match[0].length).trim();
21758
+ if (hintValue === "") {
21759
+ return { text: stripped };
21760
+ }
21761
+ return { text: stripped, hint: hintValue };
21762
+ }
21763
+ function splitOptional(raw) {
21764
+ if (raw.startsWith(OPTIONAL_PREFIX)) {
21765
+ return { body: raw.slice(OPTIONAL_PREFIX.length), isOptional: true };
21766
+ }
21767
+ return { body: raw, isOptional: false };
21768
+ }
21769
+ function findArrowIndex(body) {
21770
+ const unicodeIndex = body.indexOf(UNICODE_ARROW);
21771
+ if (unicodeIndex !== -1) {
21772
+ return unicodeIndex;
21773
+ }
21774
+ return body.indexOf(ASCII_ARROW);
21775
+ }
21776
+ function arrowLength(body, arrowIndex) {
21777
+ return body.startsWith(UNICODE_ARROW, arrowIndex) ? UNICODE_ARROW.length : ASCII_ARROW.length;
21778
+ }
21779
+ function buildStepWithoutOutcome({ body, index, isOptional }) {
21780
+ const { text, hint } = splitHint(body);
21781
+ if (hint === void 0) {
21782
+ return { index, intent: text, isOptional };
21783
+ }
21784
+ return { index, intent: text, hint, isOptional };
21785
+ }
21786
+ function buildStepWithOutcome({ intent, remainder, index, isOptional }) {
21787
+ const { text: outcome, hint } = splitHint(remainder);
21788
+ if (hint === void 0) {
21789
+ return { index, intent, outcome, isOptional };
21790
+ }
21791
+ return { index, intent, outcome, hint, isOptional };
21792
+ }
21793
+ function buildStep(raw, index) {
21794
+ const { body, isOptional } = splitOptional(raw);
21795
+ const arrowIndex = findArrowIndex(body);
21796
+ if (arrowIndex === -1) {
21797
+ return buildStepWithoutOutcome({ body, index, isOptional });
21798
+ }
21799
+ const intent = body.slice(0, arrowIndex).trim();
21800
+ const remainder = body.slice(arrowIndex + arrowLength(body, arrowIndex));
21801
+ return buildStepWithOutcome({ intent, remainder, index, isOptional });
21802
+ }
21803
+ function parseStepLine(line, index) {
21804
+ const match = STEP_LINE_PATTERN.exec(line.trim());
21805
+ return match === null ? void 0 : buildStep(match[1] ?? "", index);
21806
+ }
21807
+ function parseSteps(stepsBody) {
21808
+ const steps = stepsBody.split("\n").map((line, index) => parseStepLine(line, index + 1)).filter((step) => step !== void 0);
21809
+ if (steps.length === 0) {
21810
+ return (0, import_neverthrow4.err)({ type: "EMPTY_STEPS_SECTION" });
21811
+ }
21812
+ return (0, import_neverthrow4.ok)(steps);
21813
+ }
21814
+ function parseAssertions(body) {
21815
+ if (body === void 0 || body.trim() === "") {
21816
+ return [];
21817
+ }
21818
+ return body.split("\n").map((line) => line.trim()).filter((line) => line.startsWith(ASSERTION_BULLET)).map((line) => line.slice(ASSERTION_BULLET.length).trim());
21819
+ }
21820
+
21821
+ // ../../packages/shared/dist/parse-spec-shared.js
21742
21822
  var FRONTMATTER_OPEN = "---\n";
21743
21823
  var FRONTMATTER_CLOSE = "\n---\n";
21744
21824
  var FRONTMATTER_EMPTY_CLOSE = "---\n";
@@ -21749,13 +21829,22 @@ var SECTION_HEADING_PREFIX = "## ";
21749
21829
  var SETUP_HEADING = "## Setup";
21750
21830
  var STEPS_HEADING = "## Steps";
21751
21831
  var ASSERTIONS_HEADING = "## Assertions";
21752
- var STEP_LINE_PATTERN = /^\d+\.\s+(.*)$/;
21753
- var ARROW_SEPARATOR = " \u2192 ";
21754
- var HINT_PATTERN = /\s*\[hint:\s*([^\]]+)\]\s*$/;
21755
- var ASSERTION_BULLET = "- ";
21756
- var safeParseYaml = (0, import_neverthrow4.fromThrowable)((text) => yaml.parse(text), (cause) => ({
21757
- type: "STORED_SPEC_PARSE_FAILED",
21758
- reason: "malformed yaml",
21832
+ var VARIANT_PATTERN = /^##\s+(.+)$/;
21833
+ var URL_PATTERN = /^https?:\/\//;
21834
+ var STATUS_VALUES = ["draft", "active", "deprecated"];
21835
+ var frontmatterSchema = external_exports.object({
21836
+ id: external_exports.string().regex(ID_REGEX, { message: "invalid id slug" }),
21837
+ feature: external_exports.string().min(1).refine((value) => value.trim().length > 0, { message: "feature cannot be blank" }),
21838
+ owner: external_exports.string().optional(),
21839
+ status: external_exports.enum(STATUS_VALUES).default("active"),
21840
+ tags: external_exports.array(external_exports.string()).default([]),
21841
+ requires: external_exports.array(external_exports.string()).default([]),
21842
+ links: external_exports.array(external_exports.string().regex(URL_PATTERN, { message: "must be a URL" })).default([]),
21843
+ scenarioId: external_exports.string().min(1).optional(),
21844
+ timeout: external_exports.number().int().positive().optional()
21845
+ });
21846
+ var safeParseYaml = (0, import_neverthrow5.fromThrowable)((text) => yaml.parse(text), (cause) => ({
21847
+ type: "MALFORMED_FRONTMATTER",
21759
21848
  cause
21760
21849
  }));
21761
21850
  function normalizeRawInput(value) {
@@ -21764,49 +21853,45 @@ function normalizeRawInput(value) {
21764
21853
  }
21765
21854
  function splitFrontmatter(raw) {
21766
21855
  if (!raw.startsWith(FRONTMATTER_OPEN)) {
21767
- return (0, import_neverthrow4.err)({ type: "STORED_SPEC_PARSE_FAILED", reason: "missing frontmatter" });
21856
+ return (0, import_neverthrow5.err)({ type: "MALFORMED_FRONTMATTER", cause: "missing frontmatter delimiter" });
21768
21857
  }
21769
21858
  const afterOpen = raw.slice(FRONTMATTER_OPEN.length);
21770
21859
  if (afterOpen.startsWith(FRONTMATTER_EMPTY_CLOSE)) {
21771
- return (0, import_neverthrow4.ok)({
21860
+ return (0, import_neverthrow5.ok)({
21772
21861
  frontmatterRaw: "",
21773
21862
  body: afterOpen.slice(FRONTMATTER_EMPTY_CLOSE.length)
21774
21863
  });
21775
21864
  }
21776
21865
  const closeIndex = afterOpen.indexOf(FRONTMATTER_CLOSE);
21777
21866
  if (closeIndex === -1) {
21778
- return (0, import_neverthrow4.err)({ type: "STORED_SPEC_PARSE_FAILED", reason: "missing frontmatter" });
21867
+ return (0, import_neverthrow5.err)({ type: "MALFORMED_FRONTMATTER", cause: "missing frontmatter delimiter" });
21779
21868
  }
21780
- return (0, import_neverthrow4.ok)({
21869
+ return (0, import_neverthrow5.ok)({
21781
21870
  frontmatterRaw: afterOpen.slice(0, closeIndex),
21782
21871
  body: afterOpen.slice(closeIndex + FRONTMATTER_CLOSE.length)
21783
21872
  });
21784
21873
  }
21785
21874
  function normalizeFrontmatter(value) {
21786
21875
  if (value === null || value === void 0) {
21787
- return (0, import_neverthrow4.ok)({});
21876
+ return (0, import_neverthrow5.ok)({});
21788
21877
  }
21789
21878
  if (typeof value !== "object" || Array.isArray(value)) {
21790
- return (0, import_neverthrow4.err)({ type: "STORED_SPEC_PARSE_FAILED", reason: "malformed yaml" });
21879
+ return (0, import_neverthrow5.err)({ type: "MALFORMED_FRONTMATTER", cause: "frontmatter must be a mapping" });
21791
21880
  }
21792
- return (0, import_neverthrow4.ok)(value);
21881
+ return (0, import_neverthrow5.ok)(value);
21793
21882
  }
21794
21883
  function parseFrontmatterYaml(yamlText) {
21795
21884
  if (yamlText.trim() === "") {
21796
- return (0, import_neverthrow4.ok)({});
21885
+ return (0, import_neverthrow5.ok)({});
21797
21886
  }
21798
21887
  return safeParseYaml(yamlText).andThen(normalizeFrontmatter);
21799
21888
  }
21800
- function extractRequiredFeature(fm) {
21801
- const value = fm.feature;
21802
- if (typeof value !== "string" || value.trim() === "") {
21803
- return (0, import_neverthrow4.err)({ type: "STORED_SPEC_PARSE_FAILED", reason: "missing feature" });
21889
+ function validateFrontmatter(raw) {
21890
+ const parsed = frontmatterSchema.safeParse(raw);
21891
+ if (!parsed.success) {
21892
+ return (0, import_neverthrow5.err)({ type: "MALFORMED_FRONTMATTER", cause: parsed.error });
21804
21893
  }
21805
- return (0, import_neverthrow4.ok)(value);
21806
- }
21807
- function extractOptionalNumber(fm, key) {
21808
- const value = fm[key];
21809
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
21894
+ return (0, import_neverthrow5.ok)(parsed.data);
21810
21895
  }
21811
21896
  function findHeadingLine(lines, heading) {
21812
21897
  return lines.findIndex((line) => line.trim() === heading);
@@ -21825,81 +21910,55 @@ function extractSection(body, heading) {
21825
21910
  const endIndex = findNextHeadingIndex(lines, afterHeading);
21826
21911
  return lines.slice(afterHeading, endIndex).join("\n").trim();
21827
21912
  }
21913
+ function findCaseVariant(body, expected) {
21914
+ const expectedLower = expected.slice("## ".length).toLowerCase();
21915
+ const lines = body.split("\n");
21916
+ for (const line of lines) {
21917
+ const match = VARIANT_PATTERN.exec(line.trim());
21918
+ const heading = match?.[1];
21919
+ if (heading !== void 0 && heading !== expected.slice("## ".length) && heading.toLowerCase() === expectedLower) {
21920
+ return `## ${heading}`;
21921
+ }
21922
+ }
21923
+ return void 0;
21924
+ }
21828
21925
  function extractSpecSections(body) {
21829
21926
  const setup = extractSection(body, SETUP_HEADING);
21830
21927
  if (setup === void 0) {
21831
- return (0, import_neverthrow4.err)({ type: "STORED_SPEC_PARSE_FAILED", reason: "missing setup section" });
21928
+ const suggestedHeading = findCaseVariant(body, SETUP_HEADING);
21929
+ return (0, import_neverthrow5.err)(suggestedHeading === void 0 ? { type: "MISSING_SETUP_SECTION" } : { type: "MISSING_SETUP_SECTION", suggestedHeading });
21832
21930
  }
21833
21931
  const stepsBody = extractSection(body, STEPS_HEADING);
21834
21932
  if (stepsBody === void 0) {
21835
- return (0, import_neverthrow4.err)({ type: "STORED_SPEC_PARSE_FAILED", reason: "missing steps section" });
21836
- }
21837
- return (0, import_neverthrow4.ok)({ setup, stepsBody, assertionsBody: extractSection(body, ASSERTIONS_HEADING) });
21838
- }
21839
- function splitHint(text) {
21840
- const match = HINT_PATTERN.exec(text);
21841
- if (match === null) {
21842
- return { text: text.trim() };
21843
- }
21844
- const hintValue = match[1]?.trim();
21845
- const stripped = text.slice(0, text.length - match[0].length).trim();
21846
- if (hintValue === void 0 || hintValue === "") {
21847
- return { text: stripped };
21848
- }
21849
- return { text: stripped, hint: hintValue };
21850
- }
21851
- function buildStepWithoutOutcome(body) {
21852
- const { text, hint } = splitHint(body);
21853
- return hint === void 0 ? { intent: text } : { intent: text, hint };
21854
- }
21855
- function buildStepWithOutcome(intent, remainder) {
21856
- const { text: outcome, hint } = splitHint(remainder);
21857
- if (hint === void 0) {
21858
- return { intent, outcome };
21859
- }
21860
- return { intent, outcome, hint };
21861
- }
21862
- function buildStep(body) {
21863
- const arrowIndex = body.indexOf(ARROW_SEPARATOR);
21864
- if (arrowIndex === -1) {
21865
- return buildStepWithoutOutcome(body);
21866
- }
21867
- const intent = body.slice(0, arrowIndex).trim();
21868
- const remainder = body.slice(arrowIndex + ARROW_SEPARATOR.length);
21869
- return buildStepWithOutcome(intent, remainder);
21870
- }
21871
- function parseStepLine(line) {
21872
- const match = STEP_LINE_PATTERN.exec(line.trim());
21873
- return match === null ? void 0 : buildStep(match[1] ?? "");
21874
- }
21875
- function parseSteps(stepsBody) {
21876
- const steps = stepsBody.split("\n").map((line) => parseStepLine(line)).filter((step) => step !== void 0);
21877
- if (steps.length === 0) {
21878
- return (0, import_neverthrow4.err)({ type: "STORED_SPEC_PARSE_FAILED", reason: "empty steps section" });
21879
- }
21880
- return (0, import_neverthrow4.ok)(steps);
21881
- }
21882
- function parseAssertions(body) {
21883
- if (body === void 0 || body.trim() === "") {
21884
- return void 0;
21885
- }
21886
- const items = body.split("\n").map((line) => line.trim()).filter((line) => line.startsWith(ASSERTION_BULLET)).map((line) => line.slice(ASSERTION_BULLET.length).trim());
21887
- return items.length === 0 ? void 0 : items;
21888
- }
21889
- function buildSpec(input) {
21890
- const base = { feature: input.feature, setup: input.setup, steps: input.steps };
21891
- const withAssertions = input.assertions === void 0 ? base : { ...base, assertions: input.assertions };
21892
- return input.timeoutMs === void 0 ? withAssertions : { ...withAssertions, timeoutMs: input.timeoutMs };
21933
+ const suggestedHeading = findCaseVariant(body, STEPS_HEADING);
21934
+ return (0, import_neverthrow5.err)(suggestedHeading === void 0 ? { type: "MISSING_STEPS_SECTION" } : { type: "MISSING_STEPS_SECTION", suggestedHeading });
21935
+ }
21936
+ return (0, import_neverthrow5.ok)({ setup, stepsBody, assertionsBody: extractSection(body, ASSERTIONS_HEADING) });
21937
+ }
21938
+ function buildSpec({ fm, setup, steps, assertions }) {
21939
+ const base = {
21940
+ id: fm.id,
21941
+ feature: fm.feature,
21942
+ status: fm.status,
21943
+ tags: fm.tags,
21944
+ requires: fm.requires,
21945
+ links: fm.links,
21946
+ setup,
21947
+ steps,
21948
+ assertions
21949
+ };
21950
+ const withOwner = fm.owner === void 0 ? base : { ...base, owner: fm.owner };
21951
+ const withScenarioId = fm.scenarioId === void 0 ? withOwner : { ...withOwner, scenarioId: fm.scenarioId };
21952
+ return fm.timeout === void 0 ? withScenarioId : { ...withScenarioId, timeoutSeconds: fm.timeout };
21893
21953
  }
21894
21954
  function parseSpecShared(raw) {
21895
21955
  const normalized = normalizeRawInput(raw);
21896
- return splitFrontmatter(normalized).andThen(({ frontmatterRaw, body }) => parseFrontmatterYaml(frontmatterRaw).andThen((frontmatter) => extractRequiredFeature(frontmatter).andThen((feature) => extractSpecSections(body).andThen((sections) => parseSteps(sections.stepsBody).map((steps) => ({
21956
+ return splitFrontmatter(normalized).andThen(({ frontmatterRaw, body }) => parseFrontmatterYaml(frontmatterRaw).andThen((frontmatter) => validateFrontmatter(frontmatter).andThen((fm) => extractSpecSections(body).andThen((sections) => parseSteps(sections.stepsBody).map((steps) => ({
21897
21957
  spec: buildSpec({
21898
- feature,
21958
+ fm,
21899
21959
  setup: sections.setup,
21900
21960
  steps,
21901
- assertions: parseAssertions(sections.assertionsBody),
21902
- timeoutMs: extractOptionalNumber(frontmatter, "timeout")
21961
+ assertions: parseAssertions(sections.assertionsBody)
21903
21962
  }),
21904
21963
  frontmatter
21905
21964
  }))))));
@@ -21909,7 +21968,7 @@ function parseSpecShared(raw) {
21909
21968
  var yaml2 = __toESM(require_dist(), 1);
21910
21969
 
21911
21970
  // ../../packages/shared/dist/retry.js
21912
- var import_neverthrow5 = __toESM(require_index_cjs(), 1);
21971
+ var import_neverthrow6 = __toESM(require_index_cjs(), 1);
21913
21972
  export {
21914
21973
  CATALOG_INTERFACE_VERSION,
21915
21974
  parseSpecShared,