@doxbrix/doxloop 0.1.4 → 0.2.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.
- package/CHANGELOG.md +1153 -0
- package/README.md +422 -226
- package/assets/doxbrix-preview.css +77 -15
- package/contracts/agent-events-v1.schema.json +1 -0
- package/contracts/coverage-v1.schema.json +59 -0
- package/contracts/drift-v1.schema.json +1 -0
- package/contracts/evaluation-v1.schema.json +62 -0
- package/contracts/quality-codes-v1.json +1 -0
- package/contracts/quality-report-v1.schema.json +1 -0
- package/contracts/validation-v1.schema.json +1 -0
- package/dist/agent-failure.d.ts +36 -0
- package/dist/agent-failure.js +98 -0
- package/dist/agent-isolation.d.ts +17 -0
- package/dist/agent-isolation.js +94 -0
- package/dist/agent-log.d.ts +197 -0
- package/dist/agent-log.js +1094 -0
- package/dist/agent-process.d.ts +63 -0
- package/dist/agent-process.js +128 -0
- package/dist/agent-reply.d.ts +47 -0
- package/dist/agent-reply.js +315 -0
- package/dist/agents.d.ts +40 -4
- package/dist/agents.js +115 -11
- package/dist/api-coverage.d.ts +11 -0
- package/dist/api-coverage.js +72 -0
- package/dist/application-probe.d.ts +28 -0
- package/dist/application-probe.js +93 -0
- package/dist/application-url.d.ts +14 -0
- package/dist/application-url.js +26 -0
- package/dist/args.js +15 -0
- package/dist/artifact-deploy.d.ts +9 -0
- package/dist/artifact-deploy.js +28 -8
- package/dist/assets.d.ts +72 -0
- package/dist/assets.js +382 -0
- package/dist/auth.d.ts +10 -0
- package/dist/auth.js +38 -9
- package/dist/author.d.ts +206 -4
- package/dist/author.js +1650 -52
- package/dist/authoring-batches.d.ts +190 -0
- package/dist/authoring-batches.js +489 -0
- package/dist/authoring-postpass.d.ts +79 -0
- package/dist/authoring-postpass.js +1289 -0
- package/dist/authoring-progress.d.ts +95 -0
- package/dist/authoring-progress.js +269 -0
- package/dist/autosync.d.ts +56 -0
- package/dist/autosync.js +442 -0
- package/dist/batch-artifacts.d.ts +81 -0
- package/dist/batch-artifacts.js +412 -0
- package/dist/batch-limits.d.ts +37 -0
- package/dist/batch-limits.js +80 -0
- package/dist/branding.d.ts +41 -0
- package/dist/branding.js +229 -0
- package/dist/bundle-upload.d.ts +26 -0
- package/dist/bundle-upload.js +99 -0
- package/dist/capture-auth.d.ts +95 -0
- package/dist/capture-auth.js +204 -0
- package/dist/capture.d.ts +48 -0
- package/dist/capture.js +20 -1
- package/dist/cli.js +839 -32
- package/dist/content-links.d.ts +10 -0
- package/dist/content-links.js +49 -0
- package/dist/contract-validation.d.ts +4 -0
- package/dist/contract-validation.js +25 -0
- package/dist/coverage-actions.d.ts +8 -0
- package/dist/coverage-actions.js +71 -0
- package/dist/coverage-resolutions.d.ts +16 -0
- package/dist/coverage-resolutions.js +58 -0
- package/dist/db.d.ts +37 -0
- package/dist/db.js +288 -0
- package/dist/demo.d.ts +12 -0
- package/dist/demo.js +122 -0
- package/dist/deploy-credentials.d.ts +7 -0
- package/dist/deploy-credentials.js +76 -0
- package/dist/deploy-targets/github-pages.d.ts +3 -0
- package/dist/deploy-targets/github-pages.js +100 -0
- package/dist/deploy-targets/index.d.ts +8 -0
- package/dist/deploy-targets/index.js +78 -0
- package/dist/deploy-targets/netlify.d.ts +3 -0
- package/dist/deploy-targets/netlify.js +49 -0
- package/dist/deploy-targets/types.d.ts +32 -0
- package/dist/deploy-targets/types.js +2 -0
- package/dist/deploy-targets/vercel.d.ts +3 -0
- package/dist/deploy-targets/vercel.js +95 -0
- package/dist/deploy.d.ts +9 -0
- package/dist/deploy.js +87 -13
- package/dist/deterministic-capture.d.ts +90 -0
- package/dist/deterministic-capture.js +435 -0
- package/dist/direct-edit.d.ts +40 -0
- package/dist/direct-edit.js +159 -0
- package/dist/docs-crawl.d.ts +86 -0
- package/dist/docs-crawl.js +536 -0
- package/dist/docs-site.d.ts +49 -0
- package/dist/docs-site.js +233 -0
- package/dist/doctor.js +8 -0
- package/dist/documentation-collections.d.ts +23 -0
- package/dist/documentation-collections.js +191 -0
- package/dist/documentation-plan.d.ts +253 -0
- package/dist/documentation-plan.js +2563 -0
- package/dist/doxbrix-build.d.ts +19 -0
- package/dist/doxbrix-build.js +167 -0
- package/dist/doxbrix-markdown.d.ts +9 -0
- package/dist/doxbrix-markdown.js +115 -14
- package/dist/drift.d.ts +10 -0
- package/dist/drift.js +164 -0
- package/dist/evaluation.d.ts +46 -0
- package/dist/evaluation.js +113 -0
- package/dist/evidence-pack.d.ts +47 -0
- package/dist/evidence-pack.js +358 -0
- package/dist/evidence.d.ts +25 -0
- package/dist/evidence.js +175 -0
- package/dist/fs.d.ts +8 -2
- package/dist/fs.js +41 -11
- package/dist/generator-api.d.ts +89 -0
- package/dist/generator-preflight.d.ts +27 -0
- package/dist/generator-preflight.js +105 -0
- package/dist/generator-runtime.d.ts +7 -0
- package/dist/generator-runtime.js +17 -1
- package/dist/generators.d.ts +24 -2
- package/dist/generators.js +54 -1
- package/dist/git-delivery.d.ts +17 -0
- package/dist/git-delivery.js +123 -0
- package/dist/globs.d.ts +16 -0
- package/dist/globs.js +65 -0
- package/dist/glossary.d.ts +26 -0
- package/dist/glossary.js +179 -0
- package/dist/history.d.ts +106 -0
- package/dist/history.js +600 -0
- package/dist/html-markdown.d.ts +46 -0
- package/dist/html-markdown.js +423 -0
- package/dist/interactive.js +16 -15
- package/dist/job-events.d.ts +74 -0
- package/dist/job-events.js +377 -0
- package/dist/keep-awake.d.ts +50 -0
- package/dist/keep-awake.js +123 -0
- package/dist/local-source-snapshot.d.ts +20 -0
- package/dist/local-source-snapshot.js +61 -0
- package/dist/mintlify-detect.d.ts +3 -0
- package/dist/mintlify-detect.js +18 -0
- package/dist/mintlify-import.d.ts +75 -0
- package/dist/mintlify-import.js +190 -0
- package/dist/navigation.d.ts +98 -0
- package/dist/navigation.js +310 -0
- package/dist/openapi.d.ts +60 -0
- package/dist/openapi.js +439 -0
- package/dist/page-editor-bridge.d.ts +3 -0
- package/dist/page-editor-bridge.js +109 -0
- package/dist/page-editor-preview.d.ts +10 -0
- package/dist/page-editor-preview.js +55 -0
- package/dist/page-extension.d.ts +9 -0
- package/dist/page-extension.js +15 -0
- package/dist/page-metadata.d.ts +28 -0
- package/dist/page-metadata.js +166 -0
- package/dist/page-operations.d.ts +34 -0
- package/dist/page-operations.js +215 -0
- package/dist/page-routes.d.ts +4 -0
- package/dist/page-routes.js +61 -0
- package/dist/pages.d.ts +20 -0
- package/dist/pages.js +184 -0
- package/dist/plan-generator.d.ts +3 -0
- package/dist/plan-generator.js +21 -0
- package/dist/plan-navigation.d.ts +11 -0
- package/dist/plan-navigation.js +30 -0
- package/dist/planning-captures.d.ts +20 -0
- package/dist/planning-captures.js +143 -0
- package/dist/planning-research.d.ts +135 -0
- package/dist/planning-research.js +472 -0
- package/dist/planning-triage.d.ts +23 -0
- package/dist/planning-triage.js +131 -0
- package/dist/preview.d.ts +24 -0
- package/dist/preview.js +280 -29
- package/dist/project-detect.d.ts +36 -0
- package/dist/project-detect.js +251 -0
- package/dist/project-import.d.ts +54 -0
- package/dist/project-import.js +157 -0
- package/dist/project-lock.d.ts +6 -0
- package/dist/project-lock.js +96 -0
- package/dist/project-registry.d.ts +25 -0
- package/dist/project-registry.js +79 -0
- package/dist/project.d.ts +35 -3
- package/dist/project.js +388 -38
- package/dist/prompts.d.ts +9 -0
- package/dist/prompts.js +33 -4
- package/dist/proposal-replay.d.ts +32 -0
- package/dist/proposal-replay.js +99 -0
- package/dist/quality-claims.d.ts +8 -0
- package/dist/quality-claims.js +168 -0
- package/dist/quality-config.d.ts +5 -0
- package/dist/quality-config.js +84 -0
- package/dist/quality-contract.d.ts +37 -0
- package/dist/quality-contract.js +46 -0
- package/dist/quality-examples.d.ts +4 -0
- package/dist/quality-examples.js +233 -0
- package/dist/quality-gates.d.ts +16 -0
- package/dist/quality-gates.js +192 -0
- package/dist/quality-links.d.ts +7 -0
- package/dist/quality-links.js +149 -0
- package/dist/quality-lint.d.ts +6 -0
- package/dist/quality-lint.js +124 -0
- package/dist/quality-rendered.d.ts +11 -0
- package/dist/quality-rendered.js +222 -0
- package/dist/quality-schema.d.ts +3 -0
- package/dist/quality-schema.js +71 -0
- package/dist/release-notes.d.ts +44 -0
- package/dist/release-notes.js +183 -0
- package/dist/remote-monitor.d.ts +16 -0
- package/dist/remote-monitor.js +74 -0
- package/dist/remote-source.d.ts +34 -0
- package/dist/remote-source.js +426 -0
- package/dist/review-diff.d.ts +82 -0
- package/dist/review-diff.js +400 -0
- package/dist/review-learning.d.ts +11 -0
- package/dist/review-learning.js +60 -0
- package/dist/review-render.d.ts +38 -0
- package/dist/review-render.js +224 -0
- package/dist/review-report.d.ts +9 -0
- package/dist/review-report.js +89 -0
- package/dist/review-ui.d.ts +14 -0
- package/dist/review-ui.js +1248 -0
- package/dist/schedule.d.ts +78 -0
- package/dist/schedule.js +480 -0
- package/dist/screen-capture-provider.d.ts +52 -0
- package/dist/screen-capture-provider.js +218 -0
- package/dist/screenshot-workflow.d.ts +167 -0
- package/dist/screenshot-workflow.js +1237 -0
- package/dist/settings.d.ts +1 -1
- package/dist/settings.js +95 -7
- package/dist/site-export.d.ts +18 -0
- package/dist/site-export.js +87 -0
- package/dist/source-connectors.d.ts +33 -0
- package/dist/source-connectors.js +268 -0
- package/dist/source-discovery.d.ts +132 -0
- package/dist/source-discovery.js +823 -0
- package/dist/source-intelligence.d.ts +9 -0
- package/dist/source-intelligence.js +306 -0
- package/dist/sync-review.d.ts +28 -0
- package/dist/sync-review.js +264 -0
- package/dist/sync-runs.d.ts +192 -0
- package/dist/sync-runs.js +2244 -0
- package/dist/sync.d.ts +35 -0
- package/dist/sync.js +298 -32
- package/dist/text-diff.d.ts +9 -0
- package/dist/text-diff.js +59 -0
- package/dist/types.d.ts +946 -1
- package/dist/ui/assets/doxloop-logo-light-De7Nx7j7.png +0 -0
- package/dist/ui/assets/index-BHBYU2aG.css +1 -0
- package/dist/ui/assets/index-Cq3RPQiC.js +33 -0
- package/dist/ui/index.html +18 -0
- package/dist/ui-server.d.ts +83 -0
- package/dist/ui-server.js +3532 -0
- package/dist/usage-budget.d.ts +28 -0
- package/dist/usage-budget.js +90 -0
- package/dist/validation.d.ts +25 -1
- package/dist/validation.js +312 -26
- package/dist/workspace-tools.d.ts +54 -0
- package/dist/workspace-tools.js +123 -0
- package/docs/agent-compatibility.md +50 -28
- package/docs/ci-and-automation.md +105 -66
- package/docs/doxbrix-http-api.md +8 -1
- package/docs/existing-documentation.md +80 -0
- package/docs/generation-performance.md +108 -0
- package/docs/generator-authoring.md +68 -5
- package/docs/generator-selection.md +50 -13
- package/docs/mintlify-import.md +71 -0
- package/docs/openapi-security.md +25 -0
- package/docs/project-format.md +331 -36
- package/docs/release-quality.md +158 -0
- package/docs/releasing.md +72 -0
- package/docs/review-workflows.md +51 -0
- package/docs/security-model.md +102 -39
- package/docs/troubleshooting.md +226 -93
- package/package.json +46 -15
- package/scripts/test-auto-screenshot.mjs +172 -0
- package/skills/doxloop-authoring/SKILL.md +242 -361
- package/skills/doxloop-authoring/references/existing-documentation.md +94 -0
- package/skills/doxloop-authoring/references/navigation-architecture.md +18 -7
- package/skills/doxloop-authoring/references/page-depth.md +169 -0
- package/skills/doxloop-authoring/references/project-format.md +106 -7
- package/skills/doxloop-authoring/references/quality.md +10 -0
- package/skills/doxloop-authoring/references/screenshot-manifest.md +113 -0
- package/skills/doxloop-authoring/references/screenshots.md +149 -235
- package/skills/doxloop-authoring/references/workflows.md +76 -0
- package/skills/doxloop-doxbrix/SKILL.md +38 -22
- package/skills/doxloop-doxbrix/references/api-endpoints.md +15 -14
- package/skills/doxloop-doxbrix/references/components.md +30 -3
- package/skills/doxloop-doxbrix/references/manifest.md +3 -2
- package/vendor/doxbrix-import/LICENSE +202 -0
- package/vendor/doxbrix-import/README.md +17 -0
- package/vendor/doxbrix-import/UPSTREAM.json +47 -0
- package/vendor/doxbrix-import/dist/docs/frontmatter.d.ts +13 -0
- package/vendor/doxbrix-import/dist/docs/frontmatter.js +83 -0
- package/vendor/doxbrix-import/dist/docs/import.d.ts +21 -0
- package/vendor/doxbrix-import/dist/docs/import.js +147 -0
- package/vendor/doxbrix-import/dist/docs/manifest.d.ts +163 -0
- package/vendor/doxbrix-import/dist/docs/manifest.js +64 -0
- package/vendor/doxbrix-import/dist/docs/project.d.ts +25 -0
- package/vendor/doxbrix-import/dist/docs/project.js +77 -0
- package/vendor/doxbrix-import/dist/docs/starter.d.ts +4 -0
- package/vendor/doxbrix-import/dist/docs/starter.js +11 -0
- package/vendor/doxbrix-import/dist/importer.d.ts +227 -0
- package/vendor/doxbrix-import/dist/importer.js +1567 -0
- package/vendor/doxbrix-import/dist/mintlify-openapi.d.ts +37 -0
- package/vendor/doxbrix-import/dist/mintlify-openapi.js +305 -0
- package/vendor/doxbrix-import/dist/safe-path.d.ts +9 -0
- package/vendor/doxbrix-import/dist/safe-path.js +47 -0
- package/dist/agents.d.ts.map +0 -1
- package/dist/agents.js.map +0 -1
- package/dist/args.d.ts.map +0 -1
- package/dist/args.js.map +0 -1
- package/dist/artifact-deploy.d.ts.map +0 -1
- package/dist/artifact-deploy.js.map +0 -1
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js.map +0 -1
- package/dist/author.d.ts.map +0 -1
- package/dist/author.js.map +0 -1
- package/dist/capture.d.ts.map +0 -1
- package/dist/capture.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/deploy.d.ts.map +0 -1
- package/dist/deploy.js.map +0 -1
- package/dist/deployment-visibility.d.ts.map +0 -1
- package/dist/deployment-visibility.js.map +0 -1
- package/dist/doctor.d.ts.map +0 -1
- package/dist/doctor.js.map +0 -1
- package/dist/doxbrix-markdown.d.ts.map +0 -1
- package/dist/doxbrix-markdown.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/fs.d.ts.map +0 -1
- package/dist/fs.js.map +0 -1
- package/dist/generator-api.d.ts.map +0 -1
- package/dist/generator-api.js.map +0 -1
- package/dist/generator-manager.d.ts.map +0 -1
- package/dist/generator-manager.js.map +0 -1
- package/dist/generator-runtime.d.ts.map +0 -1
- package/dist/generator-runtime.js.map +0 -1
- package/dist/generators.d.ts.map +0 -1
- package/dist/generators.js.map +0 -1
- package/dist/interactive.d.ts.map +0 -1
- package/dist/interactive.js.map +0 -1
- package/dist/preview.d.ts.map +0 -1
- package/dist/preview.js.map +0 -1
- package/dist/progress.d.ts.map +0 -1
- package/dist/progress.js.map +0 -1
- package/dist/project.d.ts.map +0 -1
- package/dist/project.js.map +0 -1
- package/dist/prompts.d.ts.map +0 -1
- package/dist/prompts.js.map +0 -1
- package/dist/settings.d.ts.map +0 -1
- package/dist/settings.js.map +0 -1
- package/dist/sync.d.ts.map +0 -1
- package/dist/sync.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/validation.d.ts.map +0 -1
- package/dist/validation.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
package/dist/prompts.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createInterface } from 'node:readline/promises';
|
|
2
|
+
import { Writable } from 'node:stream';
|
|
2
3
|
import { styleText } from 'node:util';
|
|
3
4
|
import { booleanFlag } from './args.js';
|
|
4
5
|
import { DoxloopError } from './errors.js';
|
|
@@ -38,6 +39,7 @@ class LineReader {
|
|
|
38
39
|
pending;
|
|
39
40
|
canceled = false;
|
|
40
41
|
prompt;
|
|
42
|
+
cancel;
|
|
41
43
|
constructor(io) {
|
|
42
44
|
this.prompt = createInterface({ input: io.input, output: io.output });
|
|
43
45
|
this.prompt.on('line', (line) => {
|
|
@@ -50,7 +52,7 @@ class LineReader {
|
|
|
50
52
|
this.queue.push(line);
|
|
51
53
|
}
|
|
52
54
|
});
|
|
53
|
-
|
|
55
|
+
this.cancel = () => {
|
|
54
56
|
this.canceled = true;
|
|
55
57
|
if (this.pending) {
|
|
56
58
|
const waiting = this.pending;
|
|
@@ -58,8 +60,8 @@ class LineReader {
|
|
|
58
60
|
waiting.reject(promptCanceled());
|
|
59
61
|
}
|
|
60
62
|
};
|
|
61
|
-
this.prompt.on('SIGINT', cancel);
|
|
62
|
-
this.prompt.on('close', cancel);
|
|
63
|
+
this.prompt.on('SIGINT', this.cancel);
|
|
64
|
+
this.prompt.on('close', this.cancel);
|
|
63
65
|
}
|
|
64
66
|
next(io, query) {
|
|
65
67
|
io.output.write(query);
|
|
@@ -73,7 +75,10 @@ class LineReader {
|
|
|
73
75
|
});
|
|
74
76
|
}
|
|
75
77
|
close() {
|
|
76
|
-
|
|
78
|
+
// Keep readline's own `close` listeners intact. They detach its input and
|
|
79
|
+
// keypress handlers; removing all listeners here leaves an old interface
|
|
80
|
+
// echoing every subsequent prompt, so typed characters appear repeatedly.
|
|
81
|
+
this.prompt.removeListener('close', this.cancel);
|
|
77
82
|
this.prompt.close();
|
|
78
83
|
}
|
|
79
84
|
}
|
|
@@ -108,6 +113,30 @@ export async function promptText(options) {
|
|
|
108
113
|
reader.close();
|
|
109
114
|
}
|
|
110
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Read one line without echoing it, for passwords. The readline interface
|
|
118
|
+
* writes its echo to a sink, so a terminal shows nothing while typing; piped
|
|
119
|
+
* input is read as a plain line.
|
|
120
|
+
*/
|
|
121
|
+
export async function promptSecret(options) {
|
|
122
|
+
const io = options.io ?? defaultIo();
|
|
123
|
+
io.output.write(`${color(io, 'cyan', '◆')} ${options.message} ${color(io, 'dim', '(typing is hidden)')}\n`);
|
|
124
|
+
io.output.write(`${color(io, 'dim', '│')} `);
|
|
125
|
+
const sink = new Writable({ write(_chunk, _encoding, callback) { callback(); } });
|
|
126
|
+
const reader = createInterface({ input: io.input, output: sink, terminal: io.input.isTTY === true });
|
|
127
|
+
try {
|
|
128
|
+
const line = await new Promise((resolve, reject) => {
|
|
129
|
+
reader.once('line', resolve);
|
|
130
|
+
reader.once('SIGINT', () => reject(promptCanceled()));
|
|
131
|
+
reader.once('close', () => reject(promptCanceled()));
|
|
132
|
+
});
|
|
133
|
+
io.output.write('\n');
|
|
134
|
+
return line;
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
reader.close();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
111
140
|
export async function promptConfirm(options) {
|
|
112
141
|
const io = options.io ?? defaultIo();
|
|
113
142
|
const fallback = options.initial ?? true;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ValidationIssue } from './types.js';
|
|
2
|
+
export interface ReplayResult {
|
|
3
|
+
/** Where the copy the checks ran on was left (removed unless `keep`). */
|
|
4
|
+
workspace: string;
|
|
5
|
+
/** Validation errors before any repair; what the run itself would have shown. */
|
|
6
|
+
before: {
|
|
7
|
+
errors: number;
|
|
8
|
+
warnings: number;
|
|
9
|
+
issues: ValidationIssue[];
|
|
10
|
+
};
|
|
11
|
+
/** Doxloop's repairs, in order. */
|
|
12
|
+
repairs: string[];
|
|
13
|
+
/** Screenshot defects the tolerant check recorded. */
|
|
14
|
+
screenshotDefects: string[];
|
|
15
|
+
/** Validation after every repair; what the accept check sees. */
|
|
16
|
+
after: {
|
|
17
|
+
errors: number;
|
|
18
|
+
warnings: number;
|
|
19
|
+
issues: ValidationIssue[];
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface ReplayOptions {
|
|
23
|
+
/** Leave the working copy in place for inspection. */
|
|
24
|
+
keep?: boolean;
|
|
25
|
+
log?: (line: string) => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Run the post-authoring pipeline over a copy of `<run-dir>/workspace` using
|
|
29
|
+
* the run's approved plan. The original run folder is never modified.
|
|
30
|
+
*/
|
|
31
|
+
export declare function replayRun(runDirectory: string, options?: ReplayOptions): Promise<ReplayResult>;
|
|
32
|
+
//# sourceMappingURL=proposal-replay.d.ts.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replay the end-of-run checks over a recorded run workspace.
|
|
3
|
+
*
|
|
4
|
+
* Every "the run succeeded but the proposal is blocked" failure came from the
|
|
5
|
+
* interaction of steps that each had unit tests: reuse of planning captures,
|
|
6
|
+
* the screenshot validator, the retake, the tolerant downgrade, the post-pass
|
|
7
|
+
* and the accept check. They were only ever exercised together on a real
|
|
8
|
+
* half-hour run. A replay runs exactly that pipeline over a copy of a run's
|
|
9
|
+
* workspace in seconds, so a change to any gate is verified against real
|
|
10
|
+
* manifests and pages before the next run — and a run folder that failed
|
|
11
|
+
* becomes a permanent regression fixture.
|
|
12
|
+
*/
|
|
13
|
+
import { cp, mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
14
|
+
import { tmpdir } from 'node:os';
|
|
15
|
+
import { join, resolve } from 'node:path';
|
|
16
|
+
import { applyAuthoringPostPass, removeSupersededStarterPages } from './authoring-postpass.js';
|
|
17
|
+
import { normalizeCaptureManifest } from './batch-artifacts.js';
|
|
18
|
+
import { DoxloopError } from './errors.js';
|
|
19
|
+
import { pathExists } from './fs.js';
|
|
20
|
+
import { loadProject } from './project.js';
|
|
21
|
+
import { SCREENSHOT_MANIFEST_FILE, adoptCapturedImages, collapseDuplicateCaptures, embedMissingCaptures, validateScreenshotManifest, } from './screenshot-workflow.js';
|
|
22
|
+
import { formatValidation, validateProject } from './validation.js';
|
|
23
|
+
/**
|
|
24
|
+
* Run the post-authoring pipeline over a copy of `<run-dir>/workspace` using
|
|
25
|
+
* the run's approved plan. The original run folder is never modified.
|
|
26
|
+
*/
|
|
27
|
+
export async function replayRun(runDirectory, options = {}) {
|
|
28
|
+
const log = options.log ?? (() => { });
|
|
29
|
+
const source = resolve(runDirectory);
|
|
30
|
+
const workspaceSource = join(source, 'workspace');
|
|
31
|
+
if (!(await pathExists(workspaceSource)))
|
|
32
|
+
throw new DoxloopError(`${runDirectory} has no workspace folder to replay.`);
|
|
33
|
+
let plan;
|
|
34
|
+
try {
|
|
35
|
+
plan = JSON.parse(await readFile(join(source, 'approved-plan.json'), 'utf8'));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
throw new DoxloopError(`${runDirectory} has no readable approved-plan.json; only plan-first runs can be replayed.`);
|
|
39
|
+
}
|
|
40
|
+
const workspace = await mkdtemp(join(tmpdir(), 'doxloop-replay-'));
|
|
41
|
+
await cp(workspaceSource, workspace, { recursive: true });
|
|
42
|
+
const repairs = [];
|
|
43
|
+
const say = (line) => { repairs.push(line); log(line); };
|
|
44
|
+
try {
|
|
45
|
+
const project = await loadProject(workspace);
|
|
46
|
+
const before = await validateProject(workspace);
|
|
47
|
+
log(`Before repairs: ${before.errors} error${before.errors === 1 ? '' : 's'}, ${before.warnings} warning${before.warnings === 1 ? '' : 's'}.`);
|
|
48
|
+
// 1. Screenshots: the same order as the end of a generation run.
|
|
49
|
+
const normalized = await normalizeCaptureManifest(workspace, SCREENSHOT_MANIFEST_FILE);
|
|
50
|
+
for (const step of normalized.verified)
|
|
51
|
+
say(`manifest: ${step} verified on disk.`);
|
|
52
|
+
for (const reset of normalized.reset)
|
|
53
|
+
say(`manifest: ${reset.step} reset to planned (${reset.reason}).`);
|
|
54
|
+
const adopted = await adoptCapturedImages(workspace, project.generator, plan);
|
|
55
|
+
if (adopted.length > 0)
|
|
56
|
+
say(`captures: adopted ${adopted.length} unrecorded image${adopted.length === 1 ? '' : 's'}.`);
|
|
57
|
+
const dropped = await collapseDuplicateCaptures(workspace, plan);
|
|
58
|
+
if (dropped.length > 0)
|
|
59
|
+
say(`captures: quarantined ${dropped.length} duplicate${dropped.length === 1 ? '' : 's'}.`);
|
|
60
|
+
const placed = await embedMissingCaptures(workspace, plan);
|
|
61
|
+
if (placed.length > 0)
|
|
62
|
+
say(`captures: embedded ${placed.length} unplaced image${placed.length === 1 ? '' : 's'}.`);
|
|
63
|
+
const screenshots = await validateScreenshotManifest(workspace, plan, plan.execution?.screenshots ?? 'auto', { tolerateDefects: true });
|
|
64
|
+
for (const defect of screenshots.defects)
|
|
65
|
+
say(`screenshot defect: ${defect}`);
|
|
66
|
+
// 2. The deterministic repairs the accept path relies on.
|
|
67
|
+
const report = await applyAuthoringPostPass({ workspace, project, plan, pages: plan.pages, unlinkUnresolved: true, pruneEmptySpaces: true });
|
|
68
|
+
for (const line of report.repairs)
|
|
69
|
+
say(`post-pass: ${line}`);
|
|
70
|
+
for (const line of report.problems)
|
|
71
|
+
say(`post-pass note: ${line}`);
|
|
72
|
+
const middle = await validateProject(workspace);
|
|
73
|
+
const starters = await removeSupersededStarterPages(workspace, middle.issues);
|
|
74
|
+
if (starters.length > 0) {
|
|
75
|
+
for (const file of starters)
|
|
76
|
+
say(`starter: removed ${file}.`);
|
|
77
|
+
const again = await applyAuthoringPostPass({ workspace, project, plan, pages: plan.pages, unlinkUnresolved: true, pruneEmptySpaces: true });
|
|
78
|
+
for (const line of again.repairs)
|
|
79
|
+
say(`post-pass: ${line}`);
|
|
80
|
+
}
|
|
81
|
+
// 3. What "Accept all" checks.
|
|
82
|
+
const after = await validateProject(workspace);
|
|
83
|
+
log(`After repairs: ${after.errors} error${after.errors === 1 ? '' : 's'}, ${after.warnings} warning${after.warnings === 1 ? '' : 's'}.`);
|
|
84
|
+
if (after.errors > 0)
|
|
85
|
+
log(formatValidation({ ...after, issues: after.issues.filter((issue) => issue.severity === 'error') }));
|
|
86
|
+
return {
|
|
87
|
+
workspace,
|
|
88
|
+
before: { errors: before.errors, warnings: before.warnings, issues: before.issues },
|
|
89
|
+
repairs,
|
|
90
|
+
screenshotDefects: screenshots.defects,
|
|
91
|
+
after: { errors: after.errors, warnings: after.warnings, issues: after.issues },
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
if (!options.keep)
|
|
96
|
+
await rm(workspace, { recursive: true, force: true });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=proposal-replay.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DoxloopProject, QualityCheck, ReaderVerificationMetadata } from './types.js';
|
|
2
|
+
export declare const VERIFICATION_METADATA_FILE: string;
|
|
3
|
+
export declare function readVerificationMetadata(root: string): Promise<ReaderVerificationMetadata | undefined>;
|
|
4
|
+
export declare function reverifyClaims(root: string, project: DoxloopProject, publishMetadata: boolean): Promise<{
|
|
5
|
+
checks: QualityCheck[];
|
|
6
|
+
metadata: ReaderVerificationMetadata;
|
|
7
|
+
}>;
|
|
8
|
+
//# sourceMappingURL=quality-claims.d.ts.map
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join, relative, resolve } from 'node:path';
|
|
3
|
+
import { computeDrift } from './drift.js';
|
|
4
|
+
import { readEvidenceMap } from './evidence.js';
|
|
5
|
+
import { QUALITY_CODES } from './quality-contract.js';
|
|
6
|
+
import { readSyncState } from './sync.js';
|
|
7
|
+
import { pathExists } from './fs.js';
|
|
8
|
+
import { loadOpenApiSource } from './openapi.js';
|
|
9
|
+
import { sourceKind } from './project.js';
|
|
10
|
+
export const VERIFICATION_METADATA_FILE = join('.doxloop', 'verification-metadata.json');
|
|
11
|
+
export async function readVerificationMetadata(root) {
|
|
12
|
+
const path = join(root, VERIFICATION_METADATA_FILE);
|
|
13
|
+
if (!(await pathExists(path)))
|
|
14
|
+
return undefined;
|
|
15
|
+
try {
|
|
16
|
+
const value = JSON.parse(await readFile(path, 'utf8'));
|
|
17
|
+
return value.schemaVersion === 1 && value.pages && typeof value.pages === 'object' ? value : undefined;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export async function reverifyClaims(root, project, publishMetadata) {
|
|
24
|
+
const [map, drift, sync] = await Promise.all([readEvidenceMap(root), computeDrift(root, project), readSyncState(root)]);
|
|
25
|
+
const checks = [];
|
|
26
|
+
const metadata = { schemaVersion: 1, generatedAt: new Date().toISOString(), pages: {} };
|
|
27
|
+
if (!map) {
|
|
28
|
+
checks.push({ code: QUALITY_CODES.claimNeedsHuman, category: 'claims', status: 'warning', message: 'No evidence map exists, so reader-facing claims cannot be reverified.' });
|
|
29
|
+
return { checks, metadata };
|
|
30
|
+
}
|
|
31
|
+
const stale = new Set(drift.pages.map((page) => page.page));
|
|
32
|
+
for (const [page, evidence] of Object.entries(map.pages)) {
|
|
33
|
+
const pageIsStale = stale.has(page) || drift.status === 'unknown';
|
|
34
|
+
const hasRevision = evidence.sources.length > 0 && evidence.sources.every((source) => !!evidence.verifiedAt?.[source.source]);
|
|
35
|
+
const fallback = defaultState(evidence.confidence, pageIsStale, hasRevision);
|
|
36
|
+
const currentEvidence = await claimEvidenceText(root, project, evidence.sources);
|
|
37
|
+
const claimStates = (evidence.claims ?? []).map((claim) => {
|
|
38
|
+
const recorded = evidence.claimVerification?.[claim];
|
|
39
|
+
return semanticClaimState(claim, currentEvidence, pageIsStale, recorded, fallback);
|
|
40
|
+
});
|
|
41
|
+
const state = worstState(claimStates.length > 0 ? claimStates : [fallback]);
|
|
42
|
+
const revisions = Object.fromEntries(evidence.sources.flatMap((source) => {
|
|
43
|
+
const revision = evidence.verifiedAt?.[source.source];
|
|
44
|
+
return revision ? [[source.source, revision]] : [];
|
|
45
|
+
}));
|
|
46
|
+
const dates = Object.values(evidence.verifiedOn ?? {}).filter(Boolean).sort();
|
|
47
|
+
const verifiedOn = dates.at(0);
|
|
48
|
+
metadata.pages[page] = {
|
|
49
|
+
state,
|
|
50
|
+
confidence: evidence.confidence ?? 'needs-human',
|
|
51
|
+
...(verifiedOn ? { verifiedOn } : {}),
|
|
52
|
+
revisions,
|
|
53
|
+
locale: project.documentation.locale,
|
|
54
|
+
};
|
|
55
|
+
for (const [index, claim] of (evidence.claims ?? []).entries())
|
|
56
|
+
checks.push(checkForState(page, claim, claimStates[index] ?? state));
|
|
57
|
+
if ((evidence.claims ?? []).length === 0)
|
|
58
|
+
checks.push(checkForState(page, 'Page evidence has no claim-level entries.', state));
|
|
59
|
+
}
|
|
60
|
+
if (publishMetadata) {
|
|
61
|
+
await mkdir(join(root, '.doxloop'), { recursive: true });
|
|
62
|
+
await writeFile(join(root, VERIFICATION_METADATA_FILE), `${JSON.stringify(metadata, null, 2)}\n`, 'utf8');
|
|
63
|
+
}
|
|
64
|
+
return { checks, metadata };
|
|
65
|
+
}
|
|
66
|
+
async function claimEvidenceText(root, project, entries) {
|
|
67
|
+
const chunks = [];
|
|
68
|
+
const byName = new Map(project.sources.map((source) => [source.name, source]));
|
|
69
|
+
for (const entry of entries) {
|
|
70
|
+
const source = byName.get(entry.source);
|
|
71
|
+
if (!source)
|
|
72
|
+
continue;
|
|
73
|
+
// The crawled pages of an existing documentation site are what is being
|
|
74
|
+
// rewritten, not proof: a claim they alone support stays at the page's
|
|
75
|
+
// recorded confidence instead of being verified against itself.
|
|
76
|
+
if (sourceKind(source) === 'docs-site')
|
|
77
|
+
continue;
|
|
78
|
+
if (sourceKind(source) === 'openapi') {
|
|
79
|
+
try {
|
|
80
|
+
const loaded = await loadOpenApiSource(root, source);
|
|
81
|
+
for (const identifier of entry.operations ?? []) {
|
|
82
|
+
const operation = loaded.snapshot.operations[identifier];
|
|
83
|
+
if (operation)
|
|
84
|
+
chunks.push(identifier, operation.full);
|
|
85
|
+
const operationMatch = /^(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)\s+(\/\S+)$/i.exec(identifier);
|
|
86
|
+
if (operationMatch) {
|
|
87
|
+
const paths = objectRecord(loaded.document.paths);
|
|
88
|
+
const pathItem = objectRecord(paths[operationMatch[2]]);
|
|
89
|
+
const raw = pathItem[operationMatch[1].toLowerCase()];
|
|
90
|
+
if (raw)
|
|
91
|
+
chunks.push(identifier, JSON.stringify(raw));
|
|
92
|
+
}
|
|
93
|
+
const schema = loaded.snapshot.schemas[identifier.replace(/^schema:/, '')];
|
|
94
|
+
if (schema)
|
|
95
|
+
chunks.push(identifier, schema);
|
|
96
|
+
}
|
|
97
|
+
if (!(entry.operations?.length))
|
|
98
|
+
chunks.push(JSON.stringify(loaded.document).slice(0, 250_000));
|
|
99
|
+
}
|
|
100
|
+
catch { /* Unavailable evidence remains a needs-human result. */ }
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const sourceRoot = resolve(root, source.path);
|
|
104
|
+
for (const candidate of entry.paths ?? []) {
|
|
105
|
+
if (/[*?[\]]/.test(candidate))
|
|
106
|
+
continue;
|
|
107
|
+
const path = resolve(sourceRoot, candidate);
|
|
108
|
+
const rel = relative(sourceRoot, path);
|
|
109
|
+
if (rel === '..' || rel.startsWith('../') || rel.startsWith('..\\') || !(await pathExists(path)))
|
|
110
|
+
continue;
|
|
111
|
+
try {
|
|
112
|
+
chunks.push((await readFile(path, 'utf8')).slice(0, 250_000));
|
|
113
|
+
}
|
|
114
|
+
catch { /* Binary or unreadable evidence is ignored. */ }
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return chunks.join('\n').toLowerCase();
|
|
118
|
+
}
|
|
119
|
+
function objectRecord(value) {
|
|
120
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
121
|
+
}
|
|
122
|
+
function semanticClaimState(claim, evidence, stale, recorded, fallback) {
|
|
123
|
+
if (recorded === 'contradicted')
|
|
124
|
+
return 'contradicted';
|
|
125
|
+
if (!evidence)
|
|
126
|
+
return stale ? 'needs-human' : (recorded ?? fallback);
|
|
127
|
+
const facts = extractFacts(claim);
|
|
128
|
+
if (facts.length === 0)
|
|
129
|
+
return stale ? 'needs-human' : (recorded ?? fallback);
|
|
130
|
+
const present = facts.filter((fact) => evidence.includes(fact.value.toLowerCase()));
|
|
131
|
+
if (present.length === facts.length)
|
|
132
|
+
return 'verified';
|
|
133
|
+
const missing = facts.filter((fact) => !present.includes(fact));
|
|
134
|
+
const categories = new Set(missing.map((fact) => fact.kind));
|
|
135
|
+
const evidenceFacts = extractFacts(evidence);
|
|
136
|
+
const comparable = evidenceFacts.filter((fact) => categories.has(fact.kind));
|
|
137
|
+
if (comparable.length > 0)
|
|
138
|
+
return 'contradicted';
|
|
139
|
+
return 'needs-human';
|
|
140
|
+
}
|
|
141
|
+
function extractFacts(value) {
|
|
142
|
+
const facts = [];
|
|
143
|
+
const patterns = [
|
|
144
|
+
['http-status', /\b[1-5]\d\d\b/g],
|
|
145
|
+
['http-operation', /\b(?:GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)\s+\/[A-Za-z0-9_{}./:-]+/gi],
|
|
146
|
+
['cli-option', /--[a-z0-9][a-z0-9-]*/gi],
|
|
147
|
+
['environment', /\b[A-Z][A-Z0-9]*_[A-Z0-9_]+\b/g],
|
|
148
|
+
['version', /\bv?\d+\.\d+(?:\.\d+)?\b/gi],
|
|
149
|
+
];
|
|
150
|
+
for (const [kind, pattern] of patterns)
|
|
151
|
+
for (const match of value.matchAll(pattern))
|
|
152
|
+
facts.push({ kind, value: match[0] });
|
|
153
|
+
return [...new Map(facts.map((fact) => [`${fact.kind}:${fact.value.toLowerCase()}`, fact])).values()];
|
|
154
|
+
}
|
|
155
|
+
function defaultState(confidence, stale, hasRevision) {
|
|
156
|
+
if (stale || confidence === 'needs-human' || confidence === undefined)
|
|
157
|
+
return 'needs-human';
|
|
158
|
+
return confidence === 'verified' && !hasRevision ? 'inferred' : confidence;
|
|
159
|
+
}
|
|
160
|
+
function worstState(states) {
|
|
161
|
+
const order = ['verified', 'inferred', 'needs-human', 'contradicted'];
|
|
162
|
+
return states.reduce((worst, state) => order.indexOf(state) > order.indexOf(worst) ? state : worst, 'verified');
|
|
163
|
+
}
|
|
164
|
+
function checkForState(file, claim, state) {
|
|
165
|
+
const code = state === 'verified' ? QUALITY_CODES.claimVerified : state === 'inferred' ? QUALITY_CODES.claimInferred : state === 'contradicted' ? QUALITY_CODES.claimContradicted : QUALITY_CODES.claimNeedsHuman;
|
|
166
|
+
return { code, category: 'claims', status: state === 'verified' ? 'pass' : state === 'contradicted' ? 'fail' : 'warning', message: `${state}: ${claim}`, file };
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=quality-claims.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { QualityConfig } from './types.js';
|
|
2
|
+
export declare const QUALITY_CONFIG_FILE: string;
|
|
3
|
+
export declare const DEFAULT_QUALITY_CONFIG: QualityConfig;
|
|
4
|
+
export declare function loadQualityConfig(root: string): Promise<QualityConfig>;
|
|
5
|
+
//# sourceMappingURL=quality-config.d.ts.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { pathExists, readJson } from './fs.js';
|
|
3
|
+
import { DoxloopError } from './errors.js';
|
|
4
|
+
export const QUALITY_CONFIG_FILE = join('.doxloop', 'quality.json');
|
|
5
|
+
export const DEFAULT_QUALITY_CONFIG = {
|
|
6
|
+
schemaVersion: 1,
|
|
7
|
+
links: { mode: 'online', allowHosts: [], ignore: [], timeoutMs: 8_000, retries: 2, cacheHours: 24 },
|
|
8
|
+
examples: { enabled: false },
|
|
9
|
+
rendered: {
|
|
10
|
+
enabled: false,
|
|
11
|
+
routes: ['/'],
|
|
12
|
+
viewports: [
|
|
13
|
+
{ name: 'desktop', width: 1440, height: 900 },
|
|
14
|
+
{ name: 'mobile', width: 390, height: 844 },
|
|
15
|
+
],
|
|
16
|
+
themes: ['light', 'dark'],
|
|
17
|
+
maximumDiffRatio: 0.001,
|
|
18
|
+
},
|
|
19
|
+
lint: { maximumTitleLength: 72, maximumNavigationLabelLength: 42 },
|
|
20
|
+
readerVerification: { enabled: false },
|
|
21
|
+
suppressions: [],
|
|
22
|
+
ratchet: { enabled: false, baselineFile: '.doxloop/quality-baseline.json' },
|
|
23
|
+
};
|
|
24
|
+
export async function loadQualityConfig(root) {
|
|
25
|
+
const path = join(root, QUALITY_CONFIG_FILE);
|
|
26
|
+
if (!(await pathExists(path)))
|
|
27
|
+
return structuredClone(DEFAULT_QUALITY_CONFIG);
|
|
28
|
+
const input = await readJson(path);
|
|
29
|
+
if (!isQualityConfig(input))
|
|
30
|
+
throw new DoxloopError(`${QUALITY_CONFIG_FILE} has an unsupported format.`);
|
|
31
|
+
return {
|
|
32
|
+
schemaVersion: 1,
|
|
33
|
+
links: { ...DEFAULT_QUALITY_CONFIG.links, ...input.links },
|
|
34
|
+
examples: { ...DEFAULT_QUALITY_CONFIG.examples, ...input.examples },
|
|
35
|
+
rendered: { ...DEFAULT_QUALITY_CONFIG.rendered, ...input.rendered },
|
|
36
|
+
lint: { ...DEFAULT_QUALITY_CONFIG.lint, ...input.lint },
|
|
37
|
+
readerVerification: { ...DEFAULT_QUALITY_CONFIG.readerVerification, ...input.readerVerification },
|
|
38
|
+
suppressions: input.suppressions ?? [],
|
|
39
|
+
ratchet: { ...DEFAULT_QUALITY_CONFIG.ratchet, ...input.ratchet },
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function isQualityConfig(value) {
|
|
43
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
44
|
+
return false;
|
|
45
|
+
const config = value;
|
|
46
|
+
if (config.schemaVersion !== 1)
|
|
47
|
+
return false;
|
|
48
|
+
if (config.links) {
|
|
49
|
+
if (!object(config.links))
|
|
50
|
+
return false;
|
|
51
|
+
if (config.links.mode !== undefined && !['online', 'offline'].includes(config.links.mode))
|
|
52
|
+
return false;
|
|
53
|
+
if (!textList(config.links.allowHosts) || !textList(config.links.ignore))
|
|
54
|
+
return false;
|
|
55
|
+
if (!bounded(config.links.timeoutMs, 500, 60_000) || !bounded(config.links.retries, 0, 5) || !bounded(config.links.cacheHours, 1, 720))
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
if (config.examples && (!object(config.examples) || !optionalBoolean(config.examples.enabled)))
|
|
59
|
+
return false;
|
|
60
|
+
if (config.readerVerification && (!object(config.readerVerification) || !optionalBoolean(config.readerVerification.enabled)))
|
|
61
|
+
return false;
|
|
62
|
+
if (config.suppressions !== undefined && (!Array.isArray(config.suppressions) || !config.suppressions.every((item) => object(item) && typeof item.code === 'string' && item.code.trim() !== '' && typeof item.reason === 'string' && item.reason.trim() !== '' && (item.file === undefined || typeof item.file === 'string') && (item.expires === undefined || typeof item.expires === 'string' && !Number.isNaN(Date.parse(item.expires))))))
|
|
63
|
+
return false;
|
|
64
|
+
if (config.ratchet && (!object(config.ratchet) || !optionalBoolean(config.ratchet.enabled) || (config.ratchet.baselineFile !== undefined && typeof config.ratchet.baselineFile !== 'string')))
|
|
65
|
+
return false;
|
|
66
|
+
if (config.rendered) {
|
|
67
|
+
if (!object(config.rendered) || !optionalBoolean(config.rendered.enabled) || !textList(config.rendered.routes))
|
|
68
|
+
return false;
|
|
69
|
+
if (config.rendered.viewports !== undefined && (!Array.isArray(config.rendered.viewports) || !config.rendered.viewports.every((item) => object(item) && typeof item.name === 'string' && bounded(item.width, 320, 3840) && bounded(item.height, 320, 2160))))
|
|
70
|
+
return false;
|
|
71
|
+
if (config.rendered.themes !== undefined && (!Array.isArray(config.rendered.themes) || !config.rendered.themes.every((item) => item === 'light' || item === 'dark')))
|
|
72
|
+
return false;
|
|
73
|
+
if (config.rendered.maximumDiffRatio !== undefined && (typeof config.rendered.maximumDiffRatio !== 'number' || config.rendered.maximumDiffRatio < 0 || config.rendered.maximumDiffRatio > 1))
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
if (config.lint && (!object(config.lint) || !bounded(config.lint.maximumTitleLength, 20, 200) || !bounded(config.lint.maximumNavigationLabelLength, 10, 120)))
|
|
77
|
+
return false;
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
function object(value) { return !!value && typeof value === 'object' && !Array.isArray(value); }
|
|
81
|
+
function textList(value) { return value === undefined || (Array.isArray(value) && value.every((item) => typeof item === 'string' && item.trim() !== '')); }
|
|
82
|
+
function bounded(value, minimum, maximum) { return value === undefined || (Number.isInteger(value) && Number(value) >= minimum && Number(value) <= maximum); }
|
|
83
|
+
function optionalBoolean(value) { return value === undefined || typeof value === 'boolean'; }
|
|
84
|
+
//# sourceMappingURL=quality-config.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { QualityCheckCategory } from './types.js';
|
|
2
|
+
export declare const QUALITY_CONTRACT_VERSION: "1.0.0";
|
|
3
|
+
/**
|
|
4
|
+
* Public issue codes are append-only within contract v1. Text may change, but
|
|
5
|
+
* automation can safely baseline, suppress, and ratchet these identifiers.
|
|
6
|
+
*/
|
|
7
|
+
export declare const QUALITY_CODES: {
|
|
8
|
+
readonly validation: "quality.validation";
|
|
9
|
+
readonly buildPassed: "quality.build.passed";
|
|
10
|
+
readonly buildFailed: "quality.build.failed";
|
|
11
|
+
readonly linkOk: "quality.link.ok";
|
|
12
|
+
readonly linkBroken: "quality.link.broken";
|
|
13
|
+
readonly linkUnavailable: "quality.link.unavailable";
|
|
14
|
+
readonly linkSkipped: "quality.link.skipped";
|
|
15
|
+
readonly examplePassed: "quality.example.passed";
|
|
16
|
+
readonly exampleFailed: "quality.example.failed";
|
|
17
|
+
readonly exampleSourceVerified: "quality.example.source-verified";
|
|
18
|
+
readonly schemaPassed: "quality.schema.passed";
|
|
19
|
+
readonly schemaIssue: "quality.schema.issue";
|
|
20
|
+
readonly accessibilityPassed: "quality.accessibility.passed";
|
|
21
|
+
readonly accessibilityIssue: "quality.accessibility.issue";
|
|
22
|
+
readonly accessibilityManual: "quality.accessibility.manual-review";
|
|
23
|
+
readonly visualMatched: "quality.visual.matched";
|
|
24
|
+
readonly visualChanged: "quality.visual.changed";
|
|
25
|
+
readonly visualCreated: "quality.visual.created";
|
|
26
|
+
readonly visualSkipped: "quality.visual.skipped";
|
|
27
|
+
readonly lintIssue: "quality.lint.issue";
|
|
28
|
+
readonly lintPassed: "quality.lint.passed";
|
|
29
|
+
readonly claimVerified: "quality.claim.verified";
|
|
30
|
+
readonly claimInferred: "quality.claim.inferred";
|
|
31
|
+
readonly claimContradicted: "quality.claim.contradicted";
|
|
32
|
+
readonly claimNeedsHuman: "quality.claim.needs-human";
|
|
33
|
+
readonly ratchetBaselineMissing: "quality.ratchet.baseline-missing";
|
|
34
|
+
readonly ratchetBaselineInvalid: "quality.ratchet.baseline-invalid";
|
|
35
|
+
};
|
|
36
|
+
export declare const QUALITY_CATEGORIES: readonly QualityCheckCategory[];
|
|
37
|
+
//# sourceMappingURL=quality-contract.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export const QUALITY_CONTRACT_VERSION = '1.0.0';
|
|
2
|
+
/**
|
|
3
|
+
* Public issue codes are append-only within contract v1. Text may change, but
|
|
4
|
+
* automation can safely baseline, suppress, and ratchet these identifiers.
|
|
5
|
+
*/
|
|
6
|
+
export const QUALITY_CODES = {
|
|
7
|
+
validation: 'quality.validation',
|
|
8
|
+
buildPassed: 'quality.build.passed',
|
|
9
|
+
buildFailed: 'quality.build.failed',
|
|
10
|
+
linkOk: 'quality.link.ok',
|
|
11
|
+
linkBroken: 'quality.link.broken',
|
|
12
|
+
linkUnavailable: 'quality.link.unavailable',
|
|
13
|
+
linkSkipped: 'quality.link.skipped',
|
|
14
|
+
examplePassed: 'quality.example.passed',
|
|
15
|
+
exampleFailed: 'quality.example.failed',
|
|
16
|
+
exampleSourceVerified: 'quality.example.source-verified',
|
|
17
|
+
schemaPassed: 'quality.schema.passed',
|
|
18
|
+
schemaIssue: 'quality.schema.issue',
|
|
19
|
+
accessibilityPassed: 'quality.accessibility.passed',
|
|
20
|
+
accessibilityIssue: 'quality.accessibility.issue',
|
|
21
|
+
accessibilityManual: 'quality.accessibility.manual-review',
|
|
22
|
+
visualMatched: 'quality.visual.matched',
|
|
23
|
+
visualChanged: 'quality.visual.changed',
|
|
24
|
+
visualCreated: 'quality.visual.created',
|
|
25
|
+
visualSkipped: 'quality.visual.skipped',
|
|
26
|
+
lintIssue: 'quality.lint.issue',
|
|
27
|
+
lintPassed: 'quality.lint.passed',
|
|
28
|
+
claimVerified: 'quality.claim.verified',
|
|
29
|
+
claimInferred: 'quality.claim.inferred',
|
|
30
|
+
claimContradicted: 'quality.claim.contradicted',
|
|
31
|
+
claimNeedsHuman: 'quality.claim.needs-human',
|
|
32
|
+
ratchetBaselineMissing: 'quality.ratchet.baseline-missing',
|
|
33
|
+
ratchetBaselineInvalid: 'quality.ratchet.baseline-invalid',
|
|
34
|
+
};
|
|
35
|
+
export const QUALITY_CATEGORIES = [
|
|
36
|
+
'validation',
|
|
37
|
+
'build',
|
|
38
|
+
'links',
|
|
39
|
+
'examples',
|
|
40
|
+
'schemas',
|
|
41
|
+
'accessibility',
|
|
42
|
+
'visual',
|
|
43
|
+
'lint',
|
|
44
|
+
'claims',
|
|
45
|
+
];
|
|
46
|
+
//# sourceMappingURL=quality-contract.js.map
|