@anselmdk/feature-spec-md 0.1.1 → 0.2.1-rc.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/README.md +159 -49
- package/SPEC_FORMAT.md +200 -38
- package/dist/cli.js +198 -28
- package/dist/cli.js.map +1 -1
- package/dist/featureSpecs.d.ts +34 -0
- package/dist/featureSpecs.d.ts.map +1 -0
- package/dist/featureSpecs.js +240 -0
- package/dist/featureSpecs.js.map +1 -0
- package/dist/filePatterns.d.ts.map +1 -1
- package/dist/filePatterns.js +4 -0
- package/dist/filePatterns.js.map +1 -1
- package/dist/githubActionReport.d.ts +3 -0
- package/dist/githubActionReport.d.ts.map +1 -0
- package/dist/githubActionReport.js +247 -0
- package/dist/githubActionReport.js.map +1 -0
- package/dist/html.d.ts +3 -0
- package/dist/html.d.ts.map +1 -1
- package/dist/html.js +3 -0
- package/dist/html.js.map +1 -1
- package/dist/index.d.ts +3 -34
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -338
- package/dist/index.js.map +1 -1
- package/dist/playwright.d.ts.map +1 -1
- package/dist/playwright.js +13 -21
- package/dist/playwright.js.map +1 -1
- package/dist/reportTemplate.d.ts +4 -1
- package/dist/reportTemplate.d.ts.map +1 -1
- package/dist/reportTemplate.js +251 -21
- package/dist/reportTemplate.js.map +1 -1
- package/dist/screenshots.d.ts.map +1 -1
- package/dist/screenshots.js +4 -0
- package/dist/screenshots.js.map +1 -1
- package/dist/specDocuments.d.ts +86 -0
- package/dist/specDocuments.d.ts.map +1 -0
- package/dist/specDocuments.js +351 -0
- package/dist/specDocuments.js.map +1 -0
- package/dist/specMarkdown.d.ts +26 -0
- package/dist/specMarkdown.d.ts.map +1 -0
- package/dist/specMarkdown.js +126 -0
- package/dist/specMarkdown.js.map +1 -0
- package/dist/testImplementationReport.d.ts +53 -0
- package/dist/testImplementationReport.d.ts.map +1 -0
- package/dist/testImplementationReport.js +160 -0
- package/dist/testImplementationReport.js.map +1 -0
- package/dist/types.d.ts +76 -11
- package/dist/types.d.ts.map +1 -1
- package/docs/releasing.md +108 -0
- package/docs/spec-driven-flow.md +175 -0
- package/package.json +13 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"githubActionReport.d.ts","sourceRoot":"","sources":["../src/githubActionReport.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAe3E,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,yBAAyB,iBA8CnC"}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, join, relative, sep } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
export async function publishGithubActionReport(options) {
|
|
6
|
+
const reportDir = value(options, "report-dir", "FEATURE_SPEC_REPORT_DIR") ??
|
|
7
|
+
"test-results/spec-report";
|
|
8
|
+
const reportName = value(options, "name", "FEATURE_SPEC_REPORT_NAME") ??
|
|
9
|
+
"feature-spec-report";
|
|
10
|
+
const mode = publishMode(value(options, "publish", "FEATURE_SPEC_REPORT_PUBLISH") ?? "artifact");
|
|
11
|
+
if (mode === "ftp") {
|
|
12
|
+
const config = ftpConfig(options);
|
|
13
|
+
const reportUrl = await publishReportToFtp(reportDir, config);
|
|
14
|
+
await writeGithubSummary([
|
|
15
|
+
"## Feature Spec Report",
|
|
16
|
+
"",
|
|
17
|
+
`<p><strong>Hosted HTML report:</strong> <a href=\"${escapeHtml(reportUrl)}\">${escapeHtml(reportName)}</a></p>`,
|
|
18
|
+
`<p><strong>Report index:</strong> <a href=\"${escapeHtml(indexUrl(config.baseUrl))}\">all builds</a></p>`,
|
|
19
|
+
"",
|
|
20
|
+
].join("\n"));
|
|
21
|
+
await writeGithubOutput({
|
|
22
|
+
"upload-github-artifact": "false",
|
|
23
|
+
"report-url": reportUrl,
|
|
24
|
+
"index-url": indexUrl(config.baseUrl),
|
|
25
|
+
});
|
|
26
|
+
console.log(`Feature spec report uploaded to ${reportUrl}`);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
await writeGithubSummary([
|
|
30
|
+
"## Feature Spec Report",
|
|
31
|
+
"",
|
|
32
|
+
`<p>Feature spec report is ready for GitHub artifact upload as <strong>${escapeHtml(reportName)}</strong>.</p>`,
|
|
33
|
+
"",
|
|
34
|
+
].join("\n"));
|
|
35
|
+
await writeGithubOutput({
|
|
36
|
+
"upload-github-artifact": "true",
|
|
37
|
+
"artifact-name": reportName,
|
|
38
|
+
"artifact-path": reportDir,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async function publishReportToFtp(reportDir, config) {
|
|
42
|
+
const buildRemoteDir = pathJoin(config.remoteDir, config.buildNumber);
|
|
43
|
+
await uploadDirectory(reportDir, buildRemoteDir, config);
|
|
44
|
+
const builds = await listBuildNumbers(config.remoteDir, config);
|
|
45
|
+
if (!builds.includes(config.buildNumber))
|
|
46
|
+
builds.push(config.buildNumber);
|
|
47
|
+
builds.sort((a, b) => Number(b) - Number(a));
|
|
48
|
+
const localIndex = join(tmpdir(), `feature-spec-md-build-index-${process.pid}.html`);
|
|
49
|
+
await writeFile(localIndex, renderBuildIndex(config.baseUrl, builds), "utf8");
|
|
50
|
+
await uploadFile(localIndex, pathJoin(config.remoteDir, "index.html"), config);
|
|
51
|
+
return buildUrl(config.baseUrl, config.buildNumber);
|
|
52
|
+
}
|
|
53
|
+
async function uploadDirectory(localDir, remoteDir, config) {
|
|
54
|
+
const files = await walkFiles(localDir);
|
|
55
|
+
if (!files.length) {
|
|
56
|
+
throw new Error(`Report directory contains no files: ${localDir}`);
|
|
57
|
+
}
|
|
58
|
+
for (const file of files) {
|
|
59
|
+
const remotePath = pathJoin(remoteDir, relative(localDir, file).split(sep).join("/"));
|
|
60
|
+
await uploadFile(file, remotePath, config);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function uploadFile(localFile, remotePath, config) {
|
|
64
|
+
await runCurl([
|
|
65
|
+
"--silent",
|
|
66
|
+
"--show-error",
|
|
67
|
+
"--fail",
|
|
68
|
+
"--ftp-create-dirs",
|
|
69
|
+
"-u",
|
|
70
|
+
`${config.user}:${config.password}`,
|
|
71
|
+
"-T",
|
|
72
|
+
localFile,
|
|
73
|
+
ftpUrl(config, remotePath),
|
|
74
|
+
]);
|
|
75
|
+
}
|
|
76
|
+
async function listBuildNumbers(remoteDir, config) {
|
|
77
|
+
try {
|
|
78
|
+
const listing = await runCurl([
|
|
79
|
+
"--silent",
|
|
80
|
+
"--show-error",
|
|
81
|
+
"--fail",
|
|
82
|
+
"-u",
|
|
83
|
+
`${config.user}:${config.password}`,
|
|
84
|
+
ftpUrl(config, pathJoin(remoteDir, "/")),
|
|
85
|
+
]);
|
|
86
|
+
return Array.from(new Set(listing
|
|
87
|
+
.split(/\r?\n/)
|
|
88
|
+
.map((line) => line.trim().split(/\s+/).at(-1) ?? "")
|
|
89
|
+
.filter((name) => /^\d+$/.test(name))));
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function runCurl(args) {
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
const child = spawn("curl", args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
98
|
+
let stdout = "";
|
|
99
|
+
let stderr = "";
|
|
100
|
+
child.stdout.on("data", (chunk) => {
|
|
101
|
+
stdout += chunk.toString();
|
|
102
|
+
});
|
|
103
|
+
child.stderr.on("data", (chunk) => {
|
|
104
|
+
stderr += chunk.toString();
|
|
105
|
+
});
|
|
106
|
+
child.on("error", reject);
|
|
107
|
+
child.on("close", (code) => {
|
|
108
|
+
if (code === 0) {
|
|
109
|
+
resolve(stdout);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
reject(new Error(stderr.trim() || `curl exited with status ${code}`));
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
async function walkFiles(dir) {
|
|
117
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
118
|
+
const files = await Promise.all(entries.map(async (entry) => {
|
|
119
|
+
const fullPath = join(dir, entry.name);
|
|
120
|
+
if (entry.isDirectory())
|
|
121
|
+
return walkFiles(fullPath);
|
|
122
|
+
if (entry.isFile())
|
|
123
|
+
return [fullPath];
|
|
124
|
+
return [];
|
|
125
|
+
}));
|
|
126
|
+
return files.flat();
|
|
127
|
+
}
|
|
128
|
+
function ftpConfig(options) {
|
|
129
|
+
const host = required(options, "ftp-host", "FEATURE_SPEC_FTP_HOST");
|
|
130
|
+
const user = required(options, "ftp-user", "FEATURE_SPEC_FTP_USER");
|
|
131
|
+
const password = required(options, "ftp-password", "FEATURE_SPEC_FTP_PASSWORD");
|
|
132
|
+
const baseUrl = required(options, "base-url", "FEATURE_SPEC_REPORT_BASE_URL");
|
|
133
|
+
const remoteDir = value(options, "ftp-remote-dir", "FEATURE_SPEC_FTP_REMOTE_DIR") ?? "";
|
|
134
|
+
const buildNumber = value(options, "build-number", "GITHUB_RUN_NUMBER") ??
|
|
135
|
+
value(options, "build-number", "FEATURE_SPEC_BUILD_NUMBER") ??
|
|
136
|
+
"local";
|
|
137
|
+
const secure = booleanValue(value(options, "ftp-secure", "FEATURE_SPEC_FTP_SECURE"));
|
|
138
|
+
const port = value(options, "ftp-port", "FEATURE_SPEC_FTP_PORT");
|
|
139
|
+
if (!/^\d+$/.test(buildNumber)) {
|
|
140
|
+
throw new Error(`Build number must be numeric for FTP publishing: ${buildNumber}`);
|
|
141
|
+
}
|
|
142
|
+
return { host, user, password, port, secure, remoteDir, baseUrl, buildNumber };
|
|
143
|
+
}
|
|
144
|
+
function publishMode(value) {
|
|
145
|
+
if (value === "ftp" || value === "artifact")
|
|
146
|
+
return value;
|
|
147
|
+
throw new Error(`Unknown report publish mode: ${value}`);
|
|
148
|
+
}
|
|
149
|
+
function required(options, key, envKey) {
|
|
150
|
+
const result = value(options, key, envKey);
|
|
151
|
+
if (!result)
|
|
152
|
+
throw new Error(`Missing required option --${key} or ${envKey}.`);
|
|
153
|
+
return result;
|
|
154
|
+
}
|
|
155
|
+
function value(options, key, envKey) {
|
|
156
|
+
return options[key] ?? process.env[envKey];
|
|
157
|
+
}
|
|
158
|
+
function booleanValue(value) {
|
|
159
|
+
return value === "true" || value === "1" || value === "yes";
|
|
160
|
+
}
|
|
161
|
+
function ftpUrl(config, remotePath) {
|
|
162
|
+
const protocol = config.secure ? "ftps" : "ftp";
|
|
163
|
+
const port = config.port ? `:${config.port}` : "";
|
|
164
|
+
const encodedPath = remotePath
|
|
165
|
+
.split("/")
|
|
166
|
+
.filter(Boolean)
|
|
167
|
+
.map(encodeURIComponent)
|
|
168
|
+
.join("/");
|
|
169
|
+
return `${protocol}://${config.host}${port}/${encodedPath}`;
|
|
170
|
+
}
|
|
171
|
+
function pathJoin(...parts) {
|
|
172
|
+
const joined = parts
|
|
173
|
+
.flatMap((part) => part.split("/"))
|
|
174
|
+
.map((part) => part.trim())
|
|
175
|
+
.filter((part) => part && part !== ".")
|
|
176
|
+
.join("/");
|
|
177
|
+
return joined;
|
|
178
|
+
}
|
|
179
|
+
function renderBuildIndex(baseUrl, builds) {
|
|
180
|
+
const generatedAt = new Date().toISOString();
|
|
181
|
+
const rows = builds
|
|
182
|
+
.map((build) => `<li><a href=\"${escapeHtml(buildUrl(baseUrl, build))}\">Build ${escapeHtml(build)}</a></li>`)
|
|
183
|
+
.join("\n");
|
|
184
|
+
return `<!doctype html>
|
|
185
|
+
<html lang=\"en\">
|
|
186
|
+
<head>
|
|
187
|
+
<meta charset=\"utf-8\">
|
|
188
|
+
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">
|
|
189
|
+
<title>Feature spec reports</title>
|
|
190
|
+
<style>
|
|
191
|
+
body { font-family: system-ui, sans-serif; max-width: 48rem; margin: 3rem auto; padding: 0 1rem; line-height: 1.5; }
|
|
192
|
+
li { margin: 0.35rem 0; }
|
|
193
|
+
</style>
|
|
194
|
+
</head>
|
|
195
|
+
<body>
|
|
196
|
+
<h1>Feature spec reports</h1>
|
|
197
|
+
<p>Generated ${escapeHtml(generatedAt)}.</p>
|
|
198
|
+
<ol>
|
|
199
|
+
${rows}
|
|
200
|
+
</ol>
|
|
201
|
+
</body>
|
|
202
|
+
</html>
|
|
203
|
+
`;
|
|
204
|
+
}
|
|
205
|
+
function buildUrl(baseUrl, build) {
|
|
206
|
+
return `${trimTrailingSlash(baseUrl)}/${encodeURIComponent(build)}/`;
|
|
207
|
+
}
|
|
208
|
+
function indexUrl(baseUrl) {
|
|
209
|
+
return `${trimTrailingSlash(baseUrl)}/`;
|
|
210
|
+
}
|
|
211
|
+
function trimTrailingSlash(value) {
|
|
212
|
+
return value.replace(/\/+$/, "");
|
|
213
|
+
}
|
|
214
|
+
function escapeHtml(value) {
|
|
215
|
+
return value
|
|
216
|
+
.replace(/&/g, "&")
|
|
217
|
+
.replace(/</g, "<")
|
|
218
|
+
.replace(/>/g, ">")
|
|
219
|
+
.replace(/\"/g, """);
|
|
220
|
+
}
|
|
221
|
+
async function writeGithubSummary(markdown) {
|
|
222
|
+
const summaryPath = process.env.GITHUB_STEP_SUMMARY;
|
|
223
|
+
if (!summaryPath)
|
|
224
|
+
return;
|
|
225
|
+
await mkdir(dirname(summaryPath), { recursive: true });
|
|
226
|
+
const existing = await readTextIfExists(summaryPath);
|
|
227
|
+
await writeFile(summaryPath, `${existing}${markdown}`, "utf8");
|
|
228
|
+
}
|
|
229
|
+
async function writeGithubOutput(values) {
|
|
230
|
+
const outputPath = process.env.GITHUB_OUTPUT;
|
|
231
|
+
if (!outputPath)
|
|
232
|
+
return;
|
|
233
|
+
const lines = Object.entries(values)
|
|
234
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
235
|
+
.join("\n");
|
|
236
|
+
const existing = await readTextIfExists(outputPath);
|
|
237
|
+
await writeFile(outputPath, `${existing}${lines}\n`, "utf8");
|
|
238
|
+
}
|
|
239
|
+
async function readTextIfExists(filePath) {
|
|
240
|
+
try {
|
|
241
|
+
return await readFile(filePath, "utf8");
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
return "";
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=githubActionReport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"githubActionReport.js","sourceRoot":"","sources":["../src/githubActionReport.ts"],"names":[],"mappings":"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"}
|
package/dist/html.d.ts
CHANGED
package/dist/html.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AAAA,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,UAOjC"}
|
|
1
|
+
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,UAOjC"}
|
package/dist/html.js
CHANGED
package/dist/html.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html.js","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,IAAI,CAAC,KAAa;IAChC,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC;SACxB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC"}
|
|
1
|
+
{"version":3,"file":"html.js","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,IAAI,CAAC,KAAa;IAChC,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC;SACxB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,40 +1,9 @@
|
|
|
1
1
|
export { expandFilePatterns } from "./filePatterns.js";
|
|
2
|
+
export { buildCoverageSummary, checkFeatureSpecs, collectTestReferences, loadFeatureSpecs, parseFeatureSpec, parseTestReferences, validateCoverage, validateFeatureSpec, } from "./featureSpecs.js";
|
|
2
3
|
export { renderHtmlReport } from "./reportTemplate.js";
|
|
3
4
|
export { collectSpecScreenshots } from "./screenshots.js";
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
/** Parse one feature spec Markdown document into structured metadata, rules, and scenarios. */
|
|
7
|
-
export declare function parseFeatureSpec(source: string, options?: {
|
|
8
|
-
filePath?: string;
|
|
9
|
-
}): FeatureSpec;
|
|
10
|
-
/** Validate spec structure, ID prefixes, duplicate IDs, and required sections. */
|
|
11
|
-
export declare function validateFeatureSpec(spec: FeatureSpec): ValidationIssue[];
|
|
12
|
-
/** Find rule and scenario IDs referenced by one test source file. */
|
|
13
|
-
export declare function parseTestReferences(source: string, filePath?: string): TestReference[];
|
|
14
|
-
/** Expand test file globs and collect all rule and scenario references from them. */
|
|
15
|
-
export declare function collectTestReferences(patterns: string[]): Promise<TestReference[]>;
|
|
16
|
-
/** Build rule and scenario coverage from parsed specs and collected test references. */
|
|
17
|
-
export declare function buildCoverageSummary(specs: FeatureSpec[], references: TestReference[]): CoverageSummary;
|
|
18
|
-
/** Validate coverage expectations and orphaned references. */
|
|
19
|
-
export declare function validateCoverage(coverage: CoverageSummary, options?: {
|
|
20
|
-
requireRuleCoverage?: boolean;
|
|
21
|
-
requireScenarioCoverage?: boolean;
|
|
22
|
-
}): ValidationIssue[];
|
|
23
|
-
/** Load and parse all feature specs matching the provided globs. */
|
|
24
|
-
export declare function loadFeatureSpecs(patterns: string[]): Promise<FeatureSpec[]>;
|
|
25
|
-
/** Run spec validation and optional test coverage validation in one call. */
|
|
26
|
-
export declare function checkFeatureSpecs(options: {
|
|
27
|
-
specs: string[];
|
|
28
|
-
tests?: string[];
|
|
29
|
-
requireRuleCoverage?: boolean;
|
|
30
|
-
requireScenarioCoverage?: boolean;
|
|
31
|
-
}): Promise<{
|
|
32
|
-
specs: FeatureSpec[];
|
|
33
|
-
validationIssues: ValidationIssue[];
|
|
34
|
-
coverage: CoverageSummary | undefined;
|
|
35
|
-
coverageIssues: ValidationIssue[];
|
|
36
|
-
ok: boolean;
|
|
37
|
-
}>;
|
|
5
|
+
export { buildSpecCoverageSummary, checkSpecDocuments, collectSpecTestReferences, loadSpecDocuments, parseDesignSpec, parseModelSpec, parseSpecDocument, parseSpecTestReferences, parseStackSpec, validateDesignSpec, validateModelSpec, validateSpecDocument, validateSpecGraph, validateStackSpec, } from "./specDocuments.js";
|
|
6
|
+
export type { CoverageItem, CoverageSummary, DesignFrontmatter, DesignSpec, FeatureFrontmatter, FeatureRule, FeatureScenario, FeatureSpec, FeatureStep, ModelItem, ModelReferenceFrontmatter, ModelSpec, RuleKeyword, SpecDocument, SpecFrontmatter, SpecScreenshot, StackSpec, StepKeyword, TestReference, ValidationIssue, } from "./types.js";
|
|
38
7
|
/** Write text content to a path, creating the parent directory when needed. */
|
|
39
8
|
export declare function writeTextFile(filePath: string, content: string): Promise<void>;
|
|
40
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,uBAAuB,EACvB,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,UAAU,EACV,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,WAAW,EACX,WAAW,EACX,SAAS,EACT,yBAAyB,EACzB,SAAS,EACT,WAAW,EACX,YAAY,EACZ,eAAe,EACf,cAAc,EACd,SAAS,EACT,WAAW,EACX,aAAa,EACb,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,+EAA+E;AAC/E,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,iBAGpE"}
|