@zaunt/zest 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,187 @@
1
+ import { t as quick_reference_default } from "./quick-reference-BHuN8iCv.mjs";
2
+ //#region src/core/internal/parse-result.d.ts
3
+ type ParseResult<T> = {
4
+ ok: true;
5
+ value: T;
6
+ } | {
7
+ ok: false;
8
+ message: string;
9
+ };
10
+ //#endregion
11
+ //#region src/core/codec.d.ts
12
+ interface Codec<T> {
13
+ parse(text: string): ParseResult<T>;
14
+ format(value: T): string;
15
+ }
16
+ //#endregion
17
+ //#region src/core/fact-state.d.ts
18
+ declare enum FactState {
19
+ NOT_USED = "NOT_USED",
20
+ NOT_CHECKED = "NOT_CHECKED",
21
+ SUCCESS = "SUCCESS",
22
+ FAILURE = "FAILURE"
23
+ }
24
+ //#endregion
25
+ //#region src/core/fact.d.ts
26
+ type FactMetadata = {
27
+ language?: string;
28
+ } & Record<string, string>;
29
+ declare class Fact {
30
+ private readonly _raw;
31
+ private readonly _metadata;
32
+ readonly kind = "fact";
33
+ private _state;
34
+ private _actualText;
35
+ constructor(_raw: string, _metadata?: FactMetadata);
36
+ get raw(): string;
37
+ get metadata(): FactMetadata;
38
+ get state(): FactState;
39
+ get actualText(): string | undefined;
40
+ private markChecked;
41
+ private assertNotYetChecked;
42
+ asString(): string;
43
+ asInt(): number;
44
+ as<T>(codec: Codec<T>): T;
45
+ assertEquals(actual: string): void;
46
+ assertEquals<T>(actual: T, codec: Codec<T>): void;
47
+ assertEqualsNoThrow(actual: string): void;
48
+ assertEqualsNoThrow<T>(actual: T, codec: Codec<T>): void;
49
+ }
50
+ //#endregion
51
+ //#region src/core/fact-list.d.ts
52
+ type FactDebugInfo = {
53
+ index: number;
54
+ raw: string;
55
+ };
56
+ declare class FactList {
57
+ private readonly _items;
58
+ [index: number]: Fact;
59
+ constructor(items: Fact[]);
60
+ get length(): number;
61
+ [Symbol.iterator](): Iterator<Fact>;
62
+ at(index: number): Fact;
63
+ map<T>(callback: (fact: Fact, index: number) => T): T[];
64
+ byLanguage(language: string): Fact[];
65
+ byLanguage(language: string, n: number): Fact;
66
+ debug(logToConsole?: boolean): FactDebugInfo[];
67
+ }
68
+ //#endregion
69
+ //#region src/core/row.d.ts
70
+ declare class Row {
71
+ private readonly _headers;
72
+ private readonly _facts;
73
+ constructor(_headers: string[], _facts: Fact[]);
74
+ fact(name: string): Fact;
75
+ value(name: string): string;
76
+ value<T>(name: string, codec: Codec<T>): T;
77
+ toObject(): Record<string, string>;
78
+ }
79
+ //#endregion
80
+ //#region src/core/table.d.ts
81
+ type TableEachRowOptions = {
82
+ expectedColumn?: string;
83
+ codecs?: Record<string, Codec<unknown>>;
84
+ result?: Codec<unknown>;
85
+ failFast?: boolean;
86
+ execute: (...facts: Fact[]) => unknown;
87
+ };
88
+ declare class Table implements Iterable<Row> {
89
+ private readonly _headers;
90
+ private readonly _rows;
91
+ readonly kind = "table";
92
+ private readonly _headerFacts;
93
+ private readonly _cellFacts;
94
+ constructor(_headers: string[], _rows: string[][]);
95
+ get headers(): readonly string[];
96
+ get columnCount(): number;
97
+ get rowCount(): number;
98
+ [Symbol.iterator](): Iterator<Row>;
99
+ row(index: number): Row;
100
+ cell(col: number, row: number): Fact;
101
+ eachRow(executeOrOptions: ((...facts: Fact[]) => unknown) | TableEachRowOptions): Promise<void>;
102
+ toMap(): Record<string, string>;
103
+ toMap(codecs: Record<string, Codec<unknown>>): Record<string, unknown>;
104
+ toRecords(): Record<string, string>[];
105
+ toRecords(codecs: Record<string, Codec<unknown>>): Record<string, unknown>[];
106
+ get state(): FactState;
107
+ /** @internal */
108
+ get headerFacts(): readonly Fact[];
109
+ /** @internal */
110
+ get cellFacts(): readonly (readonly Fact[])[];
111
+ private _createRow;
112
+ }
113
+ //#endregion
114
+ //#region src/core/exhibit.d.ts
115
+ type Exhibit = Fact | Table;
116
+ //#endregion
117
+ //#region src/core/context.d.ts
118
+ type SectionHeading = {
119
+ level: number;
120
+ text: string;
121
+ exhibitStartIndex: number;
122
+ exhibitEndIndex: number;
123
+ };
124
+ declare class Context {
125
+ readonly facts: FactList;
126
+ readonly tables: Table[];
127
+ readonly exhibits: Exhibit[];
128
+ private readonly _allExhibits;
129
+ private readonly _headings;
130
+ private readonly _scopeStart;
131
+ private readonly _scopeEnd;
132
+ constructor(allExhibits: Exhibit[], headings: SectionHeading[], scopeStart: number, scopeEnd: number);
133
+ get sectionNames(): string[];
134
+ get allSectionNames(): {
135
+ name: string;
136
+ level: number;
137
+ }[];
138
+ section(name: string | RegExp): Context;
139
+ debug(logToConsole?: boolean): FactDebugInfo[];
140
+ private _scopedHeadings;
141
+ private _directChildHeadings;
142
+ }
143
+ //#endregion
144
+ //#region src/integration/scenario-definitions.d.ts
145
+ type ExecuteArgs = Context;
146
+ type ScenarioDefinition = {
147
+ name?: string;
148
+ markdown: string;
149
+ result?: Codec<unknown>;
150
+ failFast?: boolean;
151
+ debug?: boolean;
152
+ execute: (args: ExecuteArgs) => unknown;
153
+ };
154
+ type EachRowDefinition = {
155
+ name?: string;
156
+ markdown: string;
157
+ codecs?: Record<string, Codec<unknown>>;
158
+ expectedColumn?: string;
159
+ result?: Codec<unknown>;
160
+ failFast?: boolean;
161
+ execute: (...facts: Fact[]) => unknown;
162
+ };
163
+ //#endregion
164
+ //#region src/integration/scenario-api.d.ts
165
+ type ScenarioFn = (definition: ScenarioDefinition) => void;
166
+ type EachRowFn = (definition: EachRowDefinition) => void;
167
+ //#endregion
168
+ //#region src/core/codecs.d.ts
169
+ declare const stringCodec: Codec<string>;
170
+ declare const intCodec: Codec<number>;
171
+ declare const floatCodec: (fractionDigits?: number) => Codec<number>;
172
+ declare const booleanCodec: (truthy?: string, falsy?: string, ignoreCase?: boolean) => Codec<boolean>;
173
+ declare const enumCodec: <T extends string>(values: readonly T[], ignoreCase?: boolean) => Codec<T>;
174
+ declare const mappedCodec: <T>(parseMap: Record<string, T>, formatMap: Map<T, string>, ignoreCase?: boolean) => Codec<T>;
175
+ //#endregion
176
+ //#region src/config/zest-config.d.ts
177
+ type ZestConfig = {
178
+ outputEnabled: boolean;
179
+ outputDir: string;
180
+ failFast: boolean;
181
+ };
182
+ declare const setConfig: (overrides: Partial<ZestConfig>) => void;
183
+ //#endregion
184
+ //#region src/integration/vitest/vitest-scenarios.d.ts
185
+ declare const scenario: ScenarioFn, eachRow: EachRowFn;
186
+ //#endregion
187
+ export { Context, type ExecuteArgs, type Exhibit, Fact, FactList, Row, Table, type ZestConfig, booleanCodec, eachRow, enumCodec, floatCodec, intCodec, mappedCodec, quick_reference_default as quickReference, scenario, setConfig, stringCodec };
package/dist/index.mjs ADDED
@@ -0,0 +1,118 @@
1
+ import{t as e}from"./quick-reference-BHuN8iCv.mjs";import t from"markdown-it";import{createHash as n}from"node:crypto";import{mkdirSync as r,writeFileSync as i}from"node:fs";import{join as a}from"node:path";import{test as o}from"vitest";const s={parse(e){return{ok:!0,value:e}},format(e){return e}},c={parse(e){let t=e.trim();if(!/^-?\d+$/.test(t))return{ok:!1,message:`Expected integer but got '${e}'`};let n=Number(t);return Number.isSafeInteger(n)?{ok:!0,value:n}:{ok:!1,message:`Expected safe integer but got '${e}'`}},format(e){return String(e)}},l=e=>({parse(e){let t=e.trim();if(t===``)return{ok:!1,message:`Expected number but got '${e}'`};let n=Number(t);return Number.isFinite(n)?{ok:!0,value:n}:{ok:!1,message:`Expected number but got '${e}'`}},format(t){return e===void 0?String(t):t.toFixed(e)}}),u=(e=`yes`,t=`no`,n=!0)=>({parse(r){let i=n?r.toLowerCase():r,a=n?e.toLowerCase():e,o=n?t.toLowerCase():t;return i===a?{ok:!0,value:!0}:i===o?{ok:!0,value:!1}:{ok:!1,message:`Expected '${e}' or '${t}' but got '${r}'`}},format(n){return n?e:t}}),d=(e,t=!0)=>({parse(n){let r=e.find(e=>t?e.toLowerCase()===n.toLowerCase():e===n);return r?{ok:!0,value:r}:{ok:!1,message:`Expected one of [${e.join(`, `)}] but got '${n}'`}},format(e){return e}}),f=(e,t,n=!0)=>({parse(t){let r=n?t.toLowerCase():t,i=Object.entries(e).map(([e,t])=>[n?e.toLowerCase():e,t]).find(([e])=>e===r);return i?{ok:!0,value:i[1]}:{ok:!1,message:`Expected one of [${Object.keys(e).join(`, `)}] but got '${t}'`}},format(e){let n=t.get(e);if(n===void 0)throw Error(`No format mapping found for value '${String(e)}'`);return n}});var p=class extends Error{expected;actual;constructor(e,t){super(`Expected '${e}' but got '${t}'`),this.expected=e,this.actual=t,this.name=`EqualityError`}};const m=e=>{let t=e.split(`
2
+ `),n=0;for(;n<t.length&&t[n].trim()===``;)n++;let r=t.length-1;for(;r>n&&t[r].trim()===``;)r--;return t.slice(n,r+1).join(`
3
+ `)};var h=class{_raw;_metadata;kind=`fact`;_state=`NOT_USED`;_actualText;constructor(e,t={}){this._raw=e,this._metadata=t,this._raw=m(e)}get raw(){return this._raw}get metadata(){return this._metadata}get state(){return this._state}get actualText(){return this._actualText}markChecked(){this._state===`NOT_USED`&&(this._state=`NOT_CHECKED`)}assertNotYetChecked(){if(this._state===`SUCCESS`||this._state===`FAILURE`)throw Error(`The fact has already been checked.`)}asString(){return this.markChecked(),this._raw}asInt(){return this.as(c)}as(e){this.markChecked();let t=e.parse(this._raw);if(!t.ok)throw Error(t.message);return t.value}assertEquals(e,t){this.assertNotYetChecked();let n=t??s,r=this.as(n),i=n.format(r),a=n.format(e);if(this._actualText=a,a===i)this._state=`SUCCESS`;else throw this._state=`FAILURE`,new p(i,a)}assertEqualsNoThrow(e,t){this.assertNotYetChecked();let n=t??s,r=this.as(n),i=n.format(r),a=n.format(e);this._actualText=a,this._state=a===i?`SUCCESS`:`FAILURE`}},g=class{_headers;_facts;constructor(e,t){this._headers=e,this._facts=t}fact(e){let t=this._headers.indexOf(e);if(t<0)throw Error(`Unknown column '${e}'.`);return this._facts[t]}value(e,t){let n=this.fact(e);return t?n.as(t):n.asString()}toObject(){let e={};for(let t=0;t<this._headers.length;t++)e[this._headers[t]]=this._facts[t].asString();return e}};const _=()=>`${typeof process<`u`?process.env.TMPDIR??process.env.TEMP??`/tmp`:`/tmp`}/zest-output`,v=(e,t)=>{if(typeof process>`u`)return t;let n=process.env[e];return n===void 0?t:n.toLowerCase()!==`false`&&n!==`0`},y=(e,t)=>typeof process>`u`?t:process.env[e]??t,b=e=>({outputEnabled:e?.outputEnabled??v(`ZEST_OUTPUT_ENABLED`,!0),outputDir:e?.outputDir??y(`ZEST_OUTPUT_DIR`,_()),failFast:e?.failFast??v(`ZEST_FAIL_FAST`,!1),...e});let x=b();const S=()=>x,C=e=>{x=b(e)},w=e=>e===void 0?S().failFast:e,T=async(e,t)=>{let n=w(t.failFast),r=t.result??s,i=t.expectedColumn??e.headers[e.headers.length-1],a=t.codecs??{},o=!1,c=[];for(let s=0;s<e.rowCount&&!o;s++){let l=e.row(s),u=[];for(let t of e.headers)u.push(l.fact(t));try{let e=await t.execute(...u);if(e!==void 0){let t=l.fact(i),o=a[i]??r;n?t.assertEquals(e,o):t.assertEqualsNoThrow(e,o)}}catch(e){(e instanceof p||e instanceof Error)&&(c.push(e),n&&(o=!0))}}if(c.length===1)throw c[0];if(c.length>1){let e=c[0],t=`Found ${c.length} failures. First: ${e.message}`,n=Error(t);throw e instanceof p&&Object.assign(n,{expected:e.expected,actual:e.actual}),n}};var E=class{_headers;_rows;kind=`table`;_headerFacts;_cellFacts;constructor(e,t){this._headers=e,this._rows=t,this._headerFacts=e.map(e=>new h(e)),this._cellFacts=t.map(e=>e.map(e=>new h(e)))}get headers(){return this._headers}get columnCount(){return this._headers.length}get rowCount(){return this._rows.length}[Symbol.iterator](){let e=0;return{next:()=>{if(e>=this._rows.length)return{done:!0,value:void 0};let t=this._createRow(e);return e+=1,{done:!1,value:t}}}}row(e){if(e<0||e>=this._rows.length)throw Error(`Row ${e} does not exist.`);return this._createRow(e)}cell(e,t){if(e<0||e>=this._headers.length)throw Error(`Column ${e} does not exist.`);if(t===0)return this._headerFacts[e];let n=t-1;if(n<0||n>=this._rows.length)throw Error(`Row ${t} does not exist.`);return this._cellFacts[n][e]}eachRow(e){let t=typeof e==`function`?{execute:e}:e;return T(this,t)}toMap(e){let t=/* @__PURE__ */ new Set;if(this._headers.length===2&&!e){let e={};for(let n of this._rows){let r=n[0];if(t.has(r))throw Error(`Duplicate key '${r}'.`);t.add(r),e[r]=n[1]}return e}if(this._headers.length===2&&e){let n=e[this._headers[1]],r={};for(let e of this._rows){let i=e[0];if(t.has(i))throw Error(`Duplicate key '${i}'.`);t.add(i);let a=e[1];if(n){let e=n.parse(a);if(!e.ok)throw Error(e.message);r[i]=e.value}else r[i]=a}return r}let n={};for(let r=0;r<this._rows.length;r++){let i=this._rows[r],a=i[0];if(t.has(a))throw Error(`Duplicate key '${a}'.`);t.add(a);let o={};for(let t=1;t<this._headers.length;t++){let n=this._headers[t],r=i[t]??``,a=e?.[n];if(a){let e=a.parse(r);if(!e.ok)throw Error(e.message);o[n]=e.value}else o[n]=r}n[a]=o}return n}toRecords(e){let t=[];for(let n of this._rows){let r={};for(let t=0;t<this._headers.length;t++){let i=this._headers[t],a=n[t]??``,o=e?.[i];if(o){let e=o.parse(a);if(!e.ok)throw Error(e.message);r[i]=e.value}else r[i]=a}t.push(r)}return t}get state(){let e=!1,t=!1,n=!1;for(let r of this._headerFacts){let i=r.state;i===`FAILURE`&&(e=!0),i===`NOT_CHECKED`&&(t=!0),i===`SUCCESS`&&(n=!0)}for(let r of this._cellFacts)for(let i of r){let r=i.state;r===`FAILURE`&&(e=!0),r===`NOT_CHECKED`&&(t=!0),r===`SUCCESS`&&(n=!0)}return e?`FAILURE`:n?`SUCCESS`:t?`NOT_CHECKED`:`NOT_USED`}get headerFacts(){return this._headerFacts}get cellFacts(){return this._cellFacts}_createRow(e){return new g(this._headers,this._cellFacts[e])}};const D=new t({html:!0}),O=/* @__PURE__ */ new Set([`div`,`section`,`aside`,`article`,`p`,`prose`]),k=e=>{let t=e.match(/\bclass\s*=\s*["']([^"']*)["']/i);return t?t[1].trim().split(/\s+/).includes(`prose`):!1},A=/<(\/)?([a-zA-Z0-9-]+)([^>]*)>/g,j=(e,t)=>{A.lastIndex=0;let n;for(;(n=A.exec(e))!=null;){let e=!!n[1],r=n[2].toLowerCase(),i=n[3];i.trimEnd().endsWith(`/`)||(t.proseTag===void 0?!e&&O.has(r)&&(r===`prose`||k(i))&&(t.proseTag=r,t.depth=1):r===t.proseTag&&(e?(--t.depth,t.depth<=0&&(t.proseTag=void 0,t.depth=0)):t.depth+=1))}},M=(e,t)=>{let n=[],r=[],i=t+1;for(;i<e.length&&e[i].type!==`thead_close`;)e[i].type===`th_open`&&(i++,i<e.length&&e[i].type===`inline`&&n.push(e[i].content.trim())),i++;for(i++;i<e.length&&e[i].type!==`tbody_close`;){if(e[i].type===`tr_open`){let t=[];for(i++;i<e.length&&e[i].type!==`tr_close`;)e[i].type===`td_open`&&(i++,i<e.length&&e[i].type===`inline`&&t.push(e[i].content.trim())),i++;r.push(t)}i++}for(i++;i<e.length&&e[i].type!==`table_close`;)i++;return{table:new E(n,r),endIndex:i}},N=/* @__PURE__ */ new Set([`code_inline`,`em_open`,`strong_open`]),P=(e,t)=>{let n=e.children??[],r=!1,i=[],a;for(let e=0;e<n.length;e++){let o=n[e];if(o.type===`code_inline`){if(r)throw Error(`Nested facts aren't supported yet.`);let e=new h(o.content),n=o.meta??{};n.fact=e,o.meta=n,t.push({kind:`fact`,fact:e,token:o});continue}if(o.type===`em_open`||o.type===`strong_open`){if(r)throw Error(`Nested facts aren't supported yet.`);r=!0,i=[],a=o;continue}if(o.type===`em_close`||o.type===`strong_close`){if(r){let e=new h(i.join(``)),n=a.meta??{};n.fact=e,a.meta=n;let s=o.meta??{};s.fact=e,o.meta=s,t.push({kind:`fact`,fact:e,token:a}),r=!1,i=[],a=void 0}continue}if(r){if(N.has(o.type))throw Error(`Nested facts aren't supported yet.`);o.type===`text`?i.push(o.content):o.type===`softbreak`&&i.push(`
4
+ `)}}},F=e=>{if(e.type===`heading_open`)return{level:parseInt(e.tag.slice(1),10)}},I=(e,t)=>{let n=[];for(let r=0;r<e.length;r++){let i=e[r],a=t;for(let t=r+1;t<e.length;t++)if(e[t].level<=i.level){a=e[t].exhibitStartIndex;break}n.push({level:i.level,text:i.text,exhibitStartIndex:i.exhibitStartIndex,exhibitEndIndex:a})}return n},L=e=>{let t=e.replace(/\r\n/g,`
5
+ `),n=D.parse(t,{}),r=[],i=[],a={proseTag:void 0,depth:0},o;for(let e=0;e<n.length;e++){let t=n[e];if(t.type===`html_block`){j(t.content,a);continue}let s=a.proseTag!==void 0;if(t.type===`bullet_list_open`||t.type===`ordered_list_open`){if(!s)throw Error(`Zest doesn't support lists yet.`);continue}let c=F(t);if(c){s||(o=c.level);continue}if(o!==void 0&&t.type===`inline`){i.push({level:o,text:t.content.trim(),exhibitStartIndex:r.length}),o=void 0;continue}if(t.type===`heading_close`){o=void 0;continue}if(t.type===`table_open`){let{table:i,endIndex:a}=M(n,e);if(!s){let e=t.meta??{};e.table=i,t.meta=e,r.push({kind:`table`,table:i,token:t})}e=a;continue}if(t.type===`fence`||t.type===`code_block`){if(s)continue;let e={};t.type===`fence`&&t.info&&t.info.trim()!==``&&(e.language=t.info.trim());let n=new h(t.content,e),i=t.meta??{};i.fact=n,t.meta=i,r.push({kind:`fact`,fact:n,token:t});continue}if(t.type===`inline`){if(t.children)for(let e of t.children)e.type===`html_inline`&&j(e.content,a);!s&&a.proseTag===void 0&&P(t,r)}}let s=I(i,r.length);return{exhibits:r.map(e=>e.kind===`fact`?e.fact:e.table),facts:r.filter(e=>e.kind===`fact`).map(e=>e.fact),tables:r.filter(e=>e.kind===`table`).map(e=>e.table),parsedExhibits:r,tokens:n,markdown:t,headings:s}},R=e=>{if(typeof e!=`string`)throw TypeError(`Expected a string, got ${typeof e}. Maybe you need a codec?`);return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`)},z=e=>{let t=R(e.raw);return e.state===`FAILURE`&&e.actualText!==void 0?`<del>${t}</del><ins>${R(e.actualText)}</ins>`:t},B=e=>{let t=[];t.push(`<table>`),t.push(`<thead>`),t.push(`<tr>`);for(let n of e.headerFacts)t.push(`<th class="${n.state}">${z(n)}</th>`);t.push(`</tr>`),t.push(`</thead>`),t.push(`<tbody>`);for(let n of e.cellFacts){t.push(`<tr>`);for(let e of n)t.push(`<td class="${e.state}">${z(e)}</td>`);t.push(`</tr>`)}return t.push(`</tbody>`),t.push(`</table>`),t.join(`
6
+ `)};function V(e){return typeof e==`object`&&!!e&&`fact`in e}const H=e=>{let t=/* @__PURE__ */ new Set;for(let n of e){if(n.type!==`inline`)continue;let e=n.children??[],r=!1;for(let n of e){if((n.type===`em_open`||n.type===`strong_open`)&&V(n.meta)&&n.meta.fact){r=!0;continue}if((n.type===`em_close`||n.type===`strong_close`)&&V(n.meta)&&n.meta.fact){r=!1;continue}r&&t.add(n)}}return t},U=(e,n,r,i)=>{let a=new t,o=H(r),s=i.filter(e=>e.kind===`table`),c=0,l=!1;a.renderer.rules.text=(e,t)=>l||o.has(e[t])?``:R(e[t].content),a.renderer.rules.softbreak=(e,t)=>l||o.has(e[t])?``:`
7
+ `,a.renderer.rules.code_inline=(e,t)=>{if(l)return``;let n=e[t],r=V(n.meta)?n.meta.fact:void 0;return r?`<code class="${r.state}">${z(r)}</code>`:`<code>${R(n.content)}</code>`},a.renderer.rules.em_open=(e,t,n,r,i)=>{if(l)return``;let a=e[t],o=V(a.meta)?a.meta.fact:void 0;return o?`<em class="${o.state}">${z(o)}`:`<em`+i.renderAttrs(a)+`>`},a.renderer.rules.em_close=()=>l?``:`</em>`,a.renderer.rules.strong_open=(e,t,n,r,i)=>{if(l)return``;let a=e[t],o=V(a.meta)?a.meta.fact:void 0;return o?`<strong class="${o.state}">${z(o)}`:`<strong`+i.renderAttrs(a)+`>`},a.renderer.rules.strong_close=()=>l?``:`</strong>`,a.renderer.rules.fence=(e,t)=>{let n=e[t],r=V(n.meta)?n.meta.fact:void 0;return r?`<pre><code class="${r.state}">${z(r)}</code></pre>\n`:`<pre><code${n.info&&n.info.trim()?` class="language-${R(n.info.trim())}"`:``}>${R(n.content)}</code></pre>\n`},a.renderer.rules.code_block=(e,t)=>{let n=e[t],r=V(n.meta)?n.meta.fact:void 0;return r?`<pre><code class="${r.state}">${z(r)}</code></pre>\n`:`<pre><code>${R(n.content)}</code></pre>\n`},a.renderer.rules.table_open=()=>{let e=s[c];return e?(l=!0,B(e.table)):`<table>`},a.renderer.rules.table_close=()=>{let e=l;return l=!1,e?(c++,``):`</table>`};let u=(e,t,n,r,i)=>l?``:i.renderToken(e,t,n);a.renderer.rules.thead_open=u,a.renderer.rules.thead_close=u,a.renderer.rules.tbody_open=u,a.renderer.rules.tbody_close=u,a.renderer.rules.tr_open=u,a.renderer.rules.tr_close=u,a.renderer.rules.th_open=u,a.renderer.rules.th_close=u,a.renderer.rules.td_open=u,a.renderer.rules.td_close=u;let d=a.renderer.render(r,a.options,{});return`<!DOCTYPE html>
8
+ <html lang="en">
9
+ <head>
10
+ <meta charset="UTF-8" />
11
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
12
+ <title>${R(e)}</title>
13
+ <style>
14
+ html {
15
+ box-sizing: border-box;
16
+ font-family: Arial;
17
+ font-size: 17px;
18
+ }
19
+ *, *:before, *:after {
20
+ box-sizing: inherit;
21
+ }
22
+ body {
23
+ padding: 24px;
24
+ overflow-wrap: break-word;
25
+ }
26
+ .content {
27
+ margin: 0 auto;
28
+ max-width: 800px;
29
+ }
30
+ table {
31
+ border-collapse: collapse;
32
+ border-spacing: 0;
33
+ box-shadow: 3px 5px 7px 0px rgba(0,0,0,0.1);
34
+ margin: 20px 0;
35
+ }
36
+ th {
37
+ font-weight: bold;
38
+ background-color: #e3e3e3;
39
+ vertical-align: top;
40
+ }
41
+ td {
42
+ background-color: #fff;
43
+ }
44
+ td, th {
45
+ border: 1px solid #333;
46
+ padding: 4px 6px;
47
+ }
48
+ pre, code {
49
+ font-family: Lucida Sans Typewriter, monospace;
50
+ font-size: 0.9rem;
51
+ line-height: 1.4rem;
52
+ }
53
+ pre {
54
+ padding: 10px 16px !important;
55
+ background-color: #f3f3f3;
56
+ }
57
+ p {
58
+ line-height: 1.5rem;
59
+ }
60
+ .prose, prose {
61
+ margin: 16px 0;
62
+ }
63
+ .prose ul, .prose ol, prose ul, prose ol {
64
+ padding-left: 24px;
65
+ margin: 12px 0;
66
+ }
67
+ .prose li, prose li {
68
+ line-height: 1.5rem;
69
+ margin-bottom: 4px;
70
+ }
71
+ .SUCCESS, .FAILURE {
72
+ padding: 2px 5px;
73
+ border-radius: 5px;
74
+ }
75
+ table .SUCCESS, table .FAILURE {
76
+ border-radius: 0;
77
+ }
78
+ .SUCCESS {
79
+ background-color: rgba(0,255,20, 0.3);
80
+ }
81
+ .FAILURE {
82
+ background-color: rgba(255, 64, 40, 0.3);
83
+ }
84
+ .FAILURE ins {
85
+ background-color: #def3ff;
86
+ font-weight: bold;
87
+ text-decoration: none;
88
+ padding: 2px 1px;
89
+ margin: 0 -1px;
90
+ }
91
+ .FAILURE del {
92
+ background-color: #f36b6b;
93
+ font-weight: bold;
94
+ text-decoration: line-through;
95
+ text-decoration-thickness: 2px;
96
+ padding: 2px 1px;
97
+ margin: 0 -1px;
98
+ }
99
+ pre code.SUCCESS, pre code.FAILURE {
100
+ padding-left: 0;
101
+ padding-right: 0;
102
+ }
103
+ .NOT_CHECKED {
104
+ background-color: #ddf7f6;
105
+ }
106
+ .NOT_USED {
107
+ xbackground-color: #eaeaea;
108
+ color: #777;
109
+ }
110
+ </style>
111
+ </head>
112
+ <body>
113
+ <div class="content">
114
+ ${d}
115
+ </div>
116
+ </body>
117
+ </html>`},W=(e,t)=>e?e.replace(/[^a-zA-Z0-9_-]/g,`_`).slice(0,80):n(`sha256`).update(t).digest(`hex`).slice(0,12),G=(e,t,n)=>{let o=S();if(!o.outputEnabled)return;let s=o.outputDir;r(s,{recursive:!0});let c=W(e,n)+`.html`,l=a(s,c);return i(l,t,`utf-8`),l},K=(e,t,n)=>{let r=0,i,a=(e,t)=>{r+=1,!i&&e!==void 0&&(i=`Expected '${e}' but got '${t}'`)};for(let e of n)if(e.kind===`fact`)e.fact.state===`FAILURE`&&a(e.fact.raw,e.fact.actualText);else{for(let t of e.table.headerFacts)t.state===`FAILURE`&&a(t.raw,t.actualText);for(let t of e.table.cellFacts)for(let e of t)e.state===`FAILURE`&&a(e.raw,e.actualText)}return{name:e,markdown:t,passed:r===0,failureCount:r,firstFailureMessage:i}};var q=class{_items;constructor(e){return this._items=e,new Proxy(this,{get(e,t,n){return typeof t==`string`&&/^\d+$/.test(t)?e._items[Number(t)]:Reflect.get(e,t,n)}})}get length(){return this._items.length}[Symbol.iterator](){return this._items[Symbol.iterator]()}at(e){let t=e<0?this._items.length+e:e,n=this._items[t];if(n===void 0)throw Error(`No fact at index ${e} (found ${this._items.length})`);return n}map(e){return this._items.map(e)}byLanguage(e,t){let n=this._items.filter(t=>t.metadata.language===e);if(t===void 0)return n;let r=n[t];if(r===void 0)throw Error(`No ${e} block at index ${t} (found ${n.length})`);return r}debug(e=!0){let t=this._items.map((e,t)=>({index:t,raw:e.raw}));if(e)for(let{index:e,raw:n}of t){let t=n.includes(`
118
+ `)?"`":`'`;console.log(`${e}: ${t}${n}${t}`)}return t}},J=class e{facts;tables;exhibits;_allExhibits;_headings;_scopeStart;_scopeEnd;constructor(e,t,n,r){this._allExhibits=e,this._headings=t,this._scopeStart=n,this._scopeEnd=r,this.exhibits=e.slice(n,r),this.facts=new q(this.exhibits.filter(e=>e.kind===`fact`)),this.tables=this.exhibits.filter(e=>e.kind===`table`)}get sectionNames(){return this._directChildHeadings().map(e=>e.text)}get allSectionNames(){return this._scopedHeadings().map(e=>({name:e.text,level:e.level}))}section(t){let n=this._directChildHeadings(),r=typeof t==`string`?n.filter(e=>e.text===t):n.filter(e=>t.test(e.text));if(r.length===0){let e=typeof t==`string`?`"${t}"`:`${t}`;throw Error(`No section found matching ${e}. Available sections: ${n.map(e=>`"${e.text}"`).join(`, `)||`(none)`}`)}if(r.length>1){let e=typeof t==`string`?`"${t}"`:`${t}`;throw Error(`Multiple sections match ${e}: ${r.map(e=>`"${e.text}"`).join(`, `)}`)}let i=r[0];return new e(this._allExhibits,this._headings,i.exhibitStartIndex,i.exhibitEndIndex)}debug(e=!0){return this.facts.debug(e)}_scopedHeadings(){return this._headings.filter(e=>e.level!==1&&e.exhibitStartIndex>=this._scopeStart&&e.exhibitEndIndex<=this._scopeEnd)}_directChildHeadings(){let e=this._scopedHeadings();if(e.length===0)return[];let t=[],n=0;for(let r of e)(n===0||r.level<=n)&&(t.push(r),n=r.level);return t}};const Y=e=>e.replace(/\s+/g,` `).trim().slice(0,120)||`scenario`,X=()=>`each-row scenario`,Z=e=>e===void 0?S().failFast:e,Q=(e,t,n)=>{if(t.length===0)throw Error(`No facts found. Cannot assert a return value without at least one fact.`);t[t.length-1].assertEquals(e,n)},$=(e,t,n)=>{if(t.length===0)throw Error(`No facts found. Cannot assert a return value without at least one fact.`);t[t.length-1].assertEqualsNoThrow(e,n)},{scenario:ee,eachRow:te}=(e=>({scenario:t=>{let n=t.name??Y(t.markdown);e.registerTest({name:n,async run(){let e=L(t.markdown);if(e.exhibits.length===0)throw Error(`Scenario must contain at least one fact or table.`);let r=Z(t.failFast),i=t.result??s,a=new J(e.exhibits,e.headings,0,e.exhibits.length);if(t.debug&&a.debug(!0),r)try{let n=await t.execute(a);n!==void 0&&Q(n,e.facts,i)}finally{let r=U(n,t.markdown,e.tokens,e.parsedExhibits);G(n,r,t.markdown)}else{let r;try{let n=await t.execute(a);n!==void 0&&$(n,e.facts,i)}catch(e){e instanceof p||(r=e)}let o=K(n,t.markdown,e.parsedExhibits),s=U(n,t.markdown,e.tokens,e.parsedExhibits);if(G(n,s,t.markdown),r)throw r;if(!o.passed){let e=o.failureCount===1?`Failure: ${o.firstFailureMessage}`:`Found ${o.failureCount} failures. First: ${o.firstFailureMessage}`;throw Error(e)}}}})},eachRow:t=>{let n=t.name??X(),r;try{r=L(t.markdown)}catch(t){e.registerTest({name:n,run(){throw t}});return}let i=r.parsedExhibits.find(e=>e.kind===`table`);if(!i||i.kind!==`table`){e.registerTest({name:n,run(){throw Error(`Expected a table in the scenario.`)}});return}let a=i.table;e.registerTest({name:n,async run(){try{await T(a,{expectedColumn:t.expectedColumn,codecs:t.codecs,result:t.result,failFast:t.failFast,execute:t.execute});let e=K(n,t.markdown,r.parsedExhibits);if(!e.passed){let t=e.failureCount===1?`Failure: ${e.firstFailureMessage}`:`Found ${e.failureCount} failures. First: ${e.firstFailureMessage}`;throw Error(t)}}finally{let e=U(n,t.markdown,r.tokens,r.parsedExhibits);G(n,e,t.markdown)}}})}}))({registerTest(e){o(e.name,e.run)}});export{J as Context,h as Fact,q as FactList,g as Row,E as Table,u as booleanCodec,te as eachRow,d as enumCodec,l as floatCodec,c as intCodec,f as mappedCodec,e as quickReference,ee as scenario,C as setConfig,s as stringCodec};