opsx-gherkin 1.0.1 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,28 @@
1
+ ## [2.0.1](https://github.com/aquine-kujaruk/opsx-gherkin/compare/v2.0.0...v2.0.1) (2026-10-21)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **skill:** state that inline step comments are preserved step text ([7e49eae](https://github.com/aquine-kujaruk/opsx-gherkin/commit/7e49eae1fe3beb7049f7817ce84309e216ea1745))
7
+
8
+ # [2.0.0](https://github.com/aquine-kujaruk/opsx-gherkin/compare/v1.0.1...v2.0.0) (2026-10-20)
9
+
10
+
11
+ ### Features
12
+
13
+ * **mdg:** emit table separator rows in canonical output ([af14b3d](https://github.com/aquine-kujaruk/opsx-gherkin/commit/af14b3d910138da7851c425aad97dcc9b1895d74))
14
+ * **skill:** stop teaching verification modalities ([eeaa51a](https://github.com/aquine-kujaruk/opsx-gherkin/commit/eeaa51ad7a3ea86cbf61e5ed8638e06fedaf76d5))
15
+
16
+
17
+ ### BREAKING CHANGES
18
+
19
+ * **mdg:** canonical MDG from opsx-to-mdg now contains a `| --- |` row after every table
20
+ header, so converted .feature.md files with tables change bytes. The interpreted model is
21
+ unchanged and input accepts both forms; no document needs migration. The bundled
22
+ references/examples/account.* files are removed.
23
+
24
+ Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
25
+
1
26
  ## [1.0.1](https://github.com/aquine-kujaruk/opsx-gherkin/compare/v1.0.0...v1.0.1) (2026-09-08)
2
27
 
3
28
 
package/README.md CHANGED
@@ -49,8 +49,9 @@ the compatibility profile produces a diagnostic instead of an inferred mapping.
49
49
  Supported delta operations include ADDED, MODIFIED, REMOVED, and RENAMED; the
50
50
  contract defines their allowed content.
51
51
 
52
- Complete examples: [account access](skills/opsx-gherkin/references/examples/account.feature.md)
53
- and [authentication delta](skills/opsx-gherkin/references/examples/authentication-delta.feature.md).
52
+ Complete examples: [minimal main spec](skills/opsx-gherkin/references/examples/minimal.feature.md)
53
+ and [authentication delta](skills/opsx-gherkin/references/examples/authentication-delta.feature.md);
54
+ the contract's sign-in conversion scenario shows shared setup, Rules, and outlines.
54
55
 
55
56
  ## Validate syntax directly
56
57
 
package/dist/cli.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { c as errorMessage } from "./official-99qBUZpr.mjs";
3
- import { t as runConverterCli } from "./converter-CFtzcfoM.mjs";
3
+ import { t as runConverterCli } from "./converter-BaJKyFVG.mjs";
4
4
  import { readFile } from "node:fs/promises";
5
5
  //#region src/profile-resource.ts
6
6
  const profileUrl = new URL("./skills/opsx-gherkin/references/OPENSPEC_MDG_PROFILE.feature.md", import.meta.resolve("opsx-gherkin/package.json"));
@@ -34,7 +34,7 @@ function pushDescriptionAndSteps(output, node, mdg = false) {
34
34
  for (const step of node.steps) {
35
35
  const keyword = mdg ? titleCase(step.keyword) : `**${step.keyword}**`;
36
36
  output.push(`${mdg ? "*" : "-"} ${keyword} ${step.text}`);
37
- if (step.table) pushTable(output, step.table, false, !mdg);
37
+ if (step.table) pushTable(output, step.table, false);
38
38
  if (step.docString) {
39
39
  output.push("", ` \`\`\`${step.docString.mediaType || ""}`);
40
40
  for (const line of step.docString.content.split("\n")) output.push(` ${line}`);
@@ -713,7 +713,7 @@ function serializeMdgScenario(output, scenario, level) {
713
713
  pushTags(output, examples.tags);
714
714
  output.push(`${"#".repeat(level + 1)} Examples: ${examples.name}`, "");
715
715
  if (examples.description) output.push(examples.description, "");
716
- pushTable(output, examples.rows, true, false);
716
+ pushTable(output, examples.rows);
717
717
  }
718
718
  }
719
719
  //#endregion
@@ -974,4 +974,4 @@ function usage(direction) {
974
974
  //#endregion
975
975
  export { runConverterCli as t };
976
976
 
977
- //# sourceMappingURL=converter-CFtzcfoM.mjs.map
977
+ //# sourceMappingURL=converter-BaJKyFVG.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"converter-BaJKyFVG.mjs","names":[],"sources":["../src/model.ts","../src/constants.ts","../src/syntax.ts","../src/parsers/mdg.ts","../src/parsers/feature.ts","../src/parsers/openspec.ts","../src/renderers/feature.ts","../src/renderers/mdg.ts","../src/renderers/openspec.ts","../src/validation/profile.ts","../src/convert.ts","../src/cli/converter.ts"],"sourcesContent":["import { isDeepStrictEqual } from 'node:util'\nimport { ConversionError } from './errors.js'\n\nexport type DocumentKind = 'main' | 'delta'\nexport type Operation = 'ADDED' | 'MODIFIED' | 'REMOVED' | 'RENAMED'\nexport type Direction = 'mdg-to-opsx' | 'opsx-to-mdg' | 'opsx-to-feature'\nexport interface ConversionOptions {\n sourcePath?: string\n name?: string\n projectRoot?: string\n}\nexport interface StepModel {\n keyword: string\n text: string\n table: string[][] | null\n docString: { mediaType: string; content: string } | null\n}\nexport interface BackgroundModel {\n name: string\n description: string\n steps: StepModel[]\n}\nexport interface ExamplesModel {\n name: string\n description: string\n tags: string[]\n rows: string[][]\n}\nexport interface ScenarioModel extends BackgroundModel {\n kind: 'scenario' | 'outline'\n tags: string[]\n examples: ExamplesModel[]\n}\nexport interface RuleModel {\n name: string\n description: string\n tags: string[]\n operation: Operation | undefined\n rename: { from: string; to: string } | null\n background: BackgroundModel | null\n scenarios: ScenarioModel[]\n}\nexport interface DocumentModel {\n kind: DocumentKind\n name: string\n description: string\n tags: string[]\n background: BackgroundModel | null\n featureScenarios: ScenarioModel[]\n rules: RuleModel[]\n}\n\n/** Rename Rules are structural wrappers; their FROM and TO names carry the identity. */\nexport function normalizedModel(model: DocumentModel): DocumentModel {\n return {\n ...model,\n rules: model.rules.map((rule) => ({\n ...rule,\n name: rule.operation === 'RENAMED' ? 'Rename requirement' : rule.name,\n })),\n }\n}\n\nexport function assertPreserved(source: DocumentModel, destination: DocumentModel): void {\n if (!isDeepStrictEqual(normalizedModel(source), normalizedModel(destination))) {\n throw new ConversionError('Conversion would change the normalized document model', [\n 'The destination was not emitted. Check supported structure, ordering, and literal values.',\n ])\n }\n}\n","import type { Operation } from './model.js'\n\nexport const OPERATION_TAGS = new Map<string, Operation>([\n ['@openspec-added', 'ADDED'],\n ['@openspec-modified', 'MODIFIED'],\n ['@openspec-removed', 'REMOVED'],\n ['@openspec-renamed', 'RENAMED'],\n])\nexport const RESERVED_PREFIXES = ['@openspec-', '@mdg-']\nexport const SYNTHETIC_TAG = '@mdg-feature-scenarios'\nexport const DELTA_TAG = '@openspec-delta'\n","import { basename, dirname } from 'node:path'\nimport { ConversionError } from './errors.js'\nimport type { ConversionOptions, StepModel } from './model.js'\n\nexport interface Heading {\n index: number\n title: string\n}\nexport interface Section {\n start: number\n end: number\n}\nexport type Located = { location: { line: number } }\nexport type LocatedChild =\n | Located\n | { background?: Located; scenario?: Located; rule?: Located; examples?: Located }\n\nexport function pushDescriptionAndSteps(\n output: string[],\n node: { description: string; steps: StepModel[] },\n mdg = false,\n): void {\n if (node.description) output.push(node.description, '')\n for (const step of node.steps) {\n const keyword = mdg ? titleCase(step.keyword) : `**${step.keyword}**`\n output.push(`${mdg ? '*' : '-'} ${keyword} ${step.text}`)\n if (step.table) pushTable(output, step.table, false)\n if (step.docString) {\n output.push('', ` \\`\\`\\`${step.docString.mediaType || ''}`)\n for (const line of step.docString.content.split('\\n')) output.push(` ${line}`)\n output.push(' ```')\n }\n }\n output.push('')\n}\n\nexport function pushTable(\n output: string[],\n rows: string[][],\n blankAfter = true,\n separator = true,\n): void {\n if (!rows?.length) return\n output.push(` | ${rows[0].map(escapeCell).join(' | ')} |`)\n if (separator) output.push(` | ${rows[0].map(() => '---').join(' | ')} |`)\n for (const row of rows.slice(1)) output.push(` | ${row.map(escapeCell).join(' | ')} |`)\n if (blankAfter) output.push('')\n}\n\nexport function parseTable(lines: string[]): string[][] {\n const rows = lines.filter((line) => /^\\s{2,}\\|/.test(line)).map(parseTableRow)\n if (rows.length >= 2 && rows[1].every((cell) => /^:?-+:?$/.test(cell))) rows.splice(1, 1)\n if (!rows.length) throw new ConversionError('Empty table')\n const width = rows[0].length\n if (rows.some((row) => row.length !== width))\n throw new ConversionError('Inconsistent table column count')\n return rows\n}\n\nexport function parseTableRow(line: string): string[] {\n const content = line.trim().replace(/^\\|/, '').replace(/\\|$/, '')\n const cells = []\n let cell = ''\n let escaped = false\n for (const char of content) {\n if (escaped) {\n cell += char === 'n' ? '\\n' : char\n escaped = false\n } else if (char === '\\\\') escaped = true\n else if (char === '|') {\n cells.push(cell.trim())\n cell = ''\n } else cell += char\n }\n cells.push(cell.trim())\n return cells\n}\n\nexport function normalizeLines(text: string): string[] {\n return text\n .replace(/^\\uFEFF/, '')\n .replace(/\\r\\n?/g, '\\n')\n .split('\\n')\n}\n\nexport function fenceMask(lines: string[]): boolean[] {\n const mask = []\n let fence: string | null = null\n for (let index = 0; index < lines.length; index += 1) {\n const match = lines[index].match(/^\\s*(`{3,}|~{3,})/)\n mask[index] = Boolean(fence)\n if (match && !fence) {\n fence = match[1][0]\n mask[index] = true\n } else if (match && fence === match[1][0]) {\n mask[index] = true\n fence = null\n }\n }\n return mask\n}\n\nexport function findHeading(lines: string[], mask: boolean[], level: number): Heading | null {\n for (let index = 0; index < lines.length; index += 1) {\n const match = !mask[index] && lines[index].match(new RegExp(`^#{${level}}\\\\s+(.+?)\\\\s*$`))\n if (match) return { index, title: match[1].trim() }\n }\n return null\n}\n\nexport function headingsInRange(\n lines: string[],\n mask: boolean[],\n level: number,\n start: number,\n end: number,\n): Heading[] {\n const result = []\n for (let index = start; index < end; index += 1) {\n const match = !mask[index] && lines[index].match(new RegExp(`^#{${level}}\\\\s+(.+?)\\\\s*$`))\n if (match) result.push({ index, title: match[1].trim() })\n }\n return result\n}\n\nexport function findSection(lines: string[], mask: boolean[], title: string): Section {\n const section = findSectionOptional(lines, mask, title)\n if (!section) throw new ConversionError(`Missing OpenSpec section: ## ${title}`)\n return section\n}\n\nexport function findSectionOptional(\n lines: string[],\n mask: boolean[],\n title: string,\n): Section | null {\n for (let index = 0; index < lines.length; index += 1) {\n if (!mask[index] && new RegExp(`^##\\\\s+${escapeRegex(title)}\\\\s*$`, 'i').test(lines[index])) {\n let end = lines.length\n for (let next = index + 1; next < lines.length; next += 1) {\n if (!mask[next] && /^##\\s+/.test(lines[next])) {\n end = next\n break\n }\n }\n return { start: index, end }\n }\n }\n return null\n}\n\nexport function sectionContent(lines: string[], mask: boolean[], title: string): string | null {\n const section = findSectionOptional(lines, mask, title)\n return section ? trimBlank(lines.slice(section.start + 1, section.end)).join('\\n') : null\n}\n\nexport function precedingTags(lines: string[], headingIndex: number): string[] {\n let index = headingIndex - 1\n while (index >= 0 && !lines[index].trim()) index -= 1\n const blocks = []\n while (index >= 0 && isTagLine(lines[index])) {\n blocks.unshift(...extractTags(lines[index]))\n index -= 1\n while (index >= 0 && !lines[index].trim()) index -= 1\n }\n return blocks\n}\n\nexport function tagBlockStart(lines: string[], headingIndex: number): number {\n let index = headingIndex - 1\n while (index >= 0 && !lines[index].trim()) index -= 1\n while (index >= 0 && isTagLine(lines[index])) {\n index -= 1\n while (index >= 0 && !lines[index].trim()) index -= 1\n }\n return index + 1\n}\n\nexport function tagStartLine(lines: string[], node: LocatedChild): number {\n return tagBlockStart(lines, nodeLine(node) - 1) + 1\n}\n\nexport function isTagLine(line: string): boolean {\n const matches = [...line.matchAll(/`(@[^`]+)`/g)]\n return matches.length > 0 && line.replace(/`(@[^`]+)`/g, '').trim() === ''\n}\n\nexport function extractTags(line: string): string[] {\n return [...line.matchAll(/`(@[^`]+)`/g)].map((match) => match[1])\n}\n\nexport function proseBetween(lines: string[], startLine: number, endLine: number): string {\n const slice = lines.slice(Math.max(0, startLine - 1), Math.max(0, endLine - 1))\n while (slice.length && (!slice[slice.length - 1].trim() || isTagLine(slice[slice.length - 1])))\n slice.pop()\n return trimBlank(slice).join('\\n')\n}\n\nexport function nextSiblingLine(line: number, siblings: LocatedChild[], fallback: number): number {\n return (\n siblings\n .map(nodeLine)\n .filter((candidate) => candidate > line)\n .sort((a, b) => a - b)[0] || fallback\n )\n}\n\nexport function nodeLine(child: LocatedChild): number {\n if ('location' in child) return child.location.line\n const value = child.background ?? child.scenario ?? child.rule ?? child.examples\n if (!value) throw new ConversionError('Missing Gherkin node location')\n return value.location.line\n}\n\nexport function trimBlank(lines: string[]): string[] {\n const copy = [...lines]\n while (copy.length && !copy[0].trim()) copy.shift()\n while (copy.length && !copy[copy.length - 1].trim()) copy.pop()\n return copy\n}\n\nexport function pushTags(output: string[], tags: string[]): void {\n if (tags?.length) output.push(tags.map((tag) => `\\`${tag}\\``).join(' '))\n}\n\nexport function resolveOpenSpecName(text: string, options: ConversionOptions): string {\n const lines = normalizeLines(text)\n const mask = fenceMask(lines)\n const h1 = findHeading(lines, mask, 1)\n if (h1) return h1.title.replace(/^Feature:\\s*/i, '').trim()\n if (options.name) return options.name\n if (options.sourcePath) {\n const parent = basename(dirname(options.sourcePath))\n if (parent && parent !== '.') return parent\n }\n throw new ConversionError('OpenSpec text without an H1 title requires --name')\n}\n\nexport function slugify(value: string): string {\n return (\n value\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '') || 'spec'\n )\n}\n\nexport function titleCase(keyword: string): string {\n return keyword[0] + keyword.slice(1).toLowerCase()\n}\n\nexport function escapeCell(value: string): string {\n return String(value).replace(/\\\\/g, '\\\\\\\\').replace(/\\|/g, '\\\\|').replace(/\\n/g, '\\\\n')\n}\n\nexport function escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","import type {\n Background,\n Examples,\n GherkinDocument,\n Rule,\n Scenario,\n Step,\n} from '@cucumber/messages'\nimport { DELTA_TAG, OPERATION_TAGS } from '../constants.js'\nimport { ConversionError } from '../errors.js'\nimport type {\n BackgroundModel,\n DocumentModel,\n ExamplesModel,\n Operation,\n RuleModel,\n ScenarioModel,\n StepModel,\n} from '../model.js'\nimport type { LocatedChild } from '../syntax.js'\nimport { nextSiblingLine, nodeLine, normalizeLines, proseBetween, tagStartLine } from '../syntax.js'\n\nexport function parseMdgModel(text: string, document: GherkinDocument): DocumentModel {\n const feature = document.feature\n if (!feature) throw new ConversionError('Gherkin document has no Feature')\n const lines = normalizeLines(text)\n const featureChildren = [...feature.children].sort((a, b) => nodeLine(a) - nodeLine(b))\n const firstFeatureChild = featureChildren[0]\n ? tagStartLine(lines, featureChildren[0])\n : lines.length + 1\n const description = proseBetween(lines, feature.location.line + 1, firstFeatureChild)\n const featureTags = feature.tags.map((tag) => tag.name)\n const isDelta = featureTags.includes(DELTA_TAG)\n const model: DocumentModel = {\n kind: isDelta ? 'delta' : 'main',\n name: feature.name.trim(),\n description,\n tags: featureTags.filter((tag) => tag !== DELTA_TAG),\n background: null,\n featureScenarios: [],\n rules: [],\n }\n\n for (let index = 0; index < featureChildren.length; index += 1) {\n const child = featureChildren[index]\n const boundary = featureChildren[index + 1]\n ? tagStartLine(lines, featureChildren[index + 1])\n : lines.length + 1\n if (child.background)\n model.background = mapMdgBackground(child.background, lines, featureChildren)\n if (child.scenario) model.featureScenarios.push(mapMdgScenario(child.scenario, lines))\n if (child.rule) model.rules.push(mapMdgRule(child.rule, lines, isDelta, boundary))\n }\n return model\n}\n\nexport function mapMdgRule(\n rule: Rule,\n lines: string[],\n isDelta: boolean,\n boundary: number,\n): RuleModel {\n const children = [...rule.children].sort((a, b) => nodeLine(a) - nodeLine(b))\n const firstChild = children[0] ? tagStartLine(lines, children[0]) : lines.length + 1\n const tags = rule.tags.map((tag) => tag.name)\n const operationTags = tags.filter((tag) => OPERATION_TAGS.has(tag))\n let operation: Operation | undefined\n if (isDelta) {\n if (operationTags.length !== 1) {\n throw new ConversionError(\n `Delta Rule \"${rule.name}\" must have exactly one @openspec operation tag`,\n )\n }\n operation = OPERATION_TAGS.get(operationTags[0])\n } else if (operationTags.length) {\n throw new ConversionError(\n `Main-spec Rule \"${rule.name}\" cannot use OpenSpec delta operation tags`,\n )\n }\n const description = proseBetween(\n lines,\n rule.location.line + 1,\n children.length ? firstChild : boundary,\n )\n const mapped: RuleModel = {\n name: rule.name.trim(),\n description,\n tags: tags.filter((tag) => !OPERATION_TAGS.has(tag)),\n operation,\n rename: null,\n background: null,\n scenarios: [],\n }\n for (const child of children) {\n if (child.background) mapped.background = mapMdgBackground(child.background, lines, children)\n if (child.scenario) mapped.scenarios.push(mapMdgScenario(child.scenario, lines))\n }\n if (operation === 'RENAMED') {\n const from = description.match(/^FROM:\\s*`### Requirement:\\s*(.+?)`\\s*$/im)?.[1]\n const to = description.match(/^TO:\\s*`### Requirement:\\s*(.+?)`\\s*$/im)?.[1]\n if (!from || !to)\n throw new ConversionError(`RENAMED Rule \"${rule.name}\" requires FROM and TO lines`)\n const extra = description\n .replace(/^FROM:.*$/gim, '')\n .replace(/^TO:.*$/gim, '')\n .trim()\n if (extra || mapped.background || mapped.scenarios.length)\n throw new ConversionError('RENAMED Rule contains unsupported content')\n mapped.rename = { from: from.trim(), to: to.trim() }\n mapped.description = ''\n }\n return mapped\n}\n\nexport function mapMdgBackground(\n background: Background,\n lines: string[],\n siblings: LocatedChild[],\n): BackgroundModel {\n const firstStep =\n background.steps[0]?.location.line ||\n nextSiblingLine(background.location.line, siblings, lines.length + 1)\n return {\n name: background.name.trim(),\n description: proseBetween(lines, background.location.line + 1, firstStep),\n steps: background.steps.map(mapMdgStep),\n }\n}\n\nexport function mapMdgScenario(scenario: Scenario, lines: string[]): ScenarioModel {\n const firstContent = Math.min(\n scenario.steps[0]?.location.line || Infinity,\n scenario.examples[0] ? tagStartLine(lines, { examples: scenario.examples[0] }) : Infinity,\n )\n return {\n kind: /outline/i.test(scenario.keyword) ? 'outline' : 'scenario',\n name: scenario.name.trim(),\n description: proseBetween(\n lines,\n scenario.location.line + 1,\n Number.isFinite(firstContent) ? firstContent : lines.length + 1,\n ),\n tags: scenario.tags.map((tag) => tag.name),\n steps: scenario.steps.map(mapMdgStep),\n examples: scenario.examples.map((examples) => mapMdgExamples(examples, lines)),\n }\n}\n\nexport function mapMdgExamples(examples: Examples, lines: string[]): ExamplesModel {\n const tableLine = examples.tableHeader?.location.line || lines.length + 1\n const rows = []\n if (examples.tableHeader) rows.push(examples.tableHeader.cells.map((cell) => cell.value))\n rows.push(...examples.tableBody.map((row) => row.cells.map((cell) => cell.value)))\n return {\n name: examples.name.trim(),\n description: proseBetween(lines, examples.location.line + 1, tableLine),\n tags: examples.tags.map((tag) => tag.name),\n rows,\n }\n}\n\nexport function mapMdgStep(step: Step): StepModel {\n return {\n keyword: step.keyword.trim().toUpperCase(),\n text: step.text,\n table: step.dataTable\n ? step.dataTable.rows.map((row) => row.cells.map((cell) => cell.value))\n : null,\n docString: step.docString\n ? { mediaType: step.docString.mediaType || '', content: step.docString.content }\n : null,\n }\n}\n","import type { GherkinDocument, Rule } from '@cucumber/messages'\nimport { DELTA_TAG, OPERATION_TAGS } from '../constants.js'\nimport { ConversionError } from '../errors.js'\nimport type { BackgroundModel, DocumentModel, RuleModel, ScenarioModel } from '../model.js'\nimport { trimBlank } from '../syntax.js'\nimport { mapMdgStep } from './mdg.js'\n\ntype Background = NonNullable<Rule['children'][number]['background']>\ntype Scenario = NonNullable<Rule['children'][number]['scenario']>\n\nfunction description(value: { description: string; location: { column?: number } }): string {\n const prefix = ' '.repeat((value.location.column ?? 1) + 1)\n return trimBlank(\n value.description\n .split('\\n')\n .map((line) => (line.startsWith(prefix) ? line.slice(prefix.length) : line)),\n ).join('\\n')\n}\n\nfunction background(value: Background): BackgroundModel {\n return {\n name: value.name.trim(),\n description: description(value),\n steps: value.steps.map(mapMdgStep),\n }\n}\nfunction scenario(value: Scenario): ScenarioModel {\n return {\n ...background(value),\n kind: /outline/i.test(value.keyword) ? 'outline' : 'scenario',\n tags: value.tags.map((tag) => tag.name),\n examples: value.examples.map((group) => ({\n name: group.name.trim(),\n description: description(group),\n tags: group.tags.map((tag) => tag.name),\n rows: [\n ...(group.tableHeader ? [group.tableHeader.cells.map((cell) => cell.value)] : []),\n ...group.tableBody.map((row) => row.cells.map((cell) => cell.value)),\n ],\n })),\n }\n}\nexport function parseFeatureModel(document: GherkinDocument): DocumentModel {\n const feature = document.feature\n if (!feature) throw new ConversionError('Gherkin document has no Feature')\n const tags = feature.tags.map((tag) => tag.name)\n const model: DocumentModel = {\n name: feature.name.trim(),\n description: description(feature),\n kind: tags.includes(DELTA_TAG) ? 'delta' : 'main',\n tags: tags.filter((tag) => tag !== DELTA_TAG),\n background: null,\n featureScenarios: [],\n rules: [],\n }\n for (const child of feature.children) {\n if (child.background) model.background = background(child.background)\n if (child.scenario) model.featureScenarios.push(scenario(child.scenario))\n if (child.rule) {\n const value = child.rule\n const tags = value.tags.map((tag) => tag.name)\n const operationTag = tags.find((tag) => OPERATION_TAGS.has(tag))\n const rule: RuleModel = {\n name: value.name.trim(),\n description: description(value),\n tags: tags.filter((tag) => !OPERATION_TAGS.has(tag)),\n operation: operationTag ? OPERATION_TAGS.get(operationTag) : undefined,\n rename: null,\n background: null,\n scenarios: [],\n }\n for (const item of value.children) {\n if (item.background) rule.background = background(item.background)\n if (item.scenario) rule.scenarios.push(scenario(item.scenario))\n }\n if (rule.operation === 'RENAMED') {\n const from = rule.description.match(/^FROM:\\s*### Requirement:\\s*(.+)$/m)?.[1]\n const to = rule.description.match(/^TO:\\s*### Requirement:\\s*(.+)$/m)?.[1]\n if (!from || !to) throw new ConversionError('RENAMED Rule requires FROM and TO')\n rule.rename = { from: from.trim(), to: to.trim() }\n rule.description = ''\n }\n model.rules.push(rule)\n }\n }\n return model\n}\n","import { DELTA_TAG, SYNTHETIC_TAG } from '../constants.js'\nimport { ConversionError } from '../errors.js'\nimport type {\n BackgroundModel,\n DocumentKind,\n DocumentModel,\n Operation,\n RuleModel,\n ScenarioModel,\n StepModel,\n} from '../model.js'\nimport type { Heading } from '../syntax.js'\nimport {\n fenceMask,\n findHeading,\n findSection,\n findSectionOptional,\n headingsInRange,\n normalizeLines,\n parseTable,\n precedingTags,\n sectionContent,\n tagBlockStart,\n trimBlank,\n} from '../syntax.js'\n\nexport function detectOpenSpecKind(text: string): DocumentKind {\n const lines = text\n .replace(/^\\uFEFF/, '')\n .replace(/\\r\\n?/g, '\\n')\n .split('\\n')\n const mask = fenceMask(lines)\n const main = lines.some((line, index) => !mask[index] && /^##\\s+Requirements\\s*$/i.test(line))\n const delta = lines.some(\n (line, index) =>\n !mask[index] && /^##\\s+(ADDED|MODIFIED|REMOVED|RENAMED) Requirements\\s*$/i.test(line),\n )\n if (main && delta)\n throw new ConversionError('OpenSpec input mixes main and delta requirement sections')\n if (!main && !delta)\n throw new ConversionError('OpenSpec input has no Requirements or delta sections')\n return delta ? 'delta' : 'main'\n}\n\nexport function parseOpenSpecModel(\n text: string,\n { kind, name }: { kind: DocumentKind; name: string },\n): DocumentModel {\n const lines = normalizeLines(text)\n const mask = fenceMask(lines)\n const h1 = findHeading(lines, mask, 1)\n const title = h1 ? h1.title : name\n const tags = h1 ? precedingTags(lines, h1.index) : []\n const purpose = sectionContent(lines, mask, 'Purpose')\n const model: DocumentModel = {\n kind,\n name: title,\n description: purpose?.trim() || '',\n tags: tags.filter((tag) => tag !== DELTA_TAG),\n background: null,\n featureScenarios: [],\n rules: [],\n }\n\n if (kind === 'main') parseMainOpenSpec(lines, mask, model)\n else parseDeltaOpenSpec(lines, mask, model)\n return model\n}\n\nexport function parseMainOpenSpec(lines: string[], mask: boolean[], model: DocumentModel): void {\n const section = findSection(lines, mask, 'Requirements')\n const requirements = headingsInRange(lines, mask, 3, section.start + 1, section.end).filter(\n (heading) => /^Requirement:\\s*/i.test(heading.title),\n )\n const firstRequirementStart = requirements[0]\n ? tagBlockStart(lines, requirements[0].index)\n : section.end\n const preamble = trimBlank(lines.slice(section.start + 1, firstRequirementStart))\n if (preamble.length) model.background = parseOpenSpecBackground(preamble, 'feature')\n\n for (let index = 0; index < requirements.length; index += 1) {\n const heading = requirements[index]\n const end =\n index + 1 < requirements.length\n ? tagBlockStart(lines, requirements[index + 1].index)\n : section.end\n const rule = parseOpenSpecRequirement(lines, mask, heading, end)\n if (rule.tags.includes(SYNTHETIC_TAG)) {\n rule.tags = rule.tags.filter((tag) => tag !== SYNTHETIC_TAG)\n if (rule.background && !model.background) model.background = rule.background\n model.featureScenarios.push(...rule.scenarios)\n } else {\n model.rules.push(rule)\n }\n }\n}\n\nexport function parseDeltaOpenSpec(lines: string[], mask: boolean[], model: DocumentModel): void {\n model.tags = model.tags.filter((tag) => tag !== DELTA_TAG)\n const operations = headingsInRange(lines, mask, 2, 0, lines.length).flatMap((heading) => {\n const match = heading.title.match(/^(ADDED|MODIFIED|REMOVED|RENAMED) Requirements$/i)\n return match ? [match[1].toUpperCase() as Operation] : []\n })\n if (new Set(operations).size !== operations.length)\n throw new ConversionError('Repeated delta operation section is unsupported')\n for (const operation of operations) {\n const section = findSectionOptional(lines, mask, `${operation} Requirements`)\n if (!section) continue\n if (operation === 'RENAMED') {\n for (let index = section.start + 1; index < section.end; index += 1) {\n const from = lines[index].match(/^\\s*-?\\s*FROM:\\s*`?### Requirement:\\s*(.+?)`?\\s*$/i)\n if (!from) continue\n let toIndex = index + 1\n while (toIndex < section.end && !lines[toIndex].trim()) toIndex += 1\n const to = lines[toIndex]?.match(/^\\s*-?\\s*TO:\\s*`?### Requirement:\\s*(.+?)`?\\s*$/i)\n if (!to) throw new ConversionError(`RENAMED entry for \"${from[1].trim()}\" has no TO line`)\n model.rules.push({\n name: 'Rename requirement',\n description: '',\n tags: precedingTags(lines, index),\n operation,\n rename: { from: from[1].trim(), to: to[1].trim() },\n background: null,\n scenarios: [],\n })\n index = toIndex\n }\n continue\n }\n const headings = headingsInRange(lines, mask, 3, section.start + 1, section.end).filter(\n (heading) => /^Requirement:\\s*/i.test(heading.title),\n )\n for (let index = 0; index < headings.length; index += 1) {\n const heading = headings[index]\n const end =\n index + 1 < headings.length ? tagBlockStart(lines, headings[index + 1].index) : section.end\n const rule = parseOpenSpecRequirement(lines, mask, heading, end)\n rule.operation = operation\n model.rules.push(rule)\n }\n }\n}\n\nexport function parseOpenSpecRequirement(\n lines: string[],\n mask: boolean[],\n heading: Heading,\n end: number,\n): RuleModel {\n const childHeadings = headingsInRange(lines, mask, 4, heading.index + 1, end).filter((item) =>\n /^(Scenario(?: Outline)?|Background):/i.test(item.title),\n )\n const firstChild = childHeadings[0] ? tagBlockStart(lines, childHeadings[0].index) : end\n const headerBody = trimBlank(lines.slice(heading.index + 1, firstChild))\n let background = null\n let descriptionLines = headerBody\n const marker = headerBody.findIndex((line) => /^\\*\\*Background:/i.test(line))\n if (marker >= 0) {\n descriptionLines = trimBlank(headerBody.slice(0, marker))\n background = parseOpenSpecBackground(headerBody.slice(marker), 'rule')\n }\n const rule: RuleModel = {\n name: heading.title.replace(/^Requirement:\\s*/i, '').trim(),\n description: descriptionLines.join('\\n').trim(),\n tags: precedingTags(lines, heading.index),\n operation: undefined,\n rename: null,\n background,\n scenarios: [],\n }\n for (let index = 0; index < childHeadings.length; index += 1) {\n const child = childHeadings[index]\n const childEnd =\n index + 1 < childHeadings.length ? tagBlockStart(lines, childHeadings[index + 1].index) : end\n if (/^Background:/i.test(child.title))\n rule.background = parseOpenSpecScenario(lines, mask, child, childEnd, true)\n else\n rule.scenarios.push(\n parseOpenSpecScenario(lines, mask, child, childEnd, false) as ScenarioModel,\n )\n }\n return rule\n}\n\nexport function parseOpenSpecBackground(lines: string[], scope: string): BackgroundModel {\n const marker = lines[0]?.match(/^\\*\\*Background:(.*?)\\*\\*\\s*$/i)\n if (!marker)\n throw new ConversionError(\n `Unsupported ${scope} Requirements preamble; expected **Background:**`,\n )\n const parsed = parseOpenSpecBody(lines.slice(1))\n return { name: marker[1].trim(), description: parsed.description, steps: parsed.steps }\n}\n\nexport function parseOpenSpecScenario(\n lines: string[],\n mask: boolean[],\n heading: Heading,\n end: number,\n background: boolean,\n): ScenarioModel | BackgroundModel {\n const examplesHeadings = headingsInRange(lines, mask, 5, heading.index + 1, end).filter((item) =>\n /^Examples:/i.test(item.title),\n )\n const bodyEnd = examplesHeadings[0] ? tagBlockStart(lines, examplesHeadings[0].index) : end\n const body = parseOpenSpecBody(lines.slice(heading.index + 1, bodyEnd))\n if (background)\n return {\n name: heading.title.replace(/^Background:\\s*/i, '').trim(),\n description: body.description,\n steps: body.steps,\n }\n const scenario: ScenarioModel = {\n kind: /^Scenario Outline:/i.test(heading.title) ? 'outline' : 'scenario',\n name: heading.title.replace(/^Scenario(?: Outline)?:\\s*/i, '').trim(),\n description: body.description,\n tags: precedingTags(lines, heading.index),\n steps: body.steps,\n examples: [],\n }\n for (let index = 0; index < examplesHeadings.length; index += 1) {\n const example = examplesHeadings[index]\n const exampleEnd =\n index + 1 < examplesHeadings.length\n ? tagBlockStart(lines, examplesHeadings[index + 1].index)\n : end\n const exampleLines = trimBlank(lines.slice(example.index + 1, exampleEnd))\n const tableIndex = exampleLines.findIndex((line) => /^\\s{2,}\\|/.test(line))\n if (tableIndex < 0) throw new ConversionError(`Examples \"${example.title}\" has no table`)\n scenario.examples.push({\n name: example.title.replace(/^Examples:\\s*/i, '').trim(),\n description: trimBlank(exampleLines.slice(0, tableIndex)).join('\\n'),\n tags: precedingTags(lines, example.index),\n rows: parseTable(exampleLines.slice(tableIndex)),\n })\n }\n return scenario\n}\n\nexport function parseOpenSpecBody(rawLines: string[]): { description: string; steps: StepModel[] } {\n const lines = trimBlank(rawLines)\n const firstStep = lines.findIndex((line) =>\n /^\\s*[-*+]\\s+\\*\\*(GIVEN|WHEN|THEN|AND|BUT)\\*\\*/i.test(line),\n )\n const description =\n firstStep < 0 ? lines.join('\\n').trim() : trimBlank(lines.slice(0, firstStep)).join('\\n')\n const steps: StepModel[] = []\n if (firstStep < 0) return { description, steps }\n let index = firstStep\n while (index < lines.length) {\n if (!lines[index].trim()) {\n index += 1\n continue\n }\n const match = lines[index].match(/^\\s*[-*+]\\s+\\*\\*(GIVEN|WHEN|THEN|AND|BUT)\\*\\*\\s+(.+)$/i)\n if (!match) throw new ConversionError(`Unsupported OpenSpec scenario line: ${lines[index]}`)\n const step: StepModel = {\n keyword: match[1].toUpperCase(),\n text: match[2],\n table: null,\n docString: null,\n }\n index += 1\n while (index < lines.length && !lines[index].trim()) index += 1\n if (index < lines.length && /^\\s*```/.test(lines[index])) {\n const opener = /^(\\s*)```(.*)$/.exec(lines[index])\n if (!opener) throw new ConversionError('Invalid DocString opener')\n const indent = opener[1].length\n const content = []\n index += 1\n while (index < lines.length && !/^\\s*```\\s*$/.test(lines[index])) {\n content.push(lines[index].slice(Math.min(indent, lines[index].length)))\n index += 1\n }\n if (index >= lines.length) throw new ConversionError('Unclosed OpenSpec DocString')\n index += 1\n step.docString = { mediaType: opener[2].trim(), content: content.join('\\n') }\n } else if (index < lines.length && /^\\s{2,}\\|/.test(lines[index])) {\n const tableLines = []\n while (index < lines.length && /^\\s{2,}\\|/.test(lines[index])) tableLines.push(lines[index++])\n step.table = parseTable(tableLines)\n }\n steps.push(step)\n }\n return { description, steps }\n}\n","import { DELTA_TAG, OPERATION_TAGS } from '../constants.js'\nimport type {\n BackgroundModel,\n DocumentModel,\n RuleModel,\n ScenarioModel,\n StepModel,\n} from '../model.js'\nimport { escapeCell, titleCase, trimBlank } from '../syntax.js'\n\nexport function serializeFeature(model: DocumentModel): string {\n const output: string[] = []\n const featureTags = model.kind === 'delta' ? [...model.tags, DELTA_TAG] : model.tags\n pushFeatureTags(output, featureTags, 0)\n output.push(`Feature: ${model.name}`)\n pushFeatureDescription(output, model.description, 2)\n if (model.background) serializeFeatureBackground(output, model.background, 2)\n for (const scenario of model.featureScenarios) serializeFeatureScenario(output, scenario, 2)\n for (const rule of model.rules) serializeFeatureRule(output, rule)\n return `${trimBlank(output).join('\\n')}\\n`\n}\n\nexport function serializeFeatureRule(output: string[], rule: RuleModel): void {\n const operationTag = rule.operation\n ? [...OPERATION_TAGS].find(([, operation]) => operation === rule.operation)?.[0]\n : null\n output.push('')\n pushFeatureTags(output, [...rule.tags, ...(operationTag ? [operationTag] : [])], 2)\n output.push(` Rule: ${rule.operation === 'RENAMED' ? 'Rename requirement' : rule.name}`)\n if (rule.operation === 'RENAMED') {\n pushFeatureDescription(\n output,\n `FROM: ### Requirement: ${rule.rename?.from}\\nTO: ### Requirement: ${rule.rename?.to}`,\n 4,\n )\n return\n }\n pushFeatureDescription(output, rule.description, 4)\n if (rule.background) serializeFeatureBackground(output, rule.background, 4)\n for (const scenario of rule.scenarios) serializeFeatureScenario(output, scenario, 4)\n}\n\nexport function serializeFeatureBackground(\n output: string[],\n background: BackgroundModel,\n indent: number,\n): void {\n output.push('', `${' '.repeat(indent)}Background:${background.name ? ` ${background.name}` : ''}`)\n pushFeatureDescription(output, background.description, indent + 2)\n pushFeatureSteps(output, background.steps, indent + 2)\n}\n\nexport function serializeFeatureScenario(\n output: string[],\n scenario: ScenarioModel,\n indent: number,\n): void {\n output.push('')\n pushFeatureTags(output, scenario.tags, indent)\n output.push(\n `${' '.repeat(indent)}${scenario.kind === 'outline' ? 'Scenario Outline' : 'Scenario'}: ${scenario.name}`,\n )\n pushFeatureDescription(output, scenario.description, indent + 2)\n pushFeatureSteps(output, scenario.steps, indent + 2)\n for (const examples of scenario.examples) {\n output.push('')\n pushFeatureTags(output, examples.tags, indent + 2)\n output.push(`${' '.repeat(indent + 2)}Examples: ${examples.name}`)\n pushFeatureDescription(output, examples.description, indent + 4)\n pushFeatureTable(output, examples.rows, indent + 4)\n }\n}\n\nexport function pushFeatureSteps(output: string[], steps: StepModel[], indent: number): void {\n const prefix = ' '.repeat(indent)\n for (const step of steps) {\n output.push(`${prefix}${titleCase(step.keyword)} ${step.text}`)\n if (step.table) pushFeatureTable(output, step.table, indent + 2)\n if (step.docString) {\n const fence = step.docString.content.split('\\n').some((line) => line.trim() === '\"\"\"')\n ? '```'\n : '\"\"\"'\n output.push(`${' '.repeat(indent + 2)}${fence}${step.docString.mediaType || ''}`)\n for (const line of step.docString.content.split('\\n'))\n output.push(`${' '.repeat(indent + 2)}${line}`)\n output.push(`${' '.repeat(indent + 2)}${fence}`)\n }\n }\n}\n\nexport function pushFeatureTable(output: string[], rows: string[][], indent: number): void {\n if (!rows?.length) return\n const prefix = ' '.repeat(indent)\n for (const row of rows) output.push(`${prefix}| ${row.map(escapeCell).join(' | ')} |`)\n}\n\nexport function pushFeatureDescription(\n output: string[],\n description: string,\n indent: number,\n): void {\n if (!description) return\n const prefix = ' '.repeat(indent)\n output.push('')\n for (const line of description.split('\\n')) output.push(line ? `${prefix}${line}` : '')\n}\n\nexport function pushFeatureTags(output: string[], tags: string[], indent: number): void {\n if (tags?.length) output.push(`${' '.repeat(indent)}${tags.join(' ')}`)\n}\n","import { DELTA_TAG, OPERATION_TAGS } from '../constants.js'\nimport type { BackgroundModel, DocumentModel, RuleModel, ScenarioModel } from '../model.js'\nimport { pushDescriptionAndSteps, pushTable, pushTags, trimBlank } from '../syntax.js'\n\nexport function serializeMdg(model: DocumentModel): string {\n const output: string[] = []\n const featureTags = model.kind === 'delta' ? [...model.tags, DELTA_TAG] : model.tags\n pushTags(output, featureTags)\n output.push(`# Feature: ${model.name}`, '')\n if (model.description) output.push(model.description, '')\n if (model.background) serializeMdgBackground(output, model.background, 2)\n for (const scenario of model.featureScenarios) serializeMdgScenario(output, scenario, 3)\n for (const rule of model.rules) serializeMdgRule(output, rule)\n return `${trimBlank(output).join('\\n')}\\n`\n}\n\nexport function serializeMdgRule(output: string[], rule: RuleModel): void {\n const operationTag = rule.operation\n ? [...OPERATION_TAGS].find(([, operation]) => operation === rule.operation)?.[0]\n : null\n pushTags(output, [...rule.tags, ...(operationTag ? [operationTag] : [])])\n output.push(`## Rule: ${rule.operation === 'RENAMED' ? 'Rename requirement' : rule.name}`, '')\n if (rule.operation === 'RENAMED') {\n output.push(\n `FROM: \\`### Requirement: ${rule.rename?.from}\\``,\n `TO: \\`### Requirement: ${rule.rename?.to}\\``,\n '',\n )\n return\n }\n if (rule.description) output.push(rule.description, '')\n if (rule.background) serializeMdgBackground(output, rule.background, 3)\n for (const scenario of rule.scenarios) serializeMdgScenario(output, scenario, 3)\n}\n\nexport function serializeMdgBackground(\n output: string[],\n background: BackgroundModel,\n level: number,\n): void {\n output.push(`${'#'.repeat(level)} Background:${background.name ? ` ${background.name}` : ''}`, '')\n pushDescriptionAndSteps(output, background, true)\n}\n\nexport function serializeMdgScenario(\n output: string[],\n scenario: ScenarioModel,\n level: number,\n): void {\n pushTags(output, scenario.tags)\n output.push(\n `${'#'.repeat(level)} ${scenario.kind === 'outline' ? 'Scenario Outline' : 'Scenario'}: ${scenario.name}`,\n '',\n )\n pushDescriptionAndSteps(output, scenario, true)\n for (const examples of scenario.examples) {\n pushTags(output, examples.tags)\n output.push(`${'#'.repeat(level + 1)} Examples: ${examples.name}`, '')\n if (examples.description) output.push(examples.description, '')\n pushTable(output, examples.rows)\n }\n}\n","import { DELTA_TAG, SYNTHETIC_TAG } from '../constants.js'\nimport type { DocumentModel, RuleModel, ScenarioModel } from '../model.js'\nimport { pushDescriptionAndSteps, pushTable, pushTags, trimBlank } from '../syntax.js'\n\nexport function serializeOpenSpec(model: DocumentModel): string {\n const output: string[] = []\n pushTags(output, model.tags)\n if (model.kind === 'delta') pushTags(output, [DELTA_TAG])\n output.push(`# ${model.name}`, '')\n if (model.description) output.push('## Purpose', '', model.description, '')\n if (model.kind === 'delta') serializeOpenSpecDelta(output, model)\n else serializeOpenSpecMain(output, model)\n return `${trimBlank(output).join('\\n')}\\n`\n}\n\nexport function serializeOpenSpecMain(output: string[], model: DocumentModel): void {\n output.push('## Requirements', '')\n if (model.background) {\n output.push(`**Background:${model.background.name ? ` ${model.background.name}` : ''}**`, '')\n pushDescriptionAndSteps(output, model.background)\n }\n if (model.featureScenarios.length) {\n pushTags(output, [SYNTHETIC_TAG])\n output.push(\n '### Requirement: Feature-level scenarios',\n '',\n 'The system SHALL exhibit the feature-level behavior defined by the following scenarios.',\n '',\n )\n for (const scenario of model.featureScenarios) serializeOpenSpecScenario(output, scenario)\n }\n for (const rule of model.rules) serializeOpenSpecRule(output, rule)\n}\n\nexport function serializeOpenSpecDelta(output: string[], model: DocumentModel): void {\n for (const operation of new Set(model.rules.map((rule) => rule.operation))) {\n const rules = model.rules.filter((rule) => rule.operation === operation)\n if (!rules.length) continue\n output.push(`## ${operation} Requirements`, '')\n if (operation === 'RENAMED') {\n for (const rule of rules) {\n pushTags(output, rule.tags)\n output.push(\n `- FROM: \\`### Requirement: ${rule.rename?.from}\\``,\n `- TO: \\`### Requirement: ${rule.rename?.to}\\``,\n '',\n )\n }\n } else if (operation === 'REMOVED') {\n for (const rule of rules) {\n pushTags(output, rule.tags)\n output.push(`### Requirement: ${rule.name}`, '')\n }\n } else {\n for (const rule of rules) serializeOpenSpecRule(output, rule)\n }\n }\n}\n\nexport function serializeOpenSpecRule(output: string[], rule: RuleModel): void {\n pushTags(output, rule.tags)\n output.push(`### Requirement: ${rule.name}`, '', rule.description, '')\n if (rule.background) {\n output.push(`#### Background: ${rule.background.name}`.trimEnd(), '')\n pushDescriptionAndSteps(output, rule.background)\n }\n for (const scenario of rule.scenarios) serializeOpenSpecScenario(output, scenario)\n}\n\nexport function serializeOpenSpecScenario(output: string[], scenario: ScenarioModel): void {\n pushTags(output, scenario.tags)\n output.push(\n `#### ${scenario.kind === 'outline' ? 'Scenario Outline' : 'Scenario'}: ${scenario.name}`,\n '',\n )\n pushDescriptionAndSteps(output, scenario)\n for (const examples of scenario.examples) {\n pushTags(output, examples.tags)\n output.push(`##### Examples: ${examples.name}`, '')\n if (examples.description) output.push(examples.description, '')\n pushTable(output, examples.rows)\n }\n}\n","import { RESERVED_PREFIXES } from '../constants.js'\nimport { ConversionError } from '../errors.js'\nimport type { DocumentModel } from '../model.js'\n\nexport function validateProfile(model: DocumentModel, sourceKind: string): void {\n if (!model.name) throw new ConversionError('Feature/spec name is required')\n if (!model.description && model.kind === 'main')\n throw new ConversionError('Feature Purpose/description is required')\n const allTags = [\n ...model.tags,\n ...model.rules.flatMap((rule) => [\n ...rule.tags,\n ...rule.scenarios.flatMap((scenario) => [\n ...scenario.tags,\n ...scenario.examples.flatMap((examples) => examples.tags),\n ]),\n ]),\n ...model.featureScenarios.flatMap((scenario) => scenario.tags),\n ]\n for (const tag of allTags) {\n if (RESERVED_PREFIXES.some((prefix) => tag.startsWith(prefix))) {\n throw new ConversionError(`Reserved tag is not allowed as user metadata: ${tag}`)\n }\n }\n if (model.kind === 'main') {\n if (!model.rules.length && !model.featureScenarios.length)\n throw new ConversionError('Feature has no behavior to convert')\n for (const rule of model.rules) {\n if (!/\\b(?:SHALL|MUST)\\b/.test(rule.description)) {\n throw new ConversionError(`Rule/Requirement \"${rule.name}\" must contain SHALL or MUST`)\n }\n if (!rule.scenarios.length && !rule.background) {\n throw new ConversionError(`Rule/Requirement \"${rule.name}\" has no scenarios or Background`)\n }\n }\n } else {\n if (model.background || model.featureScenarios.length)\n throw new ConversionError(\n 'Delta Features cannot contain feature-level Background or scenarios',\n )\n for (const rule of model.rules) {\n if (\n (rule.operation === 'ADDED' || rule.operation === 'MODIFIED') &&\n !/\\b(?:SHALL|MUST)\\b/.test(rule.description)\n ) {\n throw new ConversionError(\n `${rule.operation} Rule \"${rule.name}\" must contain SHALL or MUST`,\n )\n }\n if (\n (rule.operation === 'ADDED' || rule.operation === 'MODIFIED') &&\n !rule.scenarios.length &&\n !rule.background\n ) {\n throw new ConversionError(`${rule.operation} Rule \"${rule.name}\" requires behavior`)\n }\n if (\n rule.operation === 'REMOVED' &&\n (rule.description || rule.scenarios.length || rule.background)\n ) {\n throw new ConversionError(`REMOVED Rule \"${rule.name}\" must contain only its name`)\n }\n if (rule.operation === 'RENAMED' && !rule.rename)\n throw new ConversionError('RENAMED Rule requires FROM and TO')\n }\n }\n for (const scenario of [\n ...model.featureScenarios,\n ...model.rules.flatMap((rule) => rule.scenarios),\n ]) {\n if (!scenario.steps.length)\n throw new ConversionError(`Scenario \"${scenario.name}\" has no steps`)\n if (scenario.kind === 'outline' && !scenario.examples.length)\n throw new ConversionError(`Scenario Outline \"${scenario.name}\" has no Examples`)\n if (scenario.kind === 'scenario' && scenario.examples.length)\n throw new ConversionError(`Scenario \"${scenario.name}\" cannot contain Examples`)\n }\n if (sourceKind === 'openspec' && model.kind === 'delta' && !model.rules.length) {\n throw new ConversionError('Delta has no operations')\n }\n}\n","import { ConversionError } from './errors.js'\nimport type { ConversionOptions } from './model.js'\nimport { assertPreserved } from './model.js'\nimport { parseFeatureModel } from './parsers/feature.js'\nimport { parseMdgModel } from './parsers/mdg.js'\nimport { detectOpenSpecKind, parseOpenSpecModel } from './parsers/openspec.js'\nimport { serializeFeature } from './renderers/feature.js'\nimport { serializeMdg } from './renderers/mdg.js'\nimport { serializeOpenSpec } from './renderers/openspec.js'\nimport { resolveOpenSpecName, slugify } from './syntax.js'\nimport {\n gherkinDiagnostics,\n openSpecDiagnostics,\n parseAndValidateFeature,\n parseAndValidateMdg,\n validateOpenSpec,\n} from './validation/official.js'\nimport { validateProfile } from './validation/profile.js'\n\nexport async function mdgToOpsx(text: string, options: ConversionOptions = {}): Promise<string> {\n const source = parseAndValidateMdg(text, options.sourcePath)\n if (!source.valid || !source.document)\n throw new ConversionError('Invalid Markdown with Gherkin input', gherkinDiagnostics(source))\n const model = parseMdgModel(text, source.document)\n validateProfile(model, 'mdg')\n const output = serializeOpenSpec(model)\n const name = options.name || slugify(model.name)\n const report = await validateOpenSpec(output, {\n kind: model.kind,\n name,\n projectRoot: options.projectRoot,\n })\n if (!report.valid)\n throw new ConversionError(\n 'Generated OpenSpec failed official validation',\n openSpecDiagnostics(report),\n )\n assertPreserved(model, parseOpenSpecModel(output, { kind: model.kind, name }))\n return output\n}\n\nasync function openSpecModel(text: string, options: ConversionOptions) {\n const kind = detectOpenSpecKind(text)\n const name = resolveOpenSpecName(text, options)\n const report = await validateOpenSpec(text, { kind, name, projectRoot: options.projectRoot })\n if (!report.valid)\n throw new ConversionError('Invalid OpenSpec input', openSpecDiagnostics(report))\n const model = parseOpenSpecModel(text, { kind, name })\n validateProfile(model, 'openspec')\n return model\n}\n\nexport async function opsxToMdg(text: string, options: ConversionOptions = {}): Promise<string> {\n const model = await openSpecModel(text, options)\n const output = serializeMdg(model)\n const target = parseAndValidateMdg(output)\n if (!target.valid || !target.document)\n throw new ConversionError(\n 'Generated Markdown with Gherkin failed official validation',\n gherkinDiagnostics(target),\n )\n assertPreserved(model, parseMdgModel(output, target.document))\n return output\n}\n\nexport async function opsxToFeature(\n text: string,\n options: ConversionOptions = {},\n): Promise<string> {\n const model = await openSpecModel(text, options)\n const output = serializeFeature(model)\n const target = parseAndValidateFeature(output)\n if (!target.valid || !target.document)\n throw new ConversionError(\n 'Generated Gherkin feature failed official validation',\n gherkinDiagnostics(target),\n )\n assertPreserved(model, parseFeatureModel(target.document))\n return output\n}\n","import type { Readable } from 'node:stream'\nimport type { Direction } from '../model.js'\n\ninterface Options {\n force: boolean\n help: boolean\n text?: string\n input?: string\n output?: string\n name?: string\n projectRoot?: string\n}\n\nimport { link, mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises'\nimport { basename, dirname, resolve } from 'node:path'\nimport { mdgToOpsx, opsxToFeature, opsxToMdg } from '../convert.js'\nimport { ConversionError, isFsError } from '../errors.js'\n\nexport async function runConverterCli(\n direction: Direction,\n argv = process.argv.slice(2),\n): Promise<number> {\n try {\n const options = parseArgs(argv, direction)\n if (options.help) {\n process.stdout.write(usage(direction))\n return 0\n }\n const input = await readInput(options)\n const converters = {\n 'mdg-to-opsx': mdgToOpsx,\n 'opsx-to-mdg': opsxToMdg,\n 'opsx-to-feature': opsxToFeature,\n }\n const convert = converters[direction]\n const output = await convert(input.text, {\n sourcePath: input.path,\n name: options.name,\n projectRoot: options.projectRoot ? resolve(options.projectRoot) : undefined,\n })\n if (options.output) await writeAtomic(resolve(options.output), output, options.force)\n else process.stdout.write(output)\n return 0\n } catch (error) {\n if (error instanceof ConversionError) {\n process.stderr.write(`${error.message}\\n`)\n for (const diagnostic of error.diagnostics) process.stderr.write(`- ${diagnostic}\\n`)\n return 1\n }\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`)\n return 2\n }\n}\n\nfunction parseArgs(argv: string[], direction: Direction): Options {\n const options: Options = { force: false, help: false }\n for (let index = 0; index < argv.length; index += 1) {\n const argument = argv[index]\n if (argument === '--help' || argument === '-h') options.help = true\n else if (argument === '--force') options.force = true\n else if (argument === '--text') options.text = requiredValue(argv, ++index, argument)\n else if (argument === '--output' || argument === '-o')\n options.output = requiredValue(argv, ++index, argument)\n else if (argument === '--name') options.name = requiredValue(argv, ++index, argument)\n else if (argument === '--project-root')\n options.projectRoot = requiredValue(argv, ++index, argument)\n else if (argument.startsWith('-'))\n throw new Error(`Unknown option: ${argument}\\n${usage(direction)}`)\n else if (!options.input) options.input = argument\n else throw new Error(`Only one input path is allowed\\n${usage(direction)}`)\n }\n if (options.input && options.text !== undefined)\n throw new Error(`Use either an input path or --text, not both\\n${usage(direction)}`)\n if (options.force && !options.output) throw new Error('--force requires --output')\n return options\n}\n\nasync function readInput(options: Options) {\n if (options.text !== undefined) return { text: options.text, path: undefined }\n if (options.input) {\n const path = resolve(options.input)\n return { text: await readFile(path, 'utf8'), path }\n }\n if (process.stdin.isTTY) throw new Error('No input: pass a path, --text, or pipe text on stdin')\n return { text: await readStream(process.stdin), path: undefined }\n}\n\nasync function readStream(stream: Readable): Promise<string> {\n stream.setEncoding('utf8')\n let result = ''\n for await (const chunk of stream) result += chunk\n return result\n}\n\nexport async function writeAtomic(path: string, content: string, force: boolean): Promise<void> {\n await mkdir(dirname(path), { recursive: true })\n const temporary = resolve(dirname(path), `.${basename(path)}.${process.pid}.${Date.now()}.tmp`)\n await writeFile(temporary, content, { encoding: 'utf8', flag: 'wx' })\n try {\n if (force) await rename(temporary, path)\n else {\n try {\n await link(temporary, path)\n } catch (error) {\n if (isFsError(error, 'EEXIST'))\n throw new Error(`Output already exists: ${path} (use --force to replace it)`)\n throw error\n }\n await unlink(temporary)\n }\n } catch (error) {\n await unlink(temporary).catch(() => {})\n throw error\n }\n}\n\nfunction requiredValue(argv: string[], index: number, option: string): string {\n if (index >= argv.length) throw new Error(`Missing value for ${option}`)\n return argv[index]\n}\n\nfunction usage(direction: Direction): string {\n return `Usage: ${direction} [input] [--text content] [-o output] [--force] [--name name] [--project-root path]\\n`\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as runConverterCli } from "./converter-CFtzcfoM.mjs";
2
+ import { t as runConverterCli } from "./converter-BaJKyFVG.mjs";
3
3
  //#region src/bin/mdg-to-opsx.ts
4
4
  process.exitCode = await runConverterCli("mdg-to-opsx");
5
5
  //#endregion
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as runConverterCli } from "./converter-CFtzcfoM.mjs";
2
+ import { t as runConverterCli } from "./converter-BaJKyFVG.mjs";
3
3
  //#region src/bin/opsx-to-feature.ts
4
4
  process.exitCode = await runConverterCli("opsx-to-feature");
5
5
  //#endregion
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as runConverterCli } from "./converter-CFtzcfoM.mjs";
2
+ import { t as runConverterCli } from "./converter-BaJKyFVG.mjs";
3
3
  //#region src/bin/opsx-to-mdg.ts
4
4
  process.exitCode = await runConverterCli("opsx-to-mdg");
5
5
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opsx-gherkin",
3
- "version": "1.0.1",
3
+ "version": "2.0.1",
4
4
  "description": "Deterministic OpenSpec and Gherkin conversion with a self-contained syntax compatibility skill.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -26,13 +26,14 @@ and a directly validatable format; it is not a converter input.
26
26
  After reading the contract, choose a complete example for the constructs you need:
27
27
 
28
28
  - Basic main spec: [minimal.feature.md](references/examples/minimal.feature.md),
29
- with a Feature description, a software target, one normative Rule, and a single-row Outline;
30
- equivalent [OpenSpec](references/examples/minimal.openspec.md) and
31
- [plain Gherkin](references/examples/minimal.feature).
32
- - Shared setup, outlines, multiple Examples groups, Data Tables, and Doc Strings:
33
- [account.feature.md](references/examples/account.feature.md), with
34
- [OpenSpec](references/examples/account.openspec.md) and
35
- [plain Gherkin](references/examples/account.feature) counterparts.
29
+ with a Feature description, one normative Rule, and a single-row Outline with
30
+ named arguments; equivalent [OpenSpec](references/examples/minimal.openspec.md)
31
+ and [plain Gherkin](references/examples/minimal.feature).
32
+ - Shared setup, Rules, outlines, and tags in paired OpenSpec and MDG form: the
33
+ contract's "Convert a complete sign-in specification to MDG" scenario.
34
+ Several tagged Examples groups, Data Tables, and Doc Strings: the Examples-group
35
+ and literal step-argument Outlines in the contract's "Preserve behavioral
36
+ Gherkin constructs" Rule.
36
37
  - Delta operations: [authentication-delta.feature.md](references/examples/authentication-delta.feature.md)
37
38
  with [OpenSpec](references/examples/authentication-delta.openspec.md) and
38
39
  [plain Gherkin](references/examples/authentication-delta.feature) counterparts.
@@ -42,15 +43,8 @@ After reading the contract, choose a complete example for the constructs you nee
42
43
 
43
44
  These examples apply the canonical contract to their own domains.
44
45
 
45
- ## Author within the contract
46
+ ## Write compatible syntax
46
47
 
47
- - Model each completed Feature as one whole public use case. Give each scenario
48
- its own necessary prior facts, one action or query, and observable consequences.
49
- Keep acceptance and rejection in separate scenarios of the same use case.
50
- - Expose meaningful example values as named Outline arguments, even for one row.
51
- Keep independent roles distinct, such as stored/submitted credentials and
52
- requested/resulting quantities. Keep fixed criteria in Rules, cohesive records
53
- in Data Tables, and literal source text intact when the use case examines it.
54
48
  - Match the requested representation: Gherkin headings and bullet steps in MDG,
55
49
  OpenSpec requirement headings and bold step keywords in OpenSpec, native
56
50
  keyword lines in `.feature`. A fenced native document is not MDG structure.
@@ -61,18 +55,22 @@ These examples apply the canonical contract to their own domains.
61
55
  its intended scope; preserve the original step roles and all example values.
62
56
  - Use English structural keywords supported by this profile. Do not translate
63
57
  keywords or rewrite domain content merely to make conversion succeed.
58
+ - Write specification content in English. Use another language only when the
59
+ user or the project's `CLAUDE.md` or `AGENTS.md` explicitly requests it for
60
+ the specifications; structural keywords stay English either way. Instructions
61
+ about the language of other documents, such as proposals, do not apply.
64
62
  - Bind outline placeholders with concrete Examples rows. Keep Examples groups,
65
63
  their tags, and step Data Tables distinct. Follow the contract's indented
66
64
  Markdown table syntax and escaping. Check parsed row counts and literal
67
65
  values, especially pipes, backslashes, newlines, and Doc String contents.
68
- - Identify a completed Feature's verification boundary: `@code` for software,
69
- `@skill` for an agent applying guidance, `@prompt` for a model executing a prompt,
70
- or `@plugin` for its host integration. The software teaching examples use `@code`.
71
- These tags identify the exercised boundary, not its internal dependencies.
72
- Descriptive topic tags are optional and need not be copied from the contract.
73
66
  - Conversion accepts compatible untagged documents and plain Scenarios too.
74
- Preserve their existing model; do not insert targets or invent parameters
75
- while converting. Authoring conventions do not narrow syntax acceptance.
67
+ Preserve their existing model; do not insert tags or invent parameters while
68
+ converting. Descriptive tags are metadata without meaning to the converter,
69
+ and none is required: a new document may omit them, but conversion preserves
70
+ every supplied tag unchanged. Authoring conventions do not narrow syntax
71
+ acceptance.
72
+ - An HTML comment at the end of a step is step text: every conversion
73
+ preserves it verbatim, and it has no meaning to the converter.
76
74
  - In MDG, put each tag in its own backtick span immediately above the tagged
77
75
  heading. In native Gherkin, use bare tags. Reserve `@openspec-*` and `@mdg-*`
78
76
  names for the structural roles defined by the contract.
@@ -83,20 +81,20 @@ These examples apply the canonical contract to their own domains.
83
81
 
84
82
  ### Keep table and argument values intact
85
83
 
86
- Indent Markdown table rows by two spaces. In MDG, put data rows directly after
87
- the header, without a separator row; the converter emits this canonical form.
88
- OpenSpec tables retain their Markdown separator row. The pinned MDG parser
89
- accepts either input form and preserves the interpreted rows; table formatting
90
- must not change Examples cases or Data Table values. Plain Gherkin also omits
91
- separators. Use `\|` for a literal pipe, `\\` for a literal backslash,
92
- and `\n` for a newline inside a cell. Do not apply cell escaping to Doc Strings.
84
+ Write each Markdown table as an indented header, one separator row with a `---`
85
+ cell per column, and indented data rows, each line indented by two spaces. This
86
+ is the canonical form the converter emits for MDG and OpenSpec; plain Gherkin
87
+ omits the separator. The pinned MDG parser treats the separator as formatting
88
+ and also accepts MDG tables without it, so table formatting must not change
89
+ Examples cases or Data Table values. Use `\|` for a literal pipe, `\\` for a
90
+ literal backslash, and `\n` for a newline inside a cell. Do not apply cell
91
+ escaping to Doc Strings.
93
92
 
94
93
  Keep a Markdown Doc String in a triple-backtick block indented by two spaces
95
94
  under its step; put its optional media type immediately after the opening fence.
96
95
  Preserve the content's indentation relative to the fence. In an outline, each
97
96
  `<placeholder>` names an Examples column; retain every intended data row and
98
- verify the expanded cases. The account example demonstrates two separate
99
- Examples groups and a JSON Doc String containing a placeholder.
97
+ verify the expanded cases.
100
98
 
101
99
  ## Convert and verify
102
100
 
@@ -1,11 +1,13 @@
1
- `@code` `@contract`
1
+ `@contract`
2
2
  # Feature: Convert a compatible specification
3
3
 
4
4
  This document is the normative contract for the conversion profile. The converters SHALL preserve the domain structure represented here and SHALL reject mappings not defined here.
5
5
 
6
6
  Its Rules and Scenarios specify the converter, and their structure demonstrates valid Markdown with Gherkin. To author a specification for another domain, express that domain's requested behavior using the demonstrated structure and the compatibility rules in this complete contract.
7
7
 
8
- The **@code** target identifies software as this Feature’s verification boundary. Descriptive tags such as **@contract** are optional example metadata. A new main specification may omit them; conversion of an existing document must preserve its tags. Markers in the reserved **@openspec-** and **@mdg-** namespaces have only the structural roles defined below.
8
+ Write specification content in English unless the user or the project's agent instructions, such as `CLAUDE.md` or `AGENTS.md`, explicitly request another language for the specifications; structural keywords remain English in every case, and instructions about the language of other documents do not apply to specifications.
9
+
10
+ Descriptive tags such as **@contract** are optional example metadata. A new main specification may omit them; conversion of an existing document must preserve its tags. Markers in the reserved **@openspec-** and **@mdg-** namespaces have only the structural roles defined below.
9
11
 
10
12
  The separate [profile-access use case](profile-access.feature.md) specifies how to find and retrieve this contract.
11
13
 
@@ -51,6 +53,7 @@ The converter SHALL map a Gherkin Feature to an OpenSpec title and Purpose, and
51
53
  * Given the following structural correspondence:
52
54
 
53
55
  | MDG | OpenSpec |
56
+ | --- | --- |
54
57
  | Feature | title and Purpose |
55
58
  | Rule | Requirement |
56
59
  | Rule description | normative requirement text |
@@ -71,7 +74,7 @@ The converter SHALL map a Gherkin Feature to an OpenSpec title and Purpose, and
71
74
  * Given this compatible OpenSpec document:
72
75
 
73
76
  ```markdown
74
- `@code`
77
+ `@authentication`
75
78
  # Sign in with account credentials
76
79
 
77
80
  ## Purpose
@@ -119,7 +122,7 @@ The converter SHALL map a Gherkin Feature to an OpenSpec title and Purpose, and
119
122
  * Then the destination is:
120
123
 
121
124
  ```markdown
122
- `@code`
125
+ `@authentication`
123
126
  # Feature: Sign in with account credentials
124
127
 
125
128
  Give an active account access when the submitted credentials match its registered credentials.
@@ -143,6 +146,7 @@ The converter SHALL map a Gherkin Feature to an OpenSpec title and Purpose, and
143
146
  #### Examples: Matching credentials
144
147
 
145
148
  | registered_email | registered_password | submitted_email | submitted_password | expected_email |
149
+ | --- | --- | --- | --- | --- |
146
150
  | user@example.com | correct-password | user@example.com | correct-password | user@example.com |
147
151
 
148
152
  `@rejected`
@@ -155,14 +159,15 @@ The converter SHALL map a Gherkin Feature to an OpenSpec title and Purpose, and
155
159
  #### Examples: Nonmatching password
156
160
 
157
161
  | registered_email | registered_password | submitted_email | submitted_password |
162
+ | --- | --- | --- | --- |
158
163
  | user@example.com | correct-password | user@example.com | wrong-password |
159
164
  ```
160
165
 
161
166
  ## Rule: Preserve behavioral Gherkin constructs
162
167
 
163
- The converter SHALL preserve Scenario and Scenario Outline structure, typed steps, tags, Examples, Data Tables, and Doc Strings without treating them as opaque prose.
168
+ The converter SHALL preserve Scenario and Scenario Outline structure, typed steps, tags, Examples, Data Tables, and Doc Strings without treating them as opaque prose. An inline HTML comment inside a step's text is step text and SHALL be preserved verbatim.
164
169
 
165
- Canonical MDG tables SHALL contain an indented header followed directly by data rows, without Markdown separator rows. Canonical OpenSpec tables SHALL retain their Markdown separator rows. Compatible MDG inputs with or without separators SHALL preserve the same interpreted rows and values.
170
+ Canonical MDG and OpenSpec tables SHALL carry one Markdown separator row directly after their indented header; canonical plain Gherkin tables SHALL omit it. Compatible MDG inputs with or without separators SHALL preserve the same interpreted rows and values.
166
171
 
167
172
  ### Scenario: Map typed steps
168
173
 
@@ -188,6 +193,7 @@ Canonical MDG tables SHALL contain an indented header followed directly by data
188
193
  #### Examples: One group
189
194
 
190
195
  | source_format | target_format | source_groups | expected_groups |
196
+ | --- | --- | --- | --- |
191
197
  | MDG | OpenSpec | 1 | 1 |
192
198
  | OpenSpec | MDG | 1 | 1 |
193
199
 
@@ -195,6 +201,7 @@ Canonical MDG tables SHALL contain an indented header followed directly by data
195
201
  #### Examples: Several groups
196
202
 
197
203
  | source_format | target_format | source_groups | expected_groups |
204
+ | --- | --- | --- | --- |
198
205
  | OpenSpec | MDG | 3 | 3 |
199
206
  | MDG | OpenSpec | 3 | 3 |
200
207
 
@@ -203,6 +210,7 @@ Canonical MDG tables SHALL contain an indented header followed directly by data
203
210
  * Given a compatible `<source_format>` main spec with a scenario step containing this Data Table:
204
211
 
205
212
  | field | value |
213
+ | --- | --- |
206
214
  | status | active |
207
215
  | attempts | 4 |
208
216
  * And another step in the same scenario containing this Doc String:
@@ -218,9 +226,25 @@ Canonical MDG tables SHALL contain an indented header followed directly by data
218
226
  #### Examples: Argument conversion directions
219
227
 
220
228
  | source_format | target_format |
229
+ | --- | --- |
221
230
  | MDG | OpenSpec |
222
231
  | OpenSpec | MDG |
223
232
 
233
+ ### Scenario Outline: Preserve an inline step comment in `<target_format>`
234
+
235
+ * Given a compatible `<source_format>` main spec or delta whose step text ends with an inline HTML comment such as `<!-- addUnits -->`
236
+ * When it is converted to `<target_format>`
237
+ * Then the step text, including the comment, is unchanged
238
+ * And the converter attaches no meaning to the comment
239
+
240
+ #### Examples: Comment conversion directions
241
+
242
+ | source_format | target_format |
243
+ | --- | --- |
244
+ | OpenSpec | MDG |
245
+ | MDG | OpenSpec |
246
+ | OpenSpec | plain Gherkin |
247
+
224
248
  ## Rule: Project OpenSpec to a plain Gherkin feature
225
249
 
226
250
  The `opsx-to-feature` command SHALL preserve the same normalized behavioral model as `opsx-to-mdg` and MUST emit plain-text Gherkin accepted by the official parser.
@@ -254,6 +278,7 @@ The converter SHALL mark a delta Feature with **@openspec-delta** and SHALL repr
254
278
  #### Examples: Supported operations
255
279
 
256
280
  | operation | tag |
281
+ | --- | --- |
257
282
  | ADDED | @openspec-added |
258
283
  | MODIFIED | @openspec-modified |
259
284
  | REMOVED | @openspec-removed |
@@ -269,6 +294,7 @@ The converter SHALL mark a delta Feature with **@openspec-delta** and SHALL repr
269
294
  #### Examples: Operation identities
270
295
 
271
296
  | operation | tag |
297
+ | --- | --- |
272
298
  | ADDED | @openspec-added |
273
299
  | MODIFIED | @openspec-modified |
274
300
  | REMOVED | @openspec-removed |
@@ -288,6 +314,7 @@ The converter SHALL mark a delta Feature with **@openspec-delta** and SHALL repr
288
314
  #### Examples: Renamed requirements
289
315
 
290
316
  | old_name | new_name |
317
+ | --- | --- |
291
318
  | Login audit | Authentication audit |
292
319
 
293
320
  ## Rule: Validate and normalize every conversion
@@ -312,6 +339,7 @@ The converter SHALL validate both endpoints with their official deterministic va
312
339
  #### Examples: Invalid profile outcome
313
340
 
314
341
  | exit_status |
342
+ | --- |
315
343
  | 1 |
316
344
 
317
345
  ### Scenario: Reject removal metadata without a defined mapping
@@ -338,6 +366,7 @@ The converter SHALL validate both endpoints with their official deterministic va
338
366
  #### Examples: Ordered delta conversion directions
339
367
 
340
368
  | source_format | target_format |
369
+ | --- | --- |
341
370
  | OpenSpec | MDG |
342
371
  | MDG | OpenSpec |
343
372
 
@@ -354,6 +383,7 @@ All converter commands SHALL accept one path, inline text, or stdin and SHALL re
354
383
  #### Examples: Input modes
355
384
 
356
385
  | source |
386
+ | --- |
357
387
  | positional path |
358
388
  | --text |
359
389
  | stdin |
@@ -369,4 +399,5 @@ All converter commands SHALL accept one path, inline text, or stdin and SHALL re
369
399
  #### Examples: Protected destination
370
400
 
371
401
  | destination | exit_status |
402
+ | --- | --- |
372
403
  | account.spec.md | 2 |
@@ -1,4 +1,4 @@
1
- @code @openspec-delta
1
+ @openspec-delta
2
2
  Feature: Authentication delta
3
3
 
4
4
  STRUCTURAL CHANGE DOCUMENT: This Feature packages OpenSpec delta operations for authentication software. It demonstrates a change document, not one completed application use case. The added and modified requirements contain complete behavioral examples; removal and rename contain only the names allowed by the conversion contract.
@@ -1,4 +1,4 @@
1
- `@code` `@openspec-delta`
1
+ `@openspec-delta`
2
2
  # Feature: Authentication delta
3
3
 
4
4
  STRUCTURAL CHANGE DOCUMENT: This Feature packages OpenSpec delta operations for authentication software. It demonstrates a change document, not one completed application use case. The added and modified requirements contain complete behavioral examples; removal and rename contain only the names allowed by the conversion contract.
@@ -17,6 +17,7 @@ The system SHALL accept one unused recovery code.
17
17
  #### Examples: Matching unused recovery code
18
18
 
19
19
  | accountEmail | storedRecoveryCode | submittedEmail | submittedRecoveryCode | expectedAccountEmail |
20
+ | --- | --- | --- | --- | --- |
20
21
  | user@example.com | recovery-7 | user@example.com | recovery-7 | user@example.com |
21
22
 
22
23
  `@openspec-modified`
@@ -33,6 +34,7 @@ The system SHALL reject an incorrect password.
33
34
  #### Examples: Password mismatch
34
35
 
35
36
  | accountEmail | storedPassword | submittedEmail | submittedPassword |
37
+ | --- | --- | --- | --- |
36
38
  | user@example.com | correct-password | user@example.com | wrong-password |
37
39
 
38
40
  `@openspec-removed`
@@ -1,4 +1,3 @@
1
- `@code`
2
1
  `@openspec-delta`
3
2
  # Authentication delta
4
3
 
@@ -1,4 +1,3 @@
1
- @code
2
1
  Feature: Add a product to an empty shopping cart
3
2
 
4
3
  Keep the quantity a customer selects when adding a product to an empty shopping cart.
@@ -1,4 +1,3 @@
1
- `@code`
2
1
  # Feature: Add a product to an empty shopping cart
3
2
 
4
3
  Keep the quantity a customer selects when adding a product to an empty shopping cart.
@@ -16,4 +15,5 @@ The cart SHALL contain the selected quantity of a product after the customer add
16
15
  #### Examples: Selected quantity
17
16
 
18
17
  | productId | requestedQuantity | expectedQuantity |
18
+ | --- | --- | --- |
19
19
  | P-7 | 2 | 2 |
@@ -1,4 +1,3 @@
1
- `@code`
2
1
  # Add a product to an empty shopping cart
3
2
 
4
3
  ## Purpose
@@ -1,4 +1,3 @@
1
- `@code`
2
1
  # Feature: Access specification conversion guidance
3
2
 
4
3
  Find the supported output formats and retrieve the canonical conversion profile before authoring a compatible specification. The [canonical mapping contract](OPENSPEC_MDG_PROFILE.feature.md) defines the mappings; this Feature specifies access to that resource.
@@ -30,5 +29,6 @@ The profile and instructions commands SHALL return the canonical mapping contrac
30
29
  #### Examples: Resource commands
31
30
 
32
31
  | command | exit_status |
32
+ | --- | --- |
33
33
  | profile | 0 |
34
34
  | instructions | 0 |
@@ -1 +0,0 @@
1
- {"version":3,"file":"converter-CFtzcfoM.mjs","names":[],"sources":["../src/model.ts","../src/constants.ts","../src/syntax.ts","../src/parsers/mdg.ts","../src/parsers/feature.ts","../src/parsers/openspec.ts","../src/renderers/feature.ts","../src/renderers/mdg.ts","../src/renderers/openspec.ts","../src/validation/profile.ts","../src/convert.ts","../src/cli/converter.ts"],"sourcesContent":["import { isDeepStrictEqual } from 'node:util'\nimport { ConversionError } from './errors.js'\n\nexport type DocumentKind = 'main' | 'delta'\nexport type Operation = 'ADDED' | 'MODIFIED' | 'REMOVED' | 'RENAMED'\nexport type Direction = 'mdg-to-opsx' | 'opsx-to-mdg' | 'opsx-to-feature'\nexport interface ConversionOptions {\n sourcePath?: string\n name?: string\n projectRoot?: string\n}\nexport interface StepModel {\n keyword: string\n text: string\n table: string[][] | null\n docString: { mediaType: string; content: string } | null\n}\nexport interface BackgroundModel {\n name: string\n description: string\n steps: StepModel[]\n}\nexport interface ExamplesModel {\n name: string\n description: string\n tags: string[]\n rows: string[][]\n}\nexport interface ScenarioModel extends BackgroundModel {\n kind: 'scenario' | 'outline'\n tags: string[]\n examples: ExamplesModel[]\n}\nexport interface RuleModel {\n name: string\n description: string\n tags: string[]\n operation: Operation | undefined\n rename: { from: string; to: string } | null\n background: BackgroundModel | null\n scenarios: ScenarioModel[]\n}\nexport interface DocumentModel {\n kind: DocumentKind\n name: string\n description: string\n tags: string[]\n background: BackgroundModel | null\n featureScenarios: ScenarioModel[]\n rules: RuleModel[]\n}\n\n/** Rename Rules are structural wrappers; their FROM and TO names carry the identity. */\nexport function normalizedModel(model: DocumentModel): DocumentModel {\n return {\n ...model,\n rules: model.rules.map((rule) => ({\n ...rule,\n name: rule.operation === 'RENAMED' ? 'Rename requirement' : rule.name,\n })),\n }\n}\n\nexport function assertPreserved(source: DocumentModel, destination: DocumentModel): void {\n if (!isDeepStrictEqual(normalizedModel(source), normalizedModel(destination))) {\n throw new ConversionError('Conversion would change the normalized document model', [\n 'The destination was not emitted. Check supported structure, ordering, and literal values.',\n ])\n }\n}\n","import type { Operation } from './model.js'\n\nexport const OPERATION_TAGS = new Map<string, Operation>([\n ['@openspec-added', 'ADDED'],\n ['@openspec-modified', 'MODIFIED'],\n ['@openspec-removed', 'REMOVED'],\n ['@openspec-renamed', 'RENAMED'],\n])\nexport const RESERVED_PREFIXES = ['@openspec-', '@mdg-']\nexport const SYNTHETIC_TAG = '@mdg-feature-scenarios'\nexport const DELTA_TAG = '@openspec-delta'\n","import { basename, dirname } from 'node:path'\nimport { ConversionError } from './errors.js'\nimport type { ConversionOptions, StepModel } from './model.js'\n\nexport interface Heading {\n index: number\n title: string\n}\nexport interface Section {\n start: number\n end: number\n}\nexport type Located = { location: { line: number } }\nexport type LocatedChild =\n | Located\n | { background?: Located; scenario?: Located; rule?: Located; examples?: Located }\n\nexport function pushDescriptionAndSteps(\n output: string[],\n node: { description: string; steps: StepModel[] },\n mdg = false,\n): void {\n if (node.description) output.push(node.description, '')\n for (const step of node.steps) {\n const keyword = mdg ? titleCase(step.keyword) : `**${step.keyword}**`\n output.push(`${mdg ? '*' : '-'} ${keyword} ${step.text}`)\n if (step.table) pushTable(output, step.table, false, !mdg)\n if (step.docString) {\n output.push('', ` \\`\\`\\`${step.docString.mediaType || ''}`)\n for (const line of step.docString.content.split('\\n')) output.push(` ${line}`)\n output.push(' ```')\n }\n }\n output.push('')\n}\n\nexport function pushTable(\n output: string[],\n rows: string[][],\n blankAfter = true,\n separator = true,\n): void {\n if (!rows?.length) return\n output.push(` | ${rows[0].map(escapeCell).join(' | ')} |`)\n if (separator) output.push(` | ${rows[0].map(() => '---').join(' | ')} |`)\n for (const row of rows.slice(1)) output.push(` | ${row.map(escapeCell).join(' | ')} |`)\n if (blankAfter) output.push('')\n}\n\nexport function parseTable(lines: string[]): string[][] {\n const rows = lines.filter((line) => /^\\s{2,}\\|/.test(line)).map(parseTableRow)\n if (rows.length >= 2 && rows[1].every((cell) => /^:?-+:?$/.test(cell))) rows.splice(1, 1)\n if (!rows.length) throw new ConversionError('Empty table')\n const width = rows[0].length\n if (rows.some((row) => row.length !== width))\n throw new ConversionError('Inconsistent table column count')\n return rows\n}\n\nexport function parseTableRow(line: string): string[] {\n const content = line.trim().replace(/^\\|/, '').replace(/\\|$/, '')\n const cells = []\n let cell = ''\n let escaped = false\n for (const char of content) {\n if (escaped) {\n cell += char === 'n' ? '\\n' : char\n escaped = false\n } else if (char === '\\\\') escaped = true\n else if (char === '|') {\n cells.push(cell.trim())\n cell = ''\n } else cell += char\n }\n cells.push(cell.trim())\n return cells\n}\n\nexport function normalizeLines(text: string): string[] {\n return text\n .replace(/^\\uFEFF/, '')\n .replace(/\\r\\n?/g, '\\n')\n .split('\\n')\n}\n\nexport function fenceMask(lines: string[]): boolean[] {\n const mask = []\n let fence: string | null = null\n for (let index = 0; index < lines.length; index += 1) {\n const match = lines[index].match(/^\\s*(`{3,}|~{3,})/)\n mask[index] = Boolean(fence)\n if (match && !fence) {\n fence = match[1][0]\n mask[index] = true\n } else if (match && fence === match[1][0]) {\n mask[index] = true\n fence = null\n }\n }\n return mask\n}\n\nexport function findHeading(lines: string[], mask: boolean[], level: number): Heading | null {\n for (let index = 0; index < lines.length; index += 1) {\n const match = !mask[index] && lines[index].match(new RegExp(`^#{${level}}\\\\s+(.+?)\\\\s*$`))\n if (match) return { index, title: match[1].trim() }\n }\n return null\n}\n\nexport function headingsInRange(\n lines: string[],\n mask: boolean[],\n level: number,\n start: number,\n end: number,\n): Heading[] {\n const result = []\n for (let index = start; index < end; index += 1) {\n const match = !mask[index] && lines[index].match(new RegExp(`^#{${level}}\\\\s+(.+?)\\\\s*$`))\n if (match) result.push({ index, title: match[1].trim() })\n }\n return result\n}\n\nexport function findSection(lines: string[], mask: boolean[], title: string): Section {\n const section = findSectionOptional(lines, mask, title)\n if (!section) throw new ConversionError(`Missing OpenSpec section: ## ${title}`)\n return section\n}\n\nexport function findSectionOptional(\n lines: string[],\n mask: boolean[],\n title: string,\n): Section | null {\n for (let index = 0; index < lines.length; index += 1) {\n if (!mask[index] && new RegExp(`^##\\\\s+${escapeRegex(title)}\\\\s*$`, 'i').test(lines[index])) {\n let end = lines.length\n for (let next = index + 1; next < lines.length; next += 1) {\n if (!mask[next] && /^##\\s+/.test(lines[next])) {\n end = next\n break\n }\n }\n return { start: index, end }\n }\n }\n return null\n}\n\nexport function sectionContent(lines: string[], mask: boolean[], title: string): string | null {\n const section = findSectionOptional(lines, mask, title)\n return section ? trimBlank(lines.slice(section.start + 1, section.end)).join('\\n') : null\n}\n\nexport function precedingTags(lines: string[], headingIndex: number): string[] {\n let index = headingIndex - 1\n while (index >= 0 && !lines[index].trim()) index -= 1\n const blocks = []\n while (index >= 0 && isTagLine(lines[index])) {\n blocks.unshift(...extractTags(lines[index]))\n index -= 1\n while (index >= 0 && !lines[index].trim()) index -= 1\n }\n return blocks\n}\n\nexport function tagBlockStart(lines: string[], headingIndex: number): number {\n let index = headingIndex - 1\n while (index >= 0 && !lines[index].trim()) index -= 1\n while (index >= 0 && isTagLine(lines[index])) {\n index -= 1\n while (index >= 0 && !lines[index].trim()) index -= 1\n }\n return index + 1\n}\n\nexport function tagStartLine(lines: string[], node: LocatedChild): number {\n return tagBlockStart(lines, nodeLine(node) - 1) + 1\n}\n\nexport function isTagLine(line: string): boolean {\n const matches = [...line.matchAll(/`(@[^`]+)`/g)]\n return matches.length > 0 && line.replace(/`(@[^`]+)`/g, '').trim() === ''\n}\n\nexport function extractTags(line: string): string[] {\n return [...line.matchAll(/`(@[^`]+)`/g)].map((match) => match[1])\n}\n\nexport function proseBetween(lines: string[], startLine: number, endLine: number): string {\n const slice = lines.slice(Math.max(0, startLine - 1), Math.max(0, endLine - 1))\n while (slice.length && (!slice[slice.length - 1].trim() || isTagLine(slice[slice.length - 1])))\n slice.pop()\n return trimBlank(slice).join('\\n')\n}\n\nexport function nextSiblingLine(line: number, siblings: LocatedChild[], fallback: number): number {\n return (\n siblings\n .map(nodeLine)\n .filter((candidate) => candidate > line)\n .sort((a, b) => a - b)[0] || fallback\n )\n}\n\nexport function nodeLine(child: LocatedChild): number {\n if ('location' in child) return child.location.line\n const value = child.background ?? child.scenario ?? child.rule ?? child.examples\n if (!value) throw new ConversionError('Missing Gherkin node location')\n return value.location.line\n}\n\nexport function trimBlank(lines: string[]): string[] {\n const copy = [...lines]\n while (copy.length && !copy[0].trim()) copy.shift()\n while (copy.length && !copy[copy.length - 1].trim()) copy.pop()\n return copy\n}\n\nexport function pushTags(output: string[], tags: string[]): void {\n if (tags?.length) output.push(tags.map((tag) => `\\`${tag}\\``).join(' '))\n}\n\nexport function resolveOpenSpecName(text: string, options: ConversionOptions): string {\n const lines = normalizeLines(text)\n const mask = fenceMask(lines)\n const h1 = findHeading(lines, mask, 1)\n if (h1) return h1.title.replace(/^Feature:\\s*/i, '').trim()\n if (options.name) return options.name\n if (options.sourcePath) {\n const parent = basename(dirname(options.sourcePath))\n if (parent && parent !== '.') return parent\n }\n throw new ConversionError('OpenSpec text without an H1 title requires --name')\n}\n\nexport function slugify(value: string): string {\n return (\n value\n .toLowerCase()\n .trim()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '') || 'spec'\n )\n}\n\nexport function titleCase(keyword: string): string {\n return keyword[0] + keyword.slice(1).toLowerCase()\n}\n\nexport function escapeCell(value: string): string {\n return String(value).replace(/\\\\/g, '\\\\\\\\').replace(/\\|/g, '\\\\|').replace(/\\n/g, '\\\\n')\n}\n\nexport function escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","import type {\n Background,\n Examples,\n GherkinDocument,\n Rule,\n Scenario,\n Step,\n} from '@cucumber/messages'\nimport { DELTA_TAG, OPERATION_TAGS } from '../constants.js'\nimport { ConversionError } from '../errors.js'\nimport type {\n BackgroundModel,\n DocumentModel,\n ExamplesModel,\n Operation,\n RuleModel,\n ScenarioModel,\n StepModel,\n} from '../model.js'\nimport type { LocatedChild } from '../syntax.js'\nimport { nextSiblingLine, nodeLine, normalizeLines, proseBetween, tagStartLine } from '../syntax.js'\n\nexport function parseMdgModel(text: string, document: GherkinDocument): DocumentModel {\n const feature = document.feature\n if (!feature) throw new ConversionError('Gherkin document has no Feature')\n const lines = normalizeLines(text)\n const featureChildren = [...feature.children].sort((a, b) => nodeLine(a) - nodeLine(b))\n const firstFeatureChild = featureChildren[0]\n ? tagStartLine(lines, featureChildren[0])\n : lines.length + 1\n const description = proseBetween(lines, feature.location.line + 1, firstFeatureChild)\n const featureTags = feature.tags.map((tag) => tag.name)\n const isDelta = featureTags.includes(DELTA_TAG)\n const model: DocumentModel = {\n kind: isDelta ? 'delta' : 'main',\n name: feature.name.trim(),\n description,\n tags: featureTags.filter((tag) => tag !== DELTA_TAG),\n background: null,\n featureScenarios: [],\n rules: [],\n }\n\n for (let index = 0; index < featureChildren.length; index += 1) {\n const child = featureChildren[index]\n const boundary = featureChildren[index + 1]\n ? tagStartLine(lines, featureChildren[index + 1])\n : lines.length + 1\n if (child.background)\n model.background = mapMdgBackground(child.background, lines, featureChildren)\n if (child.scenario) model.featureScenarios.push(mapMdgScenario(child.scenario, lines))\n if (child.rule) model.rules.push(mapMdgRule(child.rule, lines, isDelta, boundary))\n }\n return model\n}\n\nexport function mapMdgRule(\n rule: Rule,\n lines: string[],\n isDelta: boolean,\n boundary: number,\n): RuleModel {\n const children = [...rule.children].sort((a, b) => nodeLine(a) - nodeLine(b))\n const firstChild = children[0] ? tagStartLine(lines, children[0]) : lines.length + 1\n const tags = rule.tags.map((tag) => tag.name)\n const operationTags = tags.filter((tag) => OPERATION_TAGS.has(tag))\n let operation: Operation | undefined\n if (isDelta) {\n if (operationTags.length !== 1) {\n throw new ConversionError(\n `Delta Rule \"${rule.name}\" must have exactly one @openspec operation tag`,\n )\n }\n operation = OPERATION_TAGS.get(operationTags[0])\n } else if (operationTags.length) {\n throw new ConversionError(\n `Main-spec Rule \"${rule.name}\" cannot use OpenSpec delta operation tags`,\n )\n }\n const description = proseBetween(\n lines,\n rule.location.line + 1,\n children.length ? firstChild : boundary,\n )\n const mapped: RuleModel = {\n name: rule.name.trim(),\n description,\n tags: tags.filter((tag) => !OPERATION_TAGS.has(tag)),\n operation,\n rename: null,\n background: null,\n scenarios: [],\n }\n for (const child of children) {\n if (child.background) mapped.background = mapMdgBackground(child.background, lines, children)\n if (child.scenario) mapped.scenarios.push(mapMdgScenario(child.scenario, lines))\n }\n if (operation === 'RENAMED') {\n const from = description.match(/^FROM:\\s*`### Requirement:\\s*(.+?)`\\s*$/im)?.[1]\n const to = description.match(/^TO:\\s*`### Requirement:\\s*(.+?)`\\s*$/im)?.[1]\n if (!from || !to)\n throw new ConversionError(`RENAMED Rule \"${rule.name}\" requires FROM and TO lines`)\n const extra = description\n .replace(/^FROM:.*$/gim, '')\n .replace(/^TO:.*$/gim, '')\n .trim()\n if (extra || mapped.background || mapped.scenarios.length)\n throw new ConversionError('RENAMED Rule contains unsupported content')\n mapped.rename = { from: from.trim(), to: to.trim() }\n mapped.description = ''\n }\n return mapped\n}\n\nexport function mapMdgBackground(\n background: Background,\n lines: string[],\n siblings: LocatedChild[],\n): BackgroundModel {\n const firstStep =\n background.steps[0]?.location.line ||\n nextSiblingLine(background.location.line, siblings, lines.length + 1)\n return {\n name: background.name.trim(),\n description: proseBetween(lines, background.location.line + 1, firstStep),\n steps: background.steps.map(mapMdgStep),\n }\n}\n\nexport function mapMdgScenario(scenario: Scenario, lines: string[]): ScenarioModel {\n const firstContent = Math.min(\n scenario.steps[0]?.location.line || Infinity,\n scenario.examples[0] ? tagStartLine(lines, { examples: scenario.examples[0] }) : Infinity,\n )\n return {\n kind: /outline/i.test(scenario.keyword) ? 'outline' : 'scenario',\n name: scenario.name.trim(),\n description: proseBetween(\n lines,\n scenario.location.line + 1,\n Number.isFinite(firstContent) ? firstContent : lines.length + 1,\n ),\n tags: scenario.tags.map((tag) => tag.name),\n steps: scenario.steps.map(mapMdgStep),\n examples: scenario.examples.map((examples) => mapMdgExamples(examples, lines)),\n }\n}\n\nexport function mapMdgExamples(examples: Examples, lines: string[]): ExamplesModel {\n const tableLine = examples.tableHeader?.location.line || lines.length + 1\n const rows = []\n if (examples.tableHeader) rows.push(examples.tableHeader.cells.map((cell) => cell.value))\n rows.push(...examples.tableBody.map((row) => row.cells.map((cell) => cell.value)))\n return {\n name: examples.name.trim(),\n description: proseBetween(lines, examples.location.line + 1, tableLine),\n tags: examples.tags.map((tag) => tag.name),\n rows,\n }\n}\n\nexport function mapMdgStep(step: Step): StepModel {\n return {\n keyword: step.keyword.trim().toUpperCase(),\n text: step.text,\n table: step.dataTable\n ? step.dataTable.rows.map((row) => row.cells.map((cell) => cell.value))\n : null,\n docString: step.docString\n ? { mediaType: step.docString.mediaType || '', content: step.docString.content }\n : null,\n }\n}\n","import type { GherkinDocument, Rule } from '@cucumber/messages'\nimport { DELTA_TAG, OPERATION_TAGS } from '../constants.js'\nimport { ConversionError } from '../errors.js'\nimport type { BackgroundModel, DocumentModel, RuleModel, ScenarioModel } from '../model.js'\nimport { trimBlank } from '../syntax.js'\nimport { mapMdgStep } from './mdg.js'\n\ntype Background = NonNullable<Rule['children'][number]['background']>\ntype Scenario = NonNullable<Rule['children'][number]['scenario']>\n\nfunction description(value: { description: string; location: { column?: number } }): string {\n const prefix = ' '.repeat((value.location.column ?? 1) + 1)\n return trimBlank(\n value.description\n .split('\\n')\n .map((line) => (line.startsWith(prefix) ? line.slice(prefix.length) : line)),\n ).join('\\n')\n}\n\nfunction background(value: Background): BackgroundModel {\n return {\n name: value.name.trim(),\n description: description(value),\n steps: value.steps.map(mapMdgStep),\n }\n}\nfunction scenario(value: Scenario): ScenarioModel {\n return {\n ...background(value),\n kind: /outline/i.test(value.keyword) ? 'outline' : 'scenario',\n tags: value.tags.map((tag) => tag.name),\n examples: value.examples.map((group) => ({\n name: group.name.trim(),\n description: description(group),\n tags: group.tags.map((tag) => tag.name),\n rows: [\n ...(group.tableHeader ? [group.tableHeader.cells.map((cell) => cell.value)] : []),\n ...group.tableBody.map((row) => row.cells.map((cell) => cell.value)),\n ],\n })),\n }\n}\nexport function parseFeatureModel(document: GherkinDocument): DocumentModel {\n const feature = document.feature\n if (!feature) throw new ConversionError('Gherkin document has no Feature')\n const tags = feature.tags.map((tag) => tag.name)\n const model: DocumentModel = {\n name: feature.name.trim(),\n description: description(feature),\n kind: tags.includes(DELTA_TAG) ? 'delta' : 'main',\n tags: tags.filter((tag) => tag !== DELTA_TAG),\n background: null,\n featureScenarios: [],\n rules: [],\n }\n for (const child of feature.children) {\n if (child.background) model.background = background(child.background)\n if (child.scenario) model.featureScenarios.push(scenario(child.scenario))\n if (child.rule) {\n const value = child.rule\n const tags = value.tags.map((tag) => tag.name)\n const operationTag = tags.find((tag) => OPERATION_TAGS.has(tag))\n const rule: RuleModel = {\n name: value.name.trim(),\n description: description(value),\n tags: tags.filter((tag) => !OPERATION_TAGS.has(tag)),\n operation: operationTag ? OPERATION_TAGS.get(operationTag) : undefined,\n rename: null,\n background: null,\n scenarios: [],\n }\n for (const item of value.children) {\n if (item.background) rule.background = background(item.background)\n if (item.scenario) rule.scenarios.push(scenario(item.scenario))\n }\n if (rule.operation === 'RENAMED') {\n const from = rule.description.match(/^FROM:\\s*### Requirement:\\s*(.+)$/m)?.[1]\n const to = rule.description.match(/^TO:\\s*### Requirement:\\s*(.+)$/m)?.[1]\n if (!from || !to) throw new ConversionError('RENAMED Rule requires FROM and TO')\n rule.rename = { from: from.trim(), to: to.trim() }\n rule.description = ''\n }\n model.rules.push(rule)\n }\n }\n return model\n}\n","import { DELTA_TAG, SYNTHETIC_TAG } from '../constants.js'\nimport { ConversionError } from '../errors.js'\nimport type {\n BackgroundModel,\n DocumentKind,\n DocumentModel,\n Operation,\n RuleModel,\n ScenarioModel,\n StepModel,\n} from '../model.js'\nimport type { Heading } from '../syntax.js'\nimport {\n fenceMask,\n findHeading,\n findSection,\n findSectionOptional,\n headingsInRange,\n normalizeLines,\n parseTable,\n precedingTags,\n sectionContent,\n tagBlockStart,\n trimBlank,\n} from '../syntax.js'\n\nexport function detectOpenSpecKind(text: string): DocumentKind {\n const lines = text\n .replace(/^\\uFEFF/, '')\n .replace(/\\r\\n?/g, '\\n')\n .split('\\n')\n const mask = fenceMask(lines)\n const main = lines.some((line, index) => !mask[index] && /^##\\s+Requirements\\s*$/i.test(line))\n const delta = lines.some(\n (line, index) =>\n !mask[index] && /^##\\s+(ADDED|MODIFIED|REMOVED|RENAMED) Requirements\\s*$/i.test(line),\n )\n if (main && delta)\n throw new ConversionError('OpenSpec input mixes main and delta requirement sections')\n if (!main && !delta)\n throw new ConversionError('OpenSpec input has no Requirements or delta sections')\n return delta ? 'delta' : 'main'\n}\n\nexport function parseOpenSpecModel(\n text: string,\n { kind, name }: { kind: DocumentKind; name: string },\n): DocumentModel {\n const lines = normalizeLines(text)\n const mask = fenceMask(lines)\n const h1 = findHeading(lines, mask, 1)\n const title = h1 ? h1.title : name\n const tags = h1 ? precedingTags(lines, h1.index) : []\n const purpose = sectionContent(lines, mask, 'Purpose')\n const model: DocumentModel = {\n kind,\n name: title,\n description: purpose?.trim() || '',\n tags: tags.filter((tag) => tag !== DELTA_TAG),\n background: null,\n featureScenarios: [],\n rules: [],\n }\n\n if (kind === 'main') parseMainOpenSpec(lines, mask, model)\n else parseDeltaOpenSpec(lines, mask, model)\n return model\n}\n\nexport function parseMainOpenSpec(lines: string[], mask: boolean[], model: DocumentModel): void {\n const section = findSection(lines, mask, 'Requirements')\n const requirements = headingsInRange(lines, mask, 3, section.start + 1, section.end).filter(\n (heading) => /^Requirement:\\s*/i.test(heading.title),\n )\n const firstRequirementStart = requirements[0]\n ? tagBlockStart(lines, requirements[0].index)\n : section.end\n const preamble = trimBlank(lines.slice(section.start + 1, firstRequirementStart))\n if (preamble.length) model.background = parseOpenSpecBackground(preamble, 'feature')\n\n for (let index = 0; index < requirements.length; index += 1) {\n const heading = requirements[index]\n const end =\n index + 1 < requirements.length\n ? tagBlockStart(lines, requirements[index + 1].index)\n : section.end\n const rule = parseOpenSpecRequirement(lines, mask, heading, end)\n if (rule.tags.includes(SYNTHETIC_TAG)) {\n rule.tags = rule.tags.filter((tag) => tag !== SYNTHETIC_TAG)\n if (rule.background && !model.background) model.background = rule.background\n model.featureScenarios.push(...rule.scenarios)\n } else {\n model.rules.push(rule)\n }\n }\n}\n\nexport function parseDeltaOpenSpec(lines: string[], mask: boolean[], model: DocumentModel): void {\n model.tags = model.tags.filter((tag) => tag !== DELTA_TAG)\n const operations = headingsInRange(lines, mask, 2, 0, lines.length).flatMap((heading) => {\n const match = heading.title.match(/^(ADDED|MODIFIED|REMOVED|RENAMED) Requirements$/i)\n return match ? [match[1].toUpperCase() as Operation] : []\n })\n if (new Set(operations).size !== operations.length)\n throw new ConversionError('Repeated delta operation section is unsupported')\n for (const operation of operations) {\n const section = findSectionOptional(lines, mask, `${operation} Requirements`)\n if (!section) continue\n if (operation === 'RENAMED') {\n for (let index = section.start + 1; index < section.end; index += 1) {\n const from = lines[index].match(/^\\s*-?\\s*FROM:\\s*`?### Requirement:\\s*(.+?)`?\\s*$/i)\n if (!from) continue\n let toIndex = index + 1\n while (toIndex < section.end && !lines[toIndex].trim()) toIndex += 1\n const to = lines[toIndex]?.match(/^\\s*-?\\s*TO:\\s*`?### Requirement:\\s*(.+?)`?\\s*$/i)\n if (!to) throw new ConversionError(`RENAMED entry for \"${from[1].trim()}\" has no TO line`)\n model.rules.push({\n name: 'Rename requirement',\n description: '',\n tags: precedingTags(lines, index),\n operation,\n rename: { from: from[1].trim(), to: to[1].trim() },\n background: null,\n scenarios: [],\n })\n index = toIndex\n }\n continue\n }\n const headings = headingsInRange(lines, mask, 3, section.start + 1, section.end).filter(\n (heading) => /^Requirement:\\s*/i.test(heading.title),\n )\n for (let index = 0; index < headings.length; index += 1) {\n const heading = headings[index]\n const end =\n index + 1 < headings.length ? tagBlockStart(lines, headings[index + 1].index) : section.end\n const rule = parseOpenSpecRequirement(lines, mask, heading, end)\n rule.operation = operation\n model.rules.push(rule)\n }\n }\n}\n\nexport function parseOpenSpecRequirement(\n lines: string[],\n mask: boolean[],\n heading: Heading,\n end: number,\n): RuleModel {\n const childHeadings = headingsInRange(lines, mask, 4, heading.index + 1, end).filter((item) =>\n /^(Scenario(?: Outline)?|Background):/i.test(item.title),\n )\n const firstChild = childHeadings[0] ? tagBlockStart(lines, childHeadings[0].index) : end\n const headerBody = trimBlank(lines.slice(heading.index + 1, firstChild))\n let background = null\n let descriptionLines = headerBody\n const marker = headerBody.findIndex((line) => /^\\*\\*Background:/i.test(line))\n if (marker >= 0) {\n descriptionLines = trimBlank(headerBody.slice(0, marker))\n background = parseOpenSpecBackground(headerBody.slice(marker), 'rule')\n }\n const rule: RuleModel = {\n name: heading.title.replace(/^Requirement:\\s*/i, '').trim(),\n description: descriptionLines.join('\\n').trim(),\n tags: precedingTags(lines, heading.index),\n operation: undefined,\n rename: null,\n background,\n scenarios: [],\n }\n for (let index = 0; index < childHeadings.length; index += 1) {\n const child = childHeadings[index]\n const childEnd =\n index + 1 < childHeadings.length ? tagBlockStart(lines, childHeadings[index + 1].index) : end\n if (/^Background:/i.test(child.title))\n rule.background = parseOpenSpecScenario(lines, mask, child, childEnd, true)\n else\n rule.scenarios.push(\n parseOpenSpecScenario(lines, mask, child, childEnd, false) as ScenarioModel,\n )\n }\n return rule\n}\n\nexport function parseOpenSpecBackground(lines: string[], scope: string): BackgroundModel {\n const marker = lines[0]?.match(/^\\*\\*Background:(.*?)\\*\\*\\s*$/i)\n if (!marker)\n throw new ConversionError(\n `Unsupported ${scope} Requirements preamble; expected **Background:**`,\n )\n const parsed = parseOpenSpecBody(lines.slice(1))\n return { name: marker[1].trim(), description: parsed.description, steps: parsed.steps }\n}\n\nexport function parseOpenSpecScenario(\n lines: string[],\n mask: boolean[],\n heading: Heading,\n end: number,\n background: boolean,\n): ScenarioModel | BackgroundModel {\n const examplesHeadings = headingsInRange(lines, mask, 5, heading.index + 1, end).filter((item) =>\n /^Examples:/i.test(item.title),\n )\n const bodyEnd = examplesHeadings[0] ? tagBlockStart(lines, examplesHeadings[0].index) : end\n const body = parseOpenSpecBody(lines.slice(heading.index + 1, bodyEnd))\n if (background)\n return {\n name: heading.title.replace(/^Background:\\s*/i, '').trim(),\n description: body.description,\n steps: body.steps,\n }\n const scenario: ScenarioModel = {\n kind: /^Scenario Outline:/i.test(heading.title) ? 'outline' : 'scenario',\n name: heading.title.replace(/^Scenario(?: Outline)?:\\s*/i, '').trim(),\n description: body.description,\n tags: precedingTags(lines, heading.index),\n steps: body.steps,\n examples: [],\n }\n for (let index = 0; index < examplesHeadings.length; index += 1) {\n const example = examplesHeadings[index]\n const exampleEnd =\n index + 1 < examplesHeadings.length\n ? tagBlockStart(lines, examplesHeadings[index + 1].index)\n : end\n const exampleLines = trimBlank(lines.slice(example.index + 1, exampleEnd))\n const tableIndex = exampleLines.findIndex((line) => /^\\s{2,}\\|/.test(line))\n if (tableIndex < 0) throw new ConversionError(`Examples \"${example.title}\" has no table`)\n scenario.examples.push({\n name: example.title.replace(/^Examples:\\s*/i, '').trim(),\n description: trimBlank(exampleLines.slice(0, tableIndex)).join('\\n'),\n tags: precedingTags(lines, example.index),\n rows: parseTable(exampleLines.slice(tableIndex)),\n })\n }\n return scenario\n}\n\nexport function parseOpenSpecBody(rawLines: string[]): { description: string; steps: StepModel[] } {\n const lines = trimBlank(rawLines)\n const firstStep = lines.findIndex((line) =>\n /^\\s*[-*+]\\s+\\*\\*(GIVEN|WHEN|THEN|AND|BUT)\\*\\*/i.test(line),\n )\n const description =\n firstStep < 0 ? lines.join('\\n').trim() : trimBlank(lines.slice(0, firstStep)).join('\\n')\n const steps: StepModel[] = []\n if (firstStep < 0) return { description, steps }\n let index = firstStep\n while (index < lines.length) {\n if (!lines[index].trim()) {\n index += 1\n continue\n }\n const match = lines[index].match(/^\\s*[-*+]\\s+\\*\\*(GIVEN|WHEN|THEN|AND|BUT)\\*\\*\\s+(.+)$/i)\n if (!match) throw new ConversionError(`Unsupported OpenSpec scenario line: ${lines[index]}`)\n const step: StepModel = {\n keyword: match[1].toUpperCase(),\n text: match[2],\n table: null,\n docString: null,\n }\n index += 1\n while (index < lines.length && !lines[index].trim()) index += 1\n if (index < lines.length && /^\\s*```/.test(lines[index])) {\n const opener = /^(\\s*)```(.*)$/.exec(lines[index])\n if (!opener) throw new ConversionError('Invalid DocString opener')\n const indent = opener[1].length\n const content = []\n index += 1\n while (index < lines.length && !/^\\s*```\\s*$/.test(lines[index])) {\n content.push(lines[index].slice(Math.min(indent, lines[index].length)))\n index += 1\n }\n if (index >= lines.length) throw new ConversionError('Unclosed OpenSpec DocString')\n index += 1\n step.docString = { mediaType: opener[2].trim(), content: content.join('\\n') }\n } else if (index < lines.length && /^\\s{2,}\\|/.test(lines[index])) {\n const tableLines = []\n while (index < lines.length && /^\\s{2,}\\|/.test(lines[index])) tableLines.push(lines[index++])\n step.table = parseTable(tableLines)\n }\n steps.push(step)\n }\n return { description, steps }\n}\n","import { DELTA_TAG, OPERATION_TAGS } from '../constants.js'\nimport type {\n BackgroundModel,\n DocumentModel,\n RuleModel,\n ScenarioModel,\n StepModel,\n} from '../model.js'\nimport { escapeCell, titleCase, trimBlank } from '../syntax.js'\n\nexport function serializeFeature(model: DocumentModel): string {\n const output: string[] = []\n const featureTags = model.kind === 'delta' ? [...model.tags, DELTA_TAG] : model.tags\n pushFeatureTags(output, featureTags, 0)\n output.push(`Feature: ${model.name}`)\n pushFeatureDescription(output, model.description, 2)\n if (model.background) serializeFeatureBackground(output, model.background, 2)\n for (const scenario of model.featureScenarios) serializeFeatureScenario(output, scenario, 2)\n for (const rule of model.rules) serializeFeatureRule(output, rule)\n return `${trimBlank(output).join('\\n')}\\n`\n}\n\nexport function serializeFeatureRule(output: string[], rule: RuleModel): void {\n const operationTag = rule.operation\n ? [...OPERATION_TAGS].find(([, operation]) => operation === rule.operation)?.[0]\n : null\n output.push('')\n pushFeatureTags(output, [...rule.tags, ...(operationTag ? [operationTag] : [])], 2)\n output.push(` Rule: ${rule.operation === 'RENAMED' ? 'Rename requirement' : rule.name}`)\n if (rule.operation === 'RENAMED') {\n pushFeatureDescription(\n output,\n `FROM: ### Requirement: ${rule.rename?.from}\\nTO: ### Requirement: ${rule.rename?.to}`,\n 4,\n )\n return\n }\n pushFeatureDescription(output, rule.description, 4)\n if (rule.background) serializeFeatureBackground(output, rule.background, 4)\n for (const scenario of rule.scenarios) serializeFeatureScenario(output, scenario, 4)\n}\n\nexport function serializeFeatureBackground(\n output: string[],\n background: BackgroundModel,\n indent: number,\n): void {\n output.push('', `${' '.repeat(indent)}Background:${background.name ? ` ${background.name}` : ''}`)\n pushFeatureDescription(output, background.description, indent + 2)\n pushFeatureSteps(output, background.steps, indent + 2)\n}\n\nexport function serializeFeatureScenario(\n output: string[],\n scenario: ScenarioModel,\n indent: number,\n): void {\n output.push('')\n pushFeatureTags(output, scenario.tags, indent)\n output.push(\n `${' '.repeat(indent)}${scenario.kind === 'outline' ? 'Scenario Outline' : 'Scenario'}: ${scenario.name}`,\n )\n pushFeatureDescription(output, scenario.description, indent + 2)\n pushFeatureSteps(output, scenario.steps, indent + 2)\n for (const examples of scenario.examples) {\n output.push('')\n pushFeatureTags(output, examples.tags, indent + 2)\n output.push(`${' '.repeat(indent + 2)}Examples: ${examples.name}`)\n pushFeatureDescription(output, examples.description, indent + 4)\n pushFeatureTable(output, examples.rows, indent + 4)\n }\n}\n\nexport function pushFeatureSteps(output: string[], steps: StepModel[], indent: number): void {\n const prefix = ' '.repeat(indent)\n for (const step of steps) {\n output.push(`${prefix}${titleCase(step.keyword)} ${step.text}`)\n if (step.table) pushFeatureTable(output, step.table, indent + 2)\n if (step.docString) {\n const fence = step.docString.content.split('\\n').some((line) => line.trim() === '\"\"\"')\n ? '```'\n : '\"\"\"'\n output.push(`${' '.repeat(indent + 2)}${fence}${step.docString.mediaType || ''}`)\n for (const line of step.docString.content.split('\\n'))\n output.push(`${' '.repeat(indent + 2)}${line}`)\n output.push(`${' '.repeat(indent + 2)}${fence}`)\n }\n }\n}\n\nexport function pushFeatureTable(output: string[], rows: string[][], indent: number): void {\n if (!rows?.length) return\n const prefix = ' '.repeat(indent)\n for (const row of rows) output.push(`${prefix}| ${row.map(escapeCell).join(' | ')} |`)\n}\n\nexport function pushFeatureDescription(\n output: string[],\n description: string,\n indent: number,\n): void {\n if (!description) return\n const prefix = ' '.repeat(indent)\n output.push('')\n for (const line of description.split('\\n')) output.push(line ? `${prefix}${line}` : '')\n}\n\nexport function pushFeatureTags(output: string[], tags: string[], indent: number): void {\n if (tags?.length) output.push(`${' '.repeat(indent)}${tags.join(' ')}`)\n}\n","import { DELTA_TAG, OPERATION_TAGS } from '../constants.js'\nimport type { BackgroundModel, DocumentModel, RuleModel, ScenarioModel } from '../model.js'\nimport { pushDescriptionAndSteps, pushTable, pushTags, trimBlank } from '../syntax.js'\n\nexport function serializeMdg(model: DocumentModel): string {\n const output: string[] = []\n const featureTags = model.kind === 'delta' ? [...model.tags, DELTA_TAG] : model.tags\n pushTags(output, featureTags)\n output.push(`# Feature: ${model.name}`, '')\n if (model.description) output.push(model.description, '')\n if (model.background) serializeMdgBackground(output, model.background, 2)\n for (const scenario of model.featureScenarios) serializeMdgScenario(output, scenario, 3)\n for (const rule of model.rules) serializeMdgRule(output, rule)\n return `${trimBlank(output).join('\\n')}\\n`\n}\n\nexport function serializeMdgRule(output: string[], rule: RuleModel): void {\n const operationTag = rule.operation\n ? [...OPERATION_TAGS].find(([, operation]) => operation === rule.operation)?.[0]\n : null\n pushTags(output, [...rule.tags, ...(operationTag ? [operationTag] : [])])\n output.push(`## Rule: ${rule.operation === 'RENAMED' ? 'Rename requirement' : rule.name}`, '')\n if (rule.operation === 'RENAMED') {\n output.push(\n `FROM: \\`### Requirement: ${rule.rename?.from}\\``,\n `TO: \\`### Requirement: ${rule.rename?.to}\\``,\n '',\n )\n return\n }\n if (rule.description) output.push(rule.description, '')\n if (rule.background) serializeMdgBackground(output, rule.background, 3)\n for (const scenario of rule.scenarios) serializeMdgScenario(output, scenario, 3)\n}\n\nexport function serializeMdgBackground(\n output: string[],\n background: BackgroundModel,\n level: number,\n): void {\n output.push(`${'#'.repeat(level)} Background:${background.name ? ` ${background.name}` : ''}`, '')\n pushDescriptionAndSteps(output, background, true)\n}\n\nexport function serializeMdgScenario(\n output: string[],\n scenario: ScenarioModel,\n level: number,\n): void {\n pushTags(output, scenario.tags)\n output.push(\n `${'#'.repeat(level)} ${scenario.kind === 'outline' ? 'Scenario Outline' : 'Scenario'}: ${scenario.name}`,\n '',\n )\n pushDescriptionAndSteps(output, scenario, true)\n for (const examples of scenario.examples) {\n pushTags(output, examples.tags)\n output.push(`${'#'.repeat(level + 1)} Examples: ${examples.name}`, '')\n if (examples.description) output.push(examples.description, '')\n pushTable(output, examples.rows, true, false)\n }\n}\n","import { DELTA_TAG, SYNTHETIC_TAG } from '../constants.js'\nimport type { DocumentModel, RuleModel, ScenarioModel } from '../model.js'\nimport { pushDescriptionAndSteps, pushTable, pushTags, trimBlank } from '../syntax.js'\n\nexport function serializeOpenSpec(model: DocumentModel): string {\n const output: string[] = []\n pushTags(output, model.tags)\n if (model.kind === 'delta') pushTags(output, [DELTA_TAG])\n output.push(`# ${model.name}`, '')\n if (model.description) output.push('## Purpose', '', model.description, '')\n if (model.kind === 'delta') serializeOpenSpecDelta(output, model)\n else serializeOpenSpecMain(output, model)\n return `${trimBlank(output).join('\\n')}\\n`\n}\n\nexport function serializeOpenSpecMain(output: string[], model: DocumentModel): void {\n output.push('## Requirements', '')\n if (model.background) {\n output.push(`**Background:${model.background.name ? ` ${model.background.name}` : ''}**`, '')\n pushDescriptionAndSteps(output, model.background)\n }\n if (model.featureScenarios.length) {\n pushTags(output, [SYNTHETIC_TAG])\n output.push(\n '### Requirement: Feature-level scenarios',\n '',\n 'The system SHALL exhibit the feature-level behavior defined by the following scenarios.',\n '',\n )\n for (const scenario of model.featureScenarios) serializeOpenSpecScenario(output, scenario)\n }\n for (const rule of model.rules) serializeOpenSpecRule(output, rule)\n}\n\nexport function serializeOpenSpecDelta(output: string[], model: DocumentModel): void {\n for (const operation of new Set(model.rules.map((rule) => rule.operation))) {\n const rules = model.rules.filter((rule) => rule.operation === operation)\n if (!rules.length) continue\n output.push(`## ${operation} Requirements`, '')\n if (operation === 'RENAMED') {\n for (const rule of rules) {\n pushTags(output, rule.tags)\n output.push(\n `- FROM: \\`### Requirement: ${rule.rename?.from}\\``,\n `- TO: \\`### Requirement: ${rule.rename?.to}\\``,\n '',\n )\n }\n } else if (operation === 'REMOVED') {\n for (const rule of rules) {\n pushTags(output, rule.tags)\n output.push(`### Requirement: ${rule.name}`, '')\n }\n } else {\n for (const rule of rules) serializeOpenSpecRule(output, rule)\n }\n }\n}\n\nexport function serializeOpenSpecRule(output: string[], rule: RuleModel): void {\n pushTags(output, rule.tags)\n output.push(`### Requirement: ${rule.name}`, '', rule.description, '')\n if (rule.background) {\n output.push(`#### Background: ${rule.background.name}`.trimEnd(), '')\n pushDescriptionAndSteps(output, rule.background)\n }\n for (const scenario of rule.scenarios) serializeOpenSpecScenario(output, scenario)\n}\n\nexport function serializeOpenSpecScenario(output: string[], scenario: ScenarioModel): void {\n pushTags(output, scenario.tags)\n output.push(\n `#### ${scenario.kind === 'outline' ? 'Scenario Outline' : 'Scenario'}: ${scenario.name}`,\n '',\n )\n pushDescriptionAndSteps(output, scenario)\n for (const examples of scenario.examples) {\n pushTags(output, examples.tags)\n output.push(`##### Examples: ${examples.name}`, '')\n if (examples.description) output.push(examples.description, '')\n pushTable(output, examples.rows)\n }\n}\n","import { RESERVED_PREFIXES } from '../constants.js'\nimport { ConversionError } from '../errors.js'\nimport type { DocumentModel } from '../model.js'\n\nexport function validateProfile(model: DocumentModel, sourceKind: string): void {\n if (!model.name) throw new ConversionError('Feature/spec name is required')\n if (!model.description && model.kind === 'main')\n throw new ConversionError('Feature Purpose/description is required')\n const allTags = [\n ...model.tags,\n ...model.rules.flatMap((rule) => [\n ...rule.tags,\n ...rule.scenarios.flatMap((scenario) => [\n ...scenario.tags,\n ...scenario.examples.flatMap((examples) => examples.tags),\n ]),\n ]),\n ...model.featureScenarios.flatMap((scenario) => scenario.tags),\n ]\n for (const tag of allTags) {\n if (RESERVED_PREFIXES.some((prefix) => tag.startsWith(prefix))) {\n throw new ConversionError(`Reserved tag is not allowed as user metadata: ${tag}`)\n }\n }\n if (model.kind === 'main') {\n if (!model.rules.length && !model.featureScenarios.length)\n throw new ConversionError('Feature has no behavior to convert')\n for (const rule of model.rules) {\n if (!/\\b(?:SHALL|MUST)\\b/.test(rule.description)) {\n throw new ConversionError(`Rule/Requirement \"${rule.name}\" must contain SHALL or MUST`)\n }\n if (!rule.scenarios.length && !rule.background) {\n throw new ConversionError(`Rule/Requirement \"${rule.name}\" has no scenarios or Background`)\n }\n }\n } else {\n if (model.background || model.featureScenarios.length)\n throw new ConversionError(\n 'Delta Features cannot contain feature-level Background or scenarios',\n )\n for (const rule of model.rules) {\n if (\n (rule.operation === 'ADDED' || rule.operation === 'MODIFIED') &&\n !/\\b(?:SHALL|MUST)\\b/.test(rule.description)\n ) {\n throw new ConversionError(\n `${rule.operation} Rule \"${rule.name}\" must contain SHALL or MUST`,\n )\n }\n if (\n (rule.operation === 'ADDED' || rule.operation === 'MODIFIED') &&\n !rule.scenarios.length &&\n !rule.background\n ) {\n throw new ConversionError(`${rule.operation} Rule \"${rule.name}\" requires behavior`)\n }\n if (\n rule.operation === 'REMOVED' &&\n (rule.description || rule.scenarios.length || rule.background)\n ) {\n throw new ConversionError(`REMOVED Rule \"${rule.name}\" must contain only its name`)\n }\n if (rule.operation === 'RENAMED' && !rule.rename)\n throw new ConversionError('RENAMED Rule requires FROM and TO')\n }\n }\n for (const scenario of [\n ...model.featureScenarios,\n ...model.rules.flatMap((rule) => rule.scenarios),\n ]) {\n if (!scenario.steps.length)\n throw new ConversionError(`Scenario \"${scenario.name}\" has no steps`)\n if (scenario.kind === 'outline' && !scenario.examples.length)\n throw new ConversionError(`Scenario Outline \"${scenario.name}\" has no Examples`)\n if (scenario.kind === 'scenario' && scenario.examples.length)\n throw new ConversionError(`Scenario \"${scenario.name}\" cannot contain Examples`)\n }\n if (sourceKind === 'openspec' && model.kind === 'delta' && !model.rules.length) {\n throw new ConversionError('Delta has no operations')\n }\n}\n","import { ConversionError } from './errors.js'\nimport type { ConversionOptions } from './model.js'\nimport { assertPreserved } from './model.js'\nimport { parseFeatureModel } from './parsers/feature.js'\nimport { parseMdgModel } from './parsers/mdg.js'\nimport { detectOpenSpecKind, parseOpenSpecModel } from './parsers/openspec.js'\nimport { serializeFeature } from './renderers/feature.js'\nimport { serializeMdg } from './renderers/mdg.js'\nimport { serializeOpenSpec } from './renderers/openspec.js'\nimport { resolveOpenSpecName, slugify } from './syntax.js'\nimport {\n gherkinDiagnostics,\n openSpecDiagnostics,\n parseAndValidateFeature,\n parseAndValidateMdg,\n validateOpenSpec,\n} from './validation/official.js'\nimport { validateProfile } from './validation/profile.js'\n\nexport async function mdgToOpsx(text: string, options: ConversionOptions = {}): Promise<string> {\n const source = parseAndValidateMdg(text, options.sourcePath)\n if (!source.valid || !source.document)\n throw new ConversionError('Invalid Markdown with Gherkin input', gherkinDiagnostics(source))\n const model = parseMdgModel(text, source.document)\n validateProfile(model, 'mdg')\n const output = serializeOpenSpec(model)\n const name = options.name || slugify(model.name)\n const report = await validateOpenSpec(output, {\n kind: model.kind,\n name,\n projectRoot: options.projectRoot,\n })\n if (!report.valid)\n throw new ConversionError(\n 'Generated OpenSpec failed official validation',\n openSpecDiagnostics(report),\n )\n assertPreserved(model, parseOpenSpecModel(output, { kind: model.kind, name }))\n return output\n}\n\nasync function openSpecModel(text: string, options: ConversionOptions) {\n const kind = detectOpenSpecKind(text)\n const name = resolveOpenSpecName(text, options)\n const report = await validateOpenSpec(text, { kind, name, projectRoot: options.projectRoot })\n if (!report.valid)\n throw new ConversionError('Invalid OpenSpec input', openSpecDiagnostics(report))\n const model = parseOpenSpecModel(text, { kind, name })\n validateProfile(model, 'openspec')\n return model\n}\n\nexport async function opsxToMdg(text: string, options: ConversionOptions = {}): Promise<string> {\n const model = await openSpecModel(text, options)\n const output = serializeMdg(model)\n const target = parseAndValidateMdg(output)\n if (!target.valid || !target.document)\n throw new ConversionError(\n 'Generated Markdown with Gherkin failed official validation',\n gherkinDiagnostics(target),\n )\n assertPreserved(model, parseMdgModel(output, target.document))\n return output\n}\n\nexport async function opsxToFeature(\n text: string,\n options: ConversionOptions = {},\n): Promise<string> {\n const model = await openSpecModel(text, options)\n const output = serializeFeature(model)\n const target = parseAndValidateFeature(output)\n if (!target.valid || !target.document)\n throw new ConversionError(\n 'Generated Gherkin feature failed official validation',\n gherkinDiagnostics(target),\n )\n assertPreserved(model, parseFeatureModel(target.document))\n return output\n}\n","import type { Readable } from 'node:stream'\nimport type { Direction } from '../model.js'\n\ninterface Options {\n force: boolean\n help: boolean\n text?: string\n input?: string\n output?: string\n name?: string\n projectRoot?: string\n}\n\nimport { link, mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises'\nimport { basename, dirname, resolve } from 'node:path'\nimport { mdgToOpsx, opsxToFeature, opsxToMdg } from '../convert.js'\nimport { ConversionError, isFsError } from '../errors.js'\n\nexport async function runConverterCli(\n direction: Direction,\n argv = process.argv.slice(2),\n): Promise<number> {\n try {\n const options = parseArgs(argv, direction)\n if (options.help) {\n process.stdout.write(usage(direction))\n return 0\n }\n const input = await readInput(options)\n const converters = {\n 'mdg-to-opsx': mdgToOpsx,\n 'opsx-to-mdg': opsxToMdg,\n 'opsx-to-feature': opsxToFeature,\n }\n const convert = converters[direction]\n const output = await convert(input.text, {\n sourcePath: input.path,\n name: options.name,\n projectRoot: options.projectRoot ? resolve(options.projectRoot) : undefined,\n })\n if (options.output) await writeAtomic(resolve(options.output), output, options.force)\n else process.stdout.write(output)\n return 0\n } catch (error) {\n if (error instanceof ConversionError) {\n process.stderr.write(`${error.message}\\n`)\n for (const diagnostic of error.diagnostics) process.stderr.write(`- ${diagnostic}\\n`)\n return 1\n }\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`)\n return 2\n }\n}\n\nfunction parseArgs(argv: string[], direction: Direction): Options {\n const options: Options = { force: false, help: false }\n for (let index = 0; index < argv.length; index += 1) {\n const argument = argv[index]\n if (argument === '--help' || argument === '-h') options.help = true\n else if (argument === '--force') options.force = true\n else if (argument === '--text') options.text = requiredValue(argv, ++index, argument)\n else if (argument === '--output' || argument === '-o')\n options.output = requiredValue(argv, ++index, argument)\n else if (argument === '--name') options.name = requiredValue(argv, ++index, argument)\n else if (argument === '--project-root')\n options.projectRoot = requiredValue(argv, ++index, argument)\n else if (argument.startsWith('-'))\n throw new Error(`Unknown option: ${argument}\\n${usage(direction)}`)\n else if (!options.input) options.input = argument\n else throw new Error(`Only one input path is allowed\\n${usage(direction)}`)\n }\n if (options.input && options.text !== undefined)\n throw new Error(`Use either an input path or --text, not both\\n${usage(direction)}`)\n if (options.force && !options.output) throw new Error('--force requires --output')\n return options\n}\n\nasync function readInput(options: Options) {\n if (options.text !== undefined) return { text: options.text, path: undefined }\n if (options.input) {\n const path = resolve(options.input)\n return { text: await readFile(path, 'utf8'), path }\n }\n if (process.stdin.isTTY) throw new Error('No input: pass a path, --text, or pipe text on stdin')\n return { text: await readStream(process.stdin), path: undefined }\n}\n\nasync function readStream(stream: Readable): Promise<string> {\n stream.setEncoding('utf8')\n let result = ''\n for await (const chunk of stream) result += chunk\n return result\n}\n\nexport async function writeAtomic(path: string, content: string, force: boolean): Promise<void> {\n await mkdir(dirname(path), { recursive: true })\n const temporary = resolve(dirname(path), `.${basename(path)}.${process.pid}.${Date.now()}.tmp`)\n await writeFile(temporary, content, { encoding: 'utf8', flag: 'wx' })\n try {\n if (force) await rename(temporary, path)\n else {\n try {\n await link(temporary, path)\n } catch (error) {\n if (isFsError(error, 'EEXIST'))\n throw new Error(`Output already exists: ${path} (use --force to replace it)`)\n throw error\n }\n await unlink(temporary)\n }\n } catch (error) {\n await unlink(temporary).catch(() => {})\n throw error\n }\n}\n\nfunction requiredValue(argv: string[], index: number, option: string): string {\n if (index >= argv.length) throw new Error(`Missing value for ${option}`)\n return argv[index]\n}\n\nfunction usage(direction: Direction): string {\n return `Usage: ${direction} [input] [--text content] [-o output] [--force] [--name name] [--project-root path]\\n`\n}\n"],"mappings":"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"}
@@ -1,85 +0,0 @@
1
- @code @authentication @security
2
- Feature: User sign-in
3
-
4
- Allow registered users to access their accounts securely while rejecting invalid credentials and limiting repeated attempts.
5
-
6
- Background:
7
- Given the authentication service is available
8
-
9
- @credentials
10
- Rule: Credentials are verified securely
11
-
12
- The system SHALL grant access only when an active registered user provides the correct email address and password.
13
-
14
- @smoke @positive
15
- Scenario Outline: Sign-in succeeds with correct credentials
16
- Given an active registered account with these credentials:
17
- | email | password |
18
- | <storedEmail> | <storedPassword> |
19
- And the account has <priorFailureCount> consecutive failed sign-in attempts
20
- When the user signs in with email "<submittedEmail>" and password "<submittedPassword>"
21
- Then the system grants access to the account with email "<expectedAccountEmail>"
22
- And the system starts an authenticated session
23
-
24
- Examples: Matching credentials
25
- | storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword | expectedAccountEmail |
26
- | user@example.com | correct-password | 0 | user@example.com | correct-password | user@example.com |
27
-
28
- @negative @api
29
- Scenario Outline: Sign-in rejects invalid credentials
30
- Given only the following active account is registered:
31
- | email | password |
32
- | <storedEmail> | <storedPassword> |
33
- And the account has <priorFailureCount> consecutive failed sign-in attempts
34
- When a user signs in with email "<submittedEmail>" and password "<submittedPassword>"
35
- Then the system rejects access with HTTP status <httpStatus>
36
- And the response body is:
37
- """json
38
- {
39
- "error": "<errorCode>"
40
- }
41
- """
42
-
43
- @unknown-account
44
- Examples: Unknown account
45
- | storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword | httpStatus | errorCode |
46
- | user@example.com | correct-password | 0 | missing@example.com | any-password | 401 | invalid_credentials |
47
-
48
- @incorrect-password
49
- Examples: Incorrect password
50
- | storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword | httpStatus | errorCode |
51
- | user@example.com | correct-password | 0 | user@example.com | wrong-password | 401 | invalid_credentials |
52
-
53
- @account-protection
54
- Rule: Repeated failures lock the account
55
-
56
- The system SHALL lock an account after five consecutive failed sign-in attempts. The system SHALL reject sign-in while the account is locked, even when the submitted credentials are correct.
57
-
58
- @negative @lockout
59
- Scenario Outline: Sign-in locks the account at the failure limit
60
- Given an active registered account with these credentials:
61
- | email | password |
62
- | <storedEmail> | <storedPassword> |
63
- And the account has <priorFailureCount> consecutive failed sign-in attempts
64
- When a user signs in with email "<submittedEmail>" and password "<submittedPassword>"
65
- Then the system rejects access
66
- And the account has <expectedFailureCount> consecutive failed sign-in attempts
67
- And the system locks the account
68
-
69
- Examples: Failure limit reached
70
- | storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword | expectedFailureCount |
71
- | user@example.com | correct-password | 4 | user@example.com | wrong-password | 5 |
72
-
73
- @negative @lockout
74
- Scenario Outline: A locked account rejects correct credentials
75
- Given a registered account with these credentials:
76
- | email | password |
77
- | <storedEmail> | <storedPassword> |
78
- And the account has <priorFailureCount> consecutive failed sign-in attempts
79
- But the account is locked
80
- When a user signs in with email "<submittedEmail>" and password "<submittedPassword>"
81
- Then the system rejects access
82
-
83
- Examples: Correct credentials while locked
84
- | storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword |
85
- | user@example.com | correct-password | 5 | user@example.com | correct-password |
@@ -1,96 +0,0 @@
1
- `@code` `@authentication` `@security`
2
- # Feature: User sign-in
3
-
4
- Allow registered users to access their accounts securely while rejecting invalid credentials and limiting repeated attempts.
5
-
6
- ## Background:
7
-
8
- * Given the authentication service is available
9
-
10
- `@credentials`
11
- ## Rule: Credentials are verified securely
12
-
13
- The system SHALL grant access only when an active registered user provides the correct email address and password.
14
-
15
- `@smoke` `@positive`
16
- ### Scenario Outline: Sign-in succeeds with correct credentials
17
-
18
- * Given an active registered account with these credentials:
19
- | email | password |
20
- | <storedEmail> | <storedPassword> |
21
- * And the account has <priorFailureCount> consecutive failed sign-in attempts
22
- * When the user signs in with email "<submittedEmail>" and password "<submittedPassword>"
23
- * Then the system grants access to the account with email "<expectedAccountEmail>"
24
- * And the system starts an authenticated session
25
-
26
- #### Examples: Matching credentials
27
-
28
- | storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword | expectedAccountEmail |
29
- | user@example.com | correct-password | 0 | user@example.com | correct-password | user@example.com |
30
-
31
- `@negative` `@api`
32
- ### Scenario Outline: Sign-in rejects invalid credentials
33
-
34
- * Given only the following active account is registered:
35
- | email | password |
36
- | <storedEmail> | <storedPassword> |
37
- * And the account has <priorFailureCount> consecutive failed sign-in attempts
38
- * When a user signs in with email "<submittedEmail>" and password "<submittedPassword>"
39
- * Then the system rejects access with HTTP status <httpStatus>
40
- * And the response body is:
41
-
42
- ```json
43
- {
44
- "error": "<errorCode>"
45
- }
46
- ```
47
-
48
- `@unknown-account`
49
- #### Examples: Unknown account
50
-
51
- | storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword | httpStatus | errorCode |
52
- | user@example.com | correct-password | 0 | missing@example.com | any-password | 401 | invalid_credentials |
53
-
54
- `@incorrect-password`
55
- #### Examples: Incorrect password
56
-
57
- | storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword | httpStatus | errorCode |
58
- | user@example.com | correct-password | 0 | user@example.com | wrong-password | 401 | invalid_credentials |
59
-
60
- `@account-protection`
61
- ## Rule: Repeated failures lock the account
62
-
63
- The system SHALL lock an account after five consecutive failed sign-in attempts. The system SHALL reject sign-in while the account is locked, even when the submitted credentials are correct.
64
-
65
- `@negative` `@lockout`
66
- ### Scenario Outline: Sign-in locks the account at the failure limit
67
-
68
- * Given an active registered account with these credentials:
69
- | email | password |
70
- | <storedEmail> | <storedPassword> |
71
- * And the account has <priorFailureCount> consecutive failed sign-in attempts
72
- * When a user signs in with email "<submittedEmail>" and password "<submittedPassword>"
73
- * Then the system rejects access
74
- * And the account has <expectedFailureCount> consecutive failed sign-in attempts
75
- * And the system locks the account
76
-
77
- #### Examples: Failure limit reached
78
-
79
- | storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword | expectedFailureCount |
80
- | user@example.com | correct-password | 4 | user@example.com | wrong-password | 5 |
81
-
82
- `@negative` `@lockout`
83
- ### Scenario Outline: A locked account rejects correct credentials
84
-
85
- * Given a registered account with these credentials:
86
- | email | password |
87
- | <storedEmail> | <storedPassword> |
88
- * And the account has <priorFailureCount> consecutive failed sign-in attempts
89
- * But the account is locked
90
- * When a user signs in with email "<submittedEmail>" and password "<submittedPassword>"
91
- * Then the system rejects access
92
-
93
- #### Examples: Correct credentials while locked
94
-
95
- | storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword |
96
- | user@example.com | correct-password | 5 | user@example.com | correct-password |
@@ -1,109 +0,0 @@
1
- `@code` `@authentication` `@security`
2
- # User sign-in
3
-
4
- ## Purpose
5
-
6
- Allow registered users to access their accounts securely while rejecting invalid credentials and limiting repeated attempts.
7
-
8
- ## Requirements
9
-
10
- **Background:**
11
-
12
- - **GIVEN** the authentication service is available
13
-
14
- `@credentials`
15
- ### Requirement: Credentials are verified securely
16
-
17
- The system SHALL grant access only when an active registered user provides the correct email address and password.
18
-
19
- `@smoke` `@positive`
20
- #### Scenario Outline: Sign-in succeeds with correct credentials
21
-
22
- - **GIVEN** an active registered account with these credentials:
23
- | email | password |
24
- | --- | --- |
25
- | <storedEmail> | <storedPassword> |
26
- - **AND** the account has <priorFailureCount> consecutive failed sign-in attempts
27
- - **WHEN** the user signs in with email "<submittedEmail>" and password "<submittedPassword>"
28
- - **THEN** the system grants access to the account with email "<expectedAccountEmail>"
29
- - **AND** the system starts an authenticated session
30
-
31
- ##### Examples: Matching credentials
32
-
33
- | storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword | expectedAccountEmail |
34
- | --- | --- | --- | --- | --- | --- |
35
- | user@example.com | correct-password | 0 | user@example.com | correct-password | user@example.com |
36
-
37
- `@negative` `@api`
38
- #### Scenario Outline: Sign-in rejects invalid credentials
39
-
40
- - **GIVEN** only the following active account is registered:
41
- | email | password |
42
- | --- | --- |
43
- | <storedEmail> | <storedPassword> |
44
- - **AND** the account has <priorFailureCount> consecutive failed sign-in attempts
45
- - **WHEN** a user signs in with email "<submittedEmail>" and password "<submittedPassword>"
46
- - **THEN** the system rejects access with HTTP status <httpStatus>
47
- - **AND** the response body is:
48
-
49
- ```json
50
- {
51
- "error": "<errorCode>"
52
- }
53
- ```
54
-
55
- `@unknown-account`
56
- ##### Examples: Unknown account
57
-
58
- | storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword | httpStatus | errorCode |
59
- | --- | --- | --- | --- | --- | --- | --- |
60
- | user@example.com | correct-password | 0 | missing@example.com | any-password | 401 | invalid_credentials |
61
-
62
- `@incorrect-password`
63
- ##### Examples: Incorrect password
64
-
65
- | storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword | httpStatus | errorCode |
66
- | --- | --- | --- | --- | --- | --- | --- |
67
- | user@example.com | correct-password | 0 | user@example.com | wrong-password | 401 | invalid_credentials |
68
-
69
- `@account-protection`
70
- ### Requirement: Repeated failures lock the account
71
-
72
- The system SHALL lock an account after five consecutive failed sign-in attempts. The system SHALL reject sign-in while the account is locked, even when the submitted credentials are correct.
73
-
74
- `@negative` `@lockout`
75
- #### Scenario Outline: Sign-in locks the account at the failure limit
76
-
77
- - **GIVEN** an active registered account with these credentials:
78
- | email | password |
79
- | --- | --- |
80
- | <storedEmail> | <storedPassword> |
81
- - **AND** the account has <priorFailureCount> consecutive failed sign-in attempts
82
- - **WHEN** a user signs in with email "<submittedEmail>" and password "<submittedPassword>"
83
- - **THEN** the system rejects access
84
- - **AND** the account has <expectedFailureCount> consecutive failed sign-in attempts
85
- - **AND** the system locks the account
86
-
87
- ##### Examples: Failure limit reached
88
-
89
- | storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword | expectedFailureCount |
90
- | --- | --- | --- | --- | --- | --- |
91
- | user@example.com | correct-password | 4 | user@example.com | wrong-password | 5 |
92
-
93
- `@negative` `@lockout`
94
- #### Scenario Outline: A locked account rejects correct credentials
95
-
96
- - **GIVEN** a registered account with these credentials:
97
- | email | password |
98
- | --- | --- |
99
- | <storedEmail> | <storedPassword> |
100
- - **AND** the account has <priorFailureCount> consecutive failed sign-in attempts
101
- - **BUT** the account is locked
102
- - **WHEN** a user signs in with email "<submittedEmail>" and password "<submittedPassword>"
103
- - **THEN** the system rejects access
104
-
105
- ##### Examples: Correct credentials while locked
106
-
107
- | storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword |
108
- | --- | --- | --- | --- | --- |
109
- | user@example.com | correct-password | 5 | user@example.com | correct-password |