@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.
@@ -261,8 +261,8 @@ var require_index_cjs = __commonJS({
261
261
  return new Ok(res.value);
262
262
  })));
263
263
  }
264
- match(ok5, _err) {
265
- return this._promise.then((res) => res.match(ok5, _err));
264
+ match(ok6, _err) {
265
+ return this._promise.then((res) => res.match(ok6, _err));
266
266
  }
267
267
  unwrapOr(t) {
268
268
  return this._promise.then((res) => res.unwrapOr(t));
@@ -301,17 +301,17 @@ var require_index_cjs = __commonJS({
301
301
  function okAsync(value) {
302
302
  return new ResultAsync2(Promise.resolve(new Ok(value)));
303
303
  }
304
- function errAsync2(err5) {
305
- return new ResultAsync2(Promise.resolve(new Err(err5)));
304
+ function errAsync2(err6) {
305
+ return new ResultAsync2(Promise.resolve(new Err(err6)));
306
306
  }
307
307
  var fromPromise = ResultAsync2.fromPromise;
308
308
  var fromSafePromise = ResultAsync2.fromSafePromise;
309
309
  var fromAsyncThrowable = ResultAsync2.fromThrowable;
310
310
  var combineResultList = (resultList) => {
311
- let acc = ok4([]);
311
+ let acc = ok5([]);
312
312
  for (const result of resultList) {
313
313
  if (result.isErr()) {
314
- acc = err4(result.error);
314
+ acc = err5(result.error);
315
315
  break;
316
316
  } else {
317
317
  acc.map((list) => list.push(result.value));
@@ -321,12 +321,12 @@ var require_index_cjs = __commonJS({
321
321
  };
322
322
  var combineResultAsyncList = (asyncResultList) => ResultAsync2.fromSafePromise(Promise.all(asyncResultList)).andThen(combineResultList);
323
323
  var combineResultListWithAllErrors = (resultList) => {
324
- let acc = ok4([]);
324
+ let acc = ok5([]);
325
325
  for (const result of resultList) {
326
326
  if (result.isErr() && acc.isErr()) {
327
327
  acc.error.push(result.error);
328
328
  } else if (result.isErr() && acc.isOk()) {
329
- acc = err4([result.error]);
329
+ acc = err5([result.error]);
330
330
  } else if (result.isOk() && acc.isOk()) {
331
331
  acc.value.push(result.value);
332
332
  }
@@ -340,9 +340,9 @@ var require_index_cjs = __commonJS({
340
340
  return (...args) => {
341
341
  try {
342
342
  const result = fn(...args);
343
- return ok4(result);
343
+ return ok5(result);
344
344
  } catch (e) {
345
- return err4(errorFn ? errorFn(e) : e);
345
+ return err5(errorFn ? errorFn(e) : e);
346
346
  }
347
347
  };
348
348
  }
@@ -356,11 +356,11 @@ var require_index_cjs = __commonJS({
356
356
  }
357
357
  Result.combineWithAllErrors = combineWithAllErrors;
358
358
  })(exports2.Result || (exports2.Result = {}));
359
- function ok4(value) {
359
+ function ok5(value) {
360
360
  return new Ok(value);
361
361
  }
362
- function err4(err5) {
363
- return new Err(err5);
362
+ function err5(err6) {
363
+ return new Err(err6);
364
364
  }
365
365
  function safeTry(body) {
366
366
  const n = body().next();
@@ -380,11 +380,11 @@ var require_index_cjs = __commonJS({
380
380
  return !this.isOk();
381
381
  }
382
382
  map(f) {
383
- return ok4(f(this.value));
383
+ return ok5(f(this.value));
384
384
  }
385
385
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
386
386
  mapErr(_f) {
387
- return ok4(this.value);
387
+ return ok5(this.value);
388
388
  }
389
389
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
390
390
  andThen(f) {
@@ -399,14 +399,14 @@ var require_index_cjs = __commonJS({
399
399
  f(this.value);
400
400
  } catch (e) {
401
401
  }
402
- return ok4(this.value);
402
+ return ok5(this.value);
403
403
  }
404
404
  orTee(_f) {
405
- return ok4(this.value);
405
+ return ok5(this.value);
406
406
  }
407
407
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
408
408
  orElse(_f) {
409
- return ok4(this.value);
409
+ return ok5(this.value);
410
410
  }
411
411
  asyncAndThen(f) {
412
412
  return f(this.value);
@@ -423,8 +423,8 @@ var require_index_cjs = __commonJS({
423
423
  return this.value;
424
424
  }
425
425
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
426
- match(ok5, _err) {
427
- return ok5(this.value);
426
+ match(ok6, _err) {
427
+ return ok6(this.value);
428
428
  }
429
429
  safeUnwrap() {
430
430
  const value = this.value;
@@ -455,27 +455,27 @@ var require_index_cjs = __commonJS({
455
455
  }
456
456
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
457
457
  map(_f) {
458
- return err4(this.error);
458
+ return err5(this.error);
459
459
  }
460
460
  mapErr(f) {
461
- return err4(f(this.error));
461
+ return err5(f(this.error));
462
462
  }
463
463
  andThrough(_f) {
464
- return err4(this.error);
464
+ return err5(this.error);
465
465
  }
466
466
  andTee(_f) {
467
- return err4(this.error);
467
+ return err5(this.error);
468
468
  }
469
469
  orTee(f) {
470
470
  try {
471
471
  f(this.error);
472
472
  } catch (e) {
473
473
  }
474
- return err4(this.error);
474
+ return err5(this.error);
475
475
  }
476
476
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
477
477
  andThen(_f) {
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
  orElse(f) {
@@ -495,13 +495,13 @@ var require_index_cjs = __commonJS({
495
495
  unwrapOr(v) {
496
496
  return v;
497
497
  }
498
- match(_ok, err5) {
499
- return err5(this.error);
498
+ match(_ok, err6) {
499
+ return err6(this.error);
500
500
  }
501
501
  safeUnwrap() {
502
502
  const error48 = this.error;
503
503
  return (function* () {
504
- yield err4(error48);
504
+ yield err5(error48);
505
505
  throw new Error("Do not use this generator out of `safeTry`");
506
506
  })();
507
507
  }
@@ -521,13 +521,13 @@ var require_index_cjs = __commonJS({
521
521
  exports2.Err = Err;
522
522
  exports2.Ok = Ok;
523
523
  exports2.ResultAsync = ResultAsync2;
524
- exports2.err = err4;
524
+ exports2.err = err5;
525
525
  exports2.errAsync = errAsync2;
526
526
  exports2.fromAsyncThrowable = fromAsyncThrowable;
527
527
  exports2.fromPromise = fromPromise;
528
528
  exports2.fromSafePromise = fromSafePromise;
529
529
  exports2.fromThrowable = fromThrowable3;
530
- exports2.ok = ok4;
530
+ exports2.ok = ok5;
531
531
  exports2.okAsync = okAsync;
532
532
  exports2.safeTry = safeTry;
533
533
  }
@@ -5320,8 +5320,8 @@ var require_resolve_flow_scalar = __commonJS({
5320
5320
  };
5321
5321
  function parseCharCode(source, offset, length, onError) {
5322
5322
  const cc = source.substr(offset, length);
5323
- const ok4 = cc.length === length && /^[0-9a-fA-F]+$/.test(cc);
5324
- const code = ok4 ? parseInt(cc, 16) : NaN;
5323
+ const ok5 = cc.length === length && /^[0-9a-fA-F]+$/.test(cc);
5324
+ const code = ok5 ? parseInt(cc, 16) : NaN;
5325
5325
  if (isNaN(code)) {
5326
5326
  const raw = source.substr(offset - 2, length + 2);
5327
5327
  onError(offset - 2, "BAD_DQ_ESCAPE", `Invalid escape sequence ${raw}`);
@@ -21736,7 +21736,7 @@ function redactCause(value) {
21736
21736
 
21737
21737
  // ../../packages/shared/dist/spec.js
21738
21738
  var import_neverthrow3 = __toESM(require_index_cjs(), 1);
21739
- var ID_REGEX = /^[a-z0-9][a-z0-9-]*(\.[a-z0-9-]+)*$/;
21739
+ var ID_REGEX = /^[a-z0-9][a-z0-9-]*(\.[a-z0-9][a-z0-9-]*)*$/;
21740
21740
  var toSpecId = (value) => ID_REGEX.test(value) ? (0, import_neverthrow3.ok)(value) : (0, import_neverthrow3.err)({ type: "CATALOG_INVALID_ID", kind: "spec", id: value });
21741
21741
  var toSuiteId = (value) => ID_REGEX.test(value) ? (0, import_neverthrow3.ok)(value) : (0, import_neverthrow3.err)({ type: "CATALOG_INVALID_ID", kind: "suite", id: value });
21742
21742
 
@@ -21744,8 +21744,88 @@ var toSuiteId = (value) => ID_REGEX.test(value) ? (0, import_neverthrow3.ok)(val
21744
21744
  var CATALOG_INTERFACE_VERSION = 1;
21745
21745
 
21746
21746
  // ../../packages/shared/dist/parse-spec-shared.js
21747
- var import_neverthrow4 = __toESM(require_index_cjs(), 1);
21747
+ var import_neverthrow5 = __toESM(require_index_cjs(), 1);
21748
21748
  var yaml = __toESM(require_dist(), 1);
21749
+
21750
+ // ../../packages/shared/dist/parse-spec-steps.js
21751
+ var import_neverthrow4 = __toESM(require_index_cjs(), 1);
21752
+ var STEP_LINE_PATTERN = /^\d+\.\s+(.*)$/;
21753
+ var UNICODE_ARROW = " \u2192 ";
21754
+ var ASCII_ARROW = " -> ";
21755
+ var OPTIONAL_PREFIX = "(optional) ";
21756
+ var HINT_PATTERN = /\s*\[hint:\s*(?:"([^"]*)"|([^\]]+?))\s*\]\s*$/i;
21757
+ var ASSERTION_BULLET = "- ";
21758
+ function splitHint(text) {
21759
+ const match = HINT_PATTERN.exec(text);
21760
+ if (match === null) {
21761
+ return { text: text.trim() };
21762
+ }
21763
+ const hintValue = (match[1] ?? match[2] ?? "").trim();
21764
+ const stripped = text.slice(0, text.length - match[0].length).trim();
21765
+ if (hintValue === "") {
21766
+ return { text: stripped };
21767
+ }
21768
+ return { text: stripped, hint: hintValue };
21769
+ }
21770
+ function splitOptional(raw) {
21771
+ if (raw.startsWith(OPTIONAL_PREFIX)) {
21772
+ return { body: raw.slice(OPTIONAL_PREFIX.length), isOptional: true };
21773
+ }
21774
+ return { body: raw, isOptional: false };
21775
+ }
21776
+ function findArrowIndex(body) {
21777
+ const unicodeIndex = body.indexOf(UNICODE_ARROW);
21778
+ if (unicodeIndex !== -1) {
21779
+ return unicodeIndex;
21780
+ }
21781
+ return body.indexOf(ASCII_ARROW);
21782
+ }
21783
+ function arrowLength(body, arrowIndex) {
21784
+ return body.startsWith(UNICODE_ARROW, arrowIndex) ? UNICODE_ARROW.length : ASCII_ARROW.length;
21785
+ }
21786
+ function buildStepWithoutOutcome({ body, index, isOptional }) {
21787
+ const { text, hint } = splitHint(body);
21788
+ if (hint === void 0) {
21789
+ return { index, intent: text, isOptional };
21790
+ }
21791
+ return { index, intent: text, hint, isOptional };
21792
+ }
21793
+ function buildStepWithOutcome({ intent, remainder, index, isOptional }) {
21794
+ const { text: outcome, hint } = splitHint(remainder);
21795
+ if (hint === void 0) {
21796
+ return { index, intent, outcome, isOptional };
21797
+ }
21798
+ return { index, intent, outcome, hint, isOptional };
21799
+ }
21800
+ function buildStep(raw, index) {
21801
+ const { body, isOptional } = splitOptional(raw);
21802
+ const arrowIndex = findArrowIndex(body);
21803
+ if (arrowIndex === -1) {
21804
+ return buildStepWithoutOutcome({ body, index, isOptional });
21805
+ }
21806
+ const intent = body.slice(0, arrowIndex).trim();
21807
+ const remainder = body.slice(arrowIndex + arrowLength(body, arrowIndex));
21808
+ return buildStepWithOutcome({ intent, remainder, index, isOptional });
21809
+ }
21810
+ function parseStepLine(line, index) {
21811
+ const match = STEP_LINE_PATTERN.exec(line.trim());
21812
+ return match === null ? void 0 : buildStep(match[1] ?? "", index);
21813
+ }
21814
+ function parseSteps(stepsBody) {
21815
+ const steps = stepsBody.split("\n").map((line, index) => parseStepLine(line, index + 1)).filter((step) => step !== void 0);
21816
+ if (steps.length === 0) {
21817
+ return (0, import_neverthrow4.err)({ type: "EMPTY_STEPS_SECTION" });
21818
+ }
21819
+ return (0, import_neverthrow4.ok)(steps);
21820
+ }
21821
+ function parseAssertions(body) {
21822
+ if (body === void 0 || body.trim() === "") {
21823
+ return [];
21824
+ }
21825
+ return body.split("\n").map((line) => line.trim()).filter((line) => line.startsWith(ASSERTION_BULLET)).map((line) => line.slice(ASSERTION_BULLET.length).trim());
21826
+ }
21827
+
21828
+ // ../../packages/shared/dist/parse-spec-shared.js
21749
21829
  var FRONTMATTER_OPEN = "---\n";
21750
21830
  var FRONTMATTER_CLOSE = "\n---\n";
21751
21831
  var FRONTMATTER_EMPTY_CLOSE = "---\n";
@@ -21756,13 +21836,22 @@ var SECTION_HEADING_PREFIX = "## ";
21756
21836
  var SETUP_HEADING = "## Setup";
21757
21837
  var STEPS_HEADING = "## Steps";
21758
21838
  var ASSERTIONS_HEADING = "## Assertions";
21759
- var STEP_LINE_PATTERN = /^\d+\.\s+(.*)$/;
21760
- var ARROW_SEPARATOR = " \u2192 ";
21761
- var HINT_PATTERN = /\s*\[hint:\s*([^\]]+)\]\s*$/;
21762
- var ASSERTION_BULLET = "- ";
21763
- var safeParseYaml = (0, import_neverthrow4.fromThrowable)((text) => yaml.parse(text), (cause) => ({
21764
- type: "STORED_SPEC_PARSE_FAILED",
21765
- reason: "malformed yaml",
21839
+ var VARIANT_PATTERN = /^##\s+(.+)$/;
21840
+ var URL_PATTERN = /^https?:\/\//;
21841
+ var STATUS_VALUES = ["draft", "active", "deprecated"];
21842
+ var frontmatterSchema = external_exports.object({
21843
+ id: external_exports.string().regex(ID_REGEX, { message: "invalid id slug" }),
21844
+ feature: external_exports.string().min(1).refine((value) => value.trim().length > 0, { message: "feature cannot be blank" }),
21845
+ owner: external_exports.string().optional(),
21846
+ status: external_exports.enum(STATUS_VALUES).default("active"),
21847
+ tags: external_exports.array(external_exports.string()).default([]),
21848
+ requires: external_exports.array(external_exports.string()).default([]),
21849
+ links: external_exports.array(external_exports.string().regex(URL_PATTERN, { message: "must be a URL" })).default([]),
21850
+ scenarioId: external_exports.string().min(1).optional(),
21851
+ timeout: external_exports.number().int().positive().optional()
21852
+ });
21853
+ var safeParseYaml = (0, import_neverthrow5.fromThrowable)((text) => yaml.parse(text), (cause) => ({
21854
+ type: "MALFORMED_FRONTMATTER",
21766
21855
  cause
21767
21856
  }));
21768
21857
  function normalizeRawInput(value) {
@@ -21771,49 +21860,45 @@ function normalizeRawInput(value) {
21771
21860
  }
21772
21861
  function splitFrontmatter(raw) {
21773
21862
  if (!raw.startsWith(FRONTMATTER_OPEN)) {
21774
- return (0, import_neverthrow4.err)({ type: "STORED_SPEC_PARSE_FAILED", reason: "missing frontmatter" });
21863
+ return (0, import_neverthrow5.err)({ type: "MALFORMED_FRONTMATTER", cause: "missing frontmatter delimiter" });
21775
21864
  }
21776
21865
  const afterOpen = raw.slice(FRONTMATTER_OPEN.length);
21777
21866
  if (afterOpen.startsWith(FRONTMATTER_EMPTY_CLOSE)) {
21778
- return (0, import_neverthrow4.ok)({
21867
+ return (0, import_neverthrow5.ok)({
21779
21868
  frontmatterRaw: "",
21780
21869
  body: afterOpen.slice(FRONTMATTER_EMPTY_CLOSE.length)
21781
21870
  });
21782
21871
  }
21783
21872
  const closeIndex = afterOpen.indexOf(FRONTMATTER_CLOSE);
21784
21873
  if (closeIndex === -1) {
21785
- return (0, import_neverthrow4.err)({ type: "STORED_SPEC_PARSE_FAILED", reason: "missing frontmatter" });
21874
+ return (0, import_neverthrow5.err)({ type: "MALFORMED_FRONTMATTER", cause: "missing frontmatter delimiter" });
21786
21875
  }
21787
- return (0, import_neverthrow4.ok)({
21876
+ return (0, import_neverthrow5.ok)({
21788
21877
  frontmatterRaw: afterOpen.slice(0, closeIndex),
21789
21878
  body: afterOpen.slice(closeIndex + FRONTMATTER_CLOSE.length)
21790
21879
  });
21791
21880
  }
21792
21881
  function normalizeFrontmatter(value) {
21793
21882
  if (value === null || value === void 0) {
21794
- return (0, import_neverthrow4.ok)({});
21883
+ return (0, import_neverthrow5.ok)({});
21795
21884
  }
21796
21885
  if (typeof value !== "object" || Array.isArray(value)) {
21797
- return (0, import_neverthrow4.err)({ type: "STORED_SPEC_PARSE_FAILED", reason: "malformed yaml" });
21886
+ return (0, import_neverthrow5.err)({ type: "MALFORMED_FRONTMATTER", cause: "frontmatter must be a mapping" });
21798
21887
  }
21799
- return (0, import_neverthrow4.ok)(value);
21888
+ return (0, import_neverthrow5.ok)(value);
21800
21889
  }
21801
21890
  function parseFrontmatterYaml(yamlText) {
21802
21891
  if (yamlText.trim() === "") {
21803
- return (0, import_neverthrow4.ok)({});
21892
+ return (0, import_neverthrow5.ok)({});
21804
21893
  }
21805
21894
  return safeParseYaml(yamlText).andThen(normalizeFrontmatter);
21806
21895
  }
21807
- function extractRequiredFeature(fm) {
21808
- const value = fm.feature;
21809
- if (typeof value !== "string" || value.trim() === "") {
21810
- return (0, import_neverthrow4.err)({ type: "STORED_SPEC_PARSE_FAILED", reason: "missing feature" });
21896
+ function validateFrontmatter(raw) {
21897
+ const parsed = frontmatterSchema.safeParse(raw);
21898
+ if (!parsed.success) {
21899
+ return (0, import_neverthrow5.err)({ type: "MALFORMED_FRONTMATTER", cause: parsed.error });
21811
21900
  }
21812
- return (0, import_neverthrow4.ok)(value);
21813
- }
21814
- function extractOptionalNumber(fm, key) {
21815
- const value = fm[key];
21816
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
21901
+ return (0, import_neverthrow5.ok)(parsed.data);
21817
21902
  }
21818
21903
  function findHeadingLine(lines, heading) {
21819
21904
  return lines.findIndex((line) => line.trim() === heading);
@@ -21832,81 +21917,55 @@ function extractSection(body, heading) {
21832
21917
  const endIndex = findNextHeadingIndex(lines, afterHeading);
21833
21918
  return lines.slice(afterHeading, endIndex).join("\n").trim();
21834
21919
  }
21920
+ function findCaseVariant(body, expected) {
21921
+ const expectedLower = expected.slice("## ".length).toLowerCase();
21922
+ const lines = body.split("\n");
21923
+ for (const line of lines) {
21924
+ const match = VARIANT_PATTERN.exec(line.trim());
21925
+ const heading = match?.[1];
21926
+ if (heading !== void 0 && heading !== expected.slice("## ".length) && heading.toLowerCase() === expectedLower) {
21927
+ return `## ${heading}`;
21928
+ }
21929
+ }
21930
+ return void 0;
21931
+ }
21835
21932
  function extractSpecSections(body) {
21836
21933
  const setup = extractSection(body, SETUP_HEADING);
21837
21934
  if (setup === void 0) {
21838
- return (0, import_neverthrow4.err)({ type: "STORED_SPEC_PARSE_FAILED", reason: "missing setup section" });
21935
+ const suggestedHeading = findCaseVariant(body, SETUP_HEADING);
21936
+ return (0, import_neverthrow5.err)(suggestedHeading === void 0 ? { type: "MISSING_SETUP_SECTION" } : { type: "MISSING_SETUP_SECTION", suggestedHeading });
21839
21937
  }
21840
21938
  const stepsBody = extractSection(body, STEPS_HEADING);
21841
21939
  if (stepsBody === void 0) {
21842
- return (0, import_neverthrow4.err)({ type: "STORED_SPEC_PARSE_FAILED", reason: "missing steps section" });
21843
- }
21844
- return (0, import_neverthrow4.ok)({ setup, stepsBody, assertionsBody: extractSection(body, ASSERTIONS_HEADING) });
21845
- }
21846
- function splitHint(text) {
21847
- const match = HINT_PATTERN.exec(text);
21848
- if (match === null) {
21849
- return { text: text.trim() };
21850
- }
21851
- const hintValue = match[1]?.trim();
21852
- const stripped = text.slice(0, text.length - match[0].length).trim();
21853
- if (hintValue === void 0 || hintValue === "") {
21854
- return { text: stripped };
21855
- }
21856
- return { text: stripped, hint: hintValue };
21857
- }
21858
- function buildStepWithoutOutcome(body) {
21859
- const { text, hint } = splitHint(body);
21860
- return hint === void 0 ? { intent: text } : { intent: text, hint };
21861
- }
21862
- function buildStepWithOutcome(intent, remainder) {
21863
- const { text: outcome, hint } = splitHint(remainder);
21864
- if (hint === void 0) {
21865
- return { intent, outcome };
21866
- }
21867
- return { intent, outcome, hint };
21868
- }
21869
- function buildStep(body) {
21870
- const arrowIndex = body.indexOf(ARROW_SEPARATOR);
21871
- if (arrowIndex === -1) {
21872
- return buildStepWithoutOutcome(body);
21873
- }
21874
- const intent = body.slice(0, arrowIndex).trim();
21875
- const remainder = body.slice(arrowIndex + ARROW_SEPARATOR.length);
21876
- return buildStepWithOutcome(intent, remainder);
21877
- }
21878
- function parseStepLine(line) {
21879
- const match = STEP_LINE_PATTERN.exec(line.trim());
21880
- return match === null ? void 0 : buildStep(match[1] ?? "");
21881
- }
21882
- function parseSteps(stepsBody) {
21883
- const steps = stepsBody.split("\n").map((line) => parseStepLine(line)).filter((step) => step !== void 0);
21884
- if (steps.length === 0) {
21885
- return (0, import_neverthrow4.err)({ type: "STORED_SPEC_PARSE_FAILED", reason: "empty steps section" });
21886
- }
21887
- return (0, import_neverthrow4.ok)(steps);
21888
- }
21889
- function parseAssertions(body) {
21890
- if (body === void 0 || body.trim() === "") {
21891
- return void 0;
21892
- }
21893
- const items = body.split("\n").map((line) => line.trim()).filter((line) => line.startsWith(ASSERTION_BULLET)).map((line) => line.slice(ASSERTION_BULLET.length).trim());
21894
- return items.length === 0 ? void 0 : items;
21895
- }
21896
- function buildSpec(input) {
21897
- const base = { feature: input.feature, setup: input.setup, steps: input.steps };
21898
- const withAssertions = input.assertions === void 0 ? base : { ...base, assertions: input.assertions };
21899
- return input.timeoutMs === void 0 ? withAssertions : { ...withAssertions, timeoutMs: input.timeoutMs };
21940
+ const suggestedHeading = findCaseVariant(body, STEPS_HEADING);
21941
+ return (0, import_neverthrow5.err)(suggestedHeading === void 0 ? { type: "MISSING_STEPS_SECTION" } : { type: "MISSING_STEPS_SECTION", suggestedHeading });
21942
+ }
21943
+ return (0, import_neverthrow5.ok)({ setup, stepsBody, assertionsBody: extractSection(body, ASSERTIONS_HEADING) });
21944
+ }
21945
+ function buildSpec({ fm, setup, steps, assertions }) {
21946
+ const base = {
21947
+ id: fm.id,
21948
+ feature: fm.feature,
21949
+ status: fm.status,
21950
+ tags: fm.tags,
21951
+ requires: fm.requires,
21952
+ links: fm.links,
21953
+ setup,
21954
+ steps,
21955
+ assertions
21956
+ };
21957
+ const withOwner = fm.owner === void 0 ? base : { ...base, owner: fm.owner };
21958
+ const withScenarioId = fm.scenarioId === void 0 ? withOwner : { ...withOwner, scenarioId: fm.scenarioId };
21959
+ return fm.timeout === void 0 ? withScenarioId : { ...withScenarioId, timeoutSeconds: fm.timeout };
21900
21960
  }
21901
21961
  function parseSpecShared(raw) {
21902
21962
  const normalized = normalizeRawInput(raw);
21903
- return splitFrontmatter(normalized).andThen(({ frontmatterRaw, body }) => parseFrontmatterYaml(frontmatterRaw).andThen((frontmatter) => extractRequiredFeature(frontmatter).andThen((feature) => extractSpecSections(body).andThen((sections) => parseSteps(sections.stepsBody).map((steps) => ({
21963
+ return splitFrontmatter(normalized).andThen(({ frontmatterRaw, body }) => parseFrontmatterYaml(frontmatterRaw).andThen((frontmatter) => validateFrontmatter(frontmatter).andThen((fm) => extractSpecSections(body).andThen((sections) => parseSteps(sections.stepsBody).map((steps) => ({
21904
21964
  spec: buildSpec({
21905
- feature,
21965
+ fm,
21906
21966
  setup: sections.setup,
21907
21967
  steps,
21908
- assertions: parseAssertions(sections.assertionsBody),
21909
- timeoutMs: extractOptionalNumber(frontmatter, "timeout")
21968
+ assertions: parseAssertions(sections.assertionsBody)
21910
21969
  }),
21911
21970
  frontmatter
21912
21971
  }))))));
@@ -21916,7 +21975,7 @@ function parseSpecShared(raw) {
21916
21975
  var yaml2 = __toESM(require_dist(), 1);
21917
21976
 
21918
21977
  // ../../packages/shared/dist/retry.js
21919
- var import_neverthrow5 = __toESM(require_index_cjs(), 1);
21978
+ var import_neverthrow6 = __toESM(require_index_cjs(), 1);
21920
21979
  // Annotate the CommonJS export names for ESM import in node:
21921
21980
  0 && (module.exports = {
21922
21981
  CATALOG_INTERFACE_VERSION,
@@ -73,16 +73,26 @@ export type SuiteId = string & {
73
73
  readonly __brand: "SuiteId";
74
74
  };
75
75
  export interface SpecStep {
76
+ readonly index: number;
76
77
  readonly intent: string;
77
78
  readonly outcome?: string;
78
79
  readonly hint?: string;
80
+ readonly isOptional: boolean;
79
81
  }
82
+ export type SpecStatus = "draft" | "active" | "deprecated";
80
83
  export interface Spec {
84
+ readonly id: SpecId;
81
85
  readonly feature: string;
86
+ readonly owner?: string;
87
+ readonly status: SpecStatus;
88
+ readonly tags: readonly string[];
89
+ readonly requires: readonly string[];
90
+ readonly links: readonly string[];
91
+ readonly scenarioId?: string;
92
+ readonly timeoutSeconds?: number;
82
93
  readonly setup: string;
83
94
  readonly steps: readonly SpecStep[];
84
- readonly assertions?: readonly string[];
85
- readonly timeoutMs?: number;
95
+ readonly assertions: readonly string[];
86
96
  }
87
97
  export interface CatalogSpec {
88
98
  readonly id: SpecId;
@@ -118,14 +128,21 @@ export interface Catalog {
118
128
  getSpecs(): ResultAsync<readonly CatalogSpec[], CatalogError>;
119
129
  getSuites(): ResultAsync<readonly CatalogSuite[], CatalogError>;
120
130
  }
121
- export interface SharedSpecError {
122
- readonly type: "STORED_SPEC_PARSE_FAILED";
123
- readonly reason: string;
124
- readonly cause?: unknown;
125
- }
131
+ export type SpecParseError = {
132
+ type: "MALFORMED_FRONTMATTER";
133
+ cause: unknown;
134
+ } | {
135
+ type: "MISSING_SETUP_SECTION";
136
+ suggestedHeading?: string;
137
+ } | {
138
+ type: "MISSING_STEPS_SECTION";
139
+ suggestedHeading?: string;
140
+ } | {
141
+ type: "EMPTY_STEPS_SECTION";
142
+ };
126
143
  export declare function parseSpecShared(raw: string): Result<{
127
144
  readonly spec: Spec;
128
145
  readonly frontmatter: Record<string, unknown>;
129
- }, SharedSpecError>;
146
+ }, SpecParseError>;
130
147
 
131
148
  export {};