5b2: make the pipe escape terminate, cap the value a document fault echoes
CI / gate (push) Successful in 9s

This commit is contained in:
2026-09-03 18:45:17 +02:00
parent 240ea8eb64
commit 2ff03a1396
5 changed files with 11 additions and 7 deletions
+4 -3
View File
@@ -91,9 +91,10 @@ Pre-1.0, normal 0.x rules.
The error surface is a contract too. `ConvertError` is `{ code, message, path, position? }` — the The error surface is a contract too. `ConvertError` is `{ code, message, path, position? }` — the
code from a closed list a consumer may switch exhaustively, the message free text, the path the code from a closed list a consumer may switch exhaustively, the message free text, the path the
node's place from the document root, the position where a parse read the refusal in its input. node's place from the document root, the position where a parse read the refusal in its input.
A message reads `rule: violation` — a rule alone states a truth the reader must invert before it A message names the violation, not the rule alone — a rule by itself states a truth the reader
reads as a failure — and where the flavour's claim refuses ordinary prose it names the escape that must invert before it reads as a failure — and where the flavour's claim refuses ordinary prose it
unclaims the form claimed: `\:::` or `\|` for a line, `\:` for an inline directive. names the escape that unclaims the form claimed: `\:::` for a directive line, `\|` for every pipe
row, `\:` for an inline directive.
Adding, removing or renaming a code is breaking, so a milestone meeting a new failure cause Adding, removing or renaming a code is breaking, so a milestone meeting a new failure cause
reuses a code where one fits; the list is complete at `0.1.0`. A code names the reuses a code where one fits; the list is complete at `0.1.0`. A code names the
cause; where one cause recurs across node types or across directions, one code covers them all and cause; where one cause recurs across node types or across directions, one code covers them all and
+2
View File
@@ -19,6 +19,8 @@ test('names the check anything that is not a doc node failed', () => {
assert.equal(fault(undefined), 'an ADF document is an object: found undefined') assert.equal(fault(undefined), 'an ADF document is an object: found undefined')
assert.equal(fault([]), 'an ADF document is an object: found an array') assert.equal(fault([]), 'an ADF document is an object: found an array')
assert.equal(fault('doc'), 'an ADF document is an object: found "doc"') assert.equal(fault('doc'), 'an ADF document is an object: found "doc"')
assert.equal(fault('x'.repeat(200000)), `an ADF document is an object: found "${'x'.repeat(40)}…"`)
assert.equal(fault(function named(first: number, second: number) { return first + second }), 'an ADF document is an object: found a function')
assert.equal(fault({ fields: { description: { type: 'doc', version: 1 } } }), 'an ADF document holds content, type and version alone: found the key fields') assert.equal(fault({ fields: { description: { type: 'doc', version: 1 } } }), 'an ADF document holds content, type and version alone: found the key fields')
assert.equal(fault({ extra: 1, type: 'doc', version: 1 }), 'an ADF document holds content, type and version alone: found the key extra') assert.equal(fault({ extra: 1, type: 'doc', version: 1 }), 'an ADF document holds content, type and version alone: found the key extra')
assert.equal(fault({ version: 1 }), 'an ADF document holds type "doc": found no type field') assert.equal(fault({ version: 1 }), 'an ADF document holds type "doc": found no type field')
+2 -1
View File
@@ -90,7 +90,8 @@ function isRecord(value: unknown): value is Record<string, unknown> {
} }
function describe(value: unknown): string { function describe(value: unknown): string {
if (typeof value === 'string') return JSON.stringify(value) if (typeof value === 'string') return JSON.stringify(value.length > 40 ? `${value.slice(0, 40)}` : value)
if (typeof value === 'function') return 'a function'
if (typeof value === 'object' && value !== null) return Array.isArray(value) ? 'an array' : 'an object' if (typeof value === 'object' && value !== null) return Array.isArray(value) ? 'an array' : 'an object'
return String(value) return String(value)
} }
+1 -1
View File
@@ -406,7 +406,7 @@ function pipeTableBlock(rows: readonly [string[], ...string[][]], position: Sour
return faultedBlock('a pipe table carries no column alignment ADF could hold: this delimiter row holds an alignment colon', position) return faultedBlock('a pipe table carries no column alignment ADF could hold: this delimiter row holds an alignment colon', position)
} }
if (delimiter === undefined || !delimiter.every(isPipeDelimiter)) { if (delimiter === undefined || !delimiter.every(isPipeDelimiter)) {
return faultedBlock('a pipe table underlines its header with a row of `-` runs: this one has none; \\| keeps the line literal text', position) return faultedBlock('a pipe table underlines its header with a row of `-` runs: this one has none; \\| at the start of every row keeps them literal text', position)
} }
const ragged = [delimiter, ...body].find((row) => row.length !== header.length) const ragged = [delimiter, ...body].find((row) => row.length !== header.length)
if (ragged !== undefined) return faultedBlock(`a pipe table row holds ${cellCount(ragged.length)} where its header holds ${cellCount(header.length)}`, position) if (ragged !== undefined) return faultedBlock(`a pipe table row holds ${cellCount(ragged.length)} where its header holds ${cellCount(header.length)}`, position)
+2 -2
View File
@@ -182,8 +182,8 @@ test('claims the line a pipe opens and gives the rest back to the block walk', (
}) })
test('names the pipe table a claimed line does not spell', () => { test('names the pipe table a claimed line does not spell', () => {
assert.equal(content(markdownToAdf('| a | b |\n')), 'malformed-pipe-table: a pipe table underlines its header with a row of `-` runs: this one has none; \\| keeps the line literal text') assert.equal(content(markdownToAdf('| a | b |\n')), 'malformed-pipe-table: a pipe table underlines its header with a row of `-` runs: this one has none; \\| at the start of every row keeps them literal text')
assert.equal(content(markdownToAdf('| a |\n| x |\n')), 'malformed-pipe-table: a pipe table underlines its header with a row of `-` runs: this one has none; \\| keeps the line literal text') assert.equal(content(markdownToAdf('| a |\n| x |\n')), 'malformed-pipe-table: a pipe table underlines its header with a row of `-` runs: this one has none; \\| at the start of every row keeps them literal text')
assert.equal(content(markdownToAdf('| a | b |\n| :--- | ---: |\n')), 'malformed-pipe-table: a pipe table carries no column alignment ADF could hold: this delimiter row holds an alignment colon') assert.equal(content(markdownToAdf('| a | b |\n| :--- | ---: |\n')), 'malformed-pipe-table: a pipe table carries no column alignment ADF could hold: this delimiter row holds an alignment colon')
assert.equal(content(markdownToAdf('| a | b |\n| --- |\n')), 'malformed-pipe-table: a pipe table row holds 1 cell where its header holds 2 cells') assert.equal(content(markdownToAdf('| a | b |\n| --- |\n')), 'malformed-pipe-table: a pipe table row holds 1 cell where its header holds 2 cells')
assert.equal(content(markdownToAdf('| a |\n| --- |\n| b | c |\n')), 'malformed-pipe-table: a pipe table row holds 2 cells where its header holds 1 cell') assert.equal(content(markdownToAdf('| a |\n| --- |\n| b | c |\n')), 'malformed-pipe-table: a pipe table row holds 2 cells where its header holds 1 cell')