comber 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.
Files changed (158) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +732 -0
  3. package/action.yml +279 -0
  4. package/comber.config.example.json +64 -0
  5. package/dist/api/classify.js +99 -0
  6. package/dist/api/classify.js.map +1 -0
  7. package/dist/api/driver.js +235 -0
  8. package/dist/api/driver.js.map +1 -0
  9. package/dist/api/graphql-driver.js +150 -0
  10. package/dist/api/graphql-driver.js.map +1 -0
  11. package/dist/api/graphql.js +115 -0
  12. package/dist/api/graphql.js.map +1 -0
  13. package/dist/api/har-driver.js +167 -0
  14. package/dist/api/har-driver.js.map +1 -0
  15. package/dist/api/har.js +100 -0
  16. package/dist/api/har.js.map +1 -0
  17. package/dist/api/load.js +83 -0
  18. package/dist/api/load.js.map +1 -0
  19. package/dist/api/openapi.js +159 -0
  20. package/dist/api/openapi.js.map +1 -0
  21. package/dist/api/request.js +141 -0
  22. package/dist/api/request.js.map +1 -0
  23. package/dist/api/schema.js +126 -0
  24. package/dist/api/schema.js.map +1 -0
  25. package/dist/cli.js +352 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/comment.js +164 -0
  28. package/dist/comment.js.map +1 -0
  29. package/dist/config.js +664 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/connectors/builtin/discord.js +55 -0
  32. package/dist/connectors/builtin/github-checks.js +53 -0
  33. package/dist/connectors/builtin/junit.js +70 -0
  34. package/dist/connectors/builtin/linear.js +58 -0
  35. package/dist/connectors/builtin/slack.js +60 -0
  36. package/dist/connectors/builtin/webhook.js +39 -0
  37. package/dist/connectors/index.js +3 -0
  38. package/dist/connectors/loader.js +72 -0
  39. package/dist/connectors/registry.js +28 -0
  40. package/dist/connectors/types.js +23 -0
  41. package/dist/core/action-gate.js +48 -0
  42. package/dist/core/baseline.js +85 -0
  43. package/dist/core/baseline.js.map +1 -0
  44. package/dist/core/blind-spots.js +22 -0
  45. package/dist/core/blind-spots.js.map +1 -0
  46. package/dist/core/contract-extras.js +1 -0
  47. package/dist/core/contract-extras.js.map +1 -0
  48. package/dist/core/contract.js +1 -0
  49. package/dist/core/contract.js.map +1 -0
  50. package/dist/core/coverage.js +70 -0
  51. package/dist/core/coverage.js.map +1 -0
  52. package/dist/core/crawl.js +229 -0
  53. package/dist/core/crawl.js.map +1 -0
  54. package/dist/core/egress.js +128 -0
  55. package/dist/core/egress.js.map +1 -0
  56. package/dist/core/ledger.js +41 -0
  57. package/dist/core/ledger.js.map +1 -0
  58. package/dist/core/net.js +39 -0
  59. package/dist/core/pipeline.js +191 -0
  60. package/dist/core/pipeline.js.map +1 -0
  61. package/dist/core/plan.js +121 -0
  62. package/dist/core/plan.js.map +1 -0
  63. package/dist/core/redact.js +240 -0
  64. package/dist/core/redact.js.map +1 -0
  65. package/dist/core/replay.js +263 -0
  66. package/dist/core/replay.js.map +1 -0
  67. package/dist/core/report.js +253 -0
  68. package/dist/core/report.js.map +1 -0
  69. package/dist/core/safety-judge.js +116 -0
  70. package/dist/core/safety.js +92 -0
  71. package/dist/core/safety.js.map +1 -0
  72. package/dist/core/signal-normalize.js +190 -0
  73. package/dist/core/signal-normalize.js.map +1 -0
  74. package/dist/core/signals.js +5 -0
  75. package/dist/core/signals.js.map +1 -0
  76. package/dist/core/triage.js +104 -0
  77. package/dist/core/triage.js.map +1 -0
  78. package/dist/core/types.js +1 -0
  79. package/dist/core/types.js.map +1 -0
  80. package/dist/desktop/desktop-profile.js +6 -0
  81. package/dist/desktop/desktop-profile.js.map +1 -0
  82. package/dist/desktop/driver.js +433 -0
  83. package/dist/desktop/driver.js.map +1 -0
  84. package/dist/desktop/evidence.js +206 -0
  85. package/dist/desktop/evidence.js.map +1 -0
  86. package/dist/desktop/macos/accessibility-runner.js +118 -0
  87. package/dist/desktop/macos/accessibility-runner.js.map +1 -0
  88. package/dist/desktop/macos/types.js +1 -0
  89. package/dist/desktop/macos/types.js.map +1 -0
  90. package/dist/desktop/preflight.js +107 -0
  91. package/dist/desktop/preflight.js.map +1 -0
  92. package/dist/desktop/proxy-proof.js +185 -0
  93. package/dist/desktop/proxy-proof.js.map +1 -0
  94. package/dist/desktop/view.js +348 -0
  95. package/dist/desktop/view.js.map +1 -0
  96. package/dist/desktop/windows/types.js +1 -0
  97. package/dist/desktop/windows/types.js.map +1 -0
  98. package/dist/desktop/windows/uia-runner.js +127 -0
  99. package/dist/desktop/windows/uia-runner.js.map +1 -0
  100. package/dist/native/apple.js +14 -0
  101. package/dist/native/apple.js.map +1 -0
  102. package/dist/native/device-profile.js +6 -0
  103. package/dist/native/device-profile.js.map +1 -0
  104. package/dist/native/driver.js +416 -0
  105. package/dist/native/driver.js.map +1 -0
  106. package/dist/native/evidence.js +164 -0
  107. package/dist/native/evidence.js.map +1 -0
  108. package/dist/native/load.js +9 -0
  109. package/dist/native/load.js.map +1 -0
  110. package/dist/native/maestro.js +153 -0
  111. package/dist/native/maestro.js.map +1 -0
  112. package/dist/native/preflight.js +72 -0
  113. package/dist/native/preflight.js.map +1 -0
  114. package/dist/native/proxy-proof.js +182 -0
  115. package/dist/native/proxy-proof.js.map +1 -0
  116. package/dist/native/samsung.js +34 -0
  117. package/dist/native/samsung.js.map +1 -0
  118. package/dist/native/signals.js +50 -0
  119. package/dist/native/signals.js.map +1 -0
  120. package/dist/native/view.js +139 -0
  121. package/dist/native/view.js.map +1 -0
  122. package/dist/profile.js +237 -0
  123. package/dist/profile.js.map +1 -0
  124. package/dist/sink.js +134 -0
  125. package/dist/sink.js.map +1 -0
  126. package/dist/sinks/file.js +49 -0
  127. package/dist/sinks/file.js.map +1 -0
  128. package/dist/sinks/http.js +38 -0
  129. package/dist/sinks/http.js.map +1 -0
  130. package/dist/sinks/index.js +5 -0
  131. package/dist/sinks/index.js.map +1 -0
  132. package/dist/sinks/multi.js +40 -0
  133. package/dist/sinks/multi.js.map +1 -0
  134. package/dist/sinks/paths.js +32 -0
  135. package/dist/sinks/paths.js.map +1 -0
  136. package/dist/sinks/record.js +58 -0
  137. package/dist/sinks/record.js.map +1 -0
  138. package/dist/sinks/types.js +1 -0
  139. package/dist/sinks/types.js.map +1 -0
  140. package/dist/web/breakage.js +210 -0
  141. package/dist/web/breakage.js.map +1 -0
  142. package/dist/web/controls.js +102 -0
  143. package/dist/web/controls.js.map +1 -0
  144. package/dist/web/driver.js +803 -0
  145. package/dist/web/driver.js.map +1 -0
  146. package/dist/web/elements.js +151 -0
  147. package/dist/web/elements.js.map +1 -0
  148. package/dist/web/fingerprint.js +13 -0
  149. package/dist/web/fingerprint.js.map +1 -0
  150. package/dist/web/forms.js +74 -0
  151. package/dist/web/forms.js.map +1 -0
  152. package/dist/web/links.js +194 -0
  153. package/dist/web/links.js.map +1 -0
  154. package/dist/web/types.js +1 -0
  155. package/dist/web/types.js.map +1 -0
  156. package/package.json +80 -0
  157. package/runtime/macos-accessibility-runner.js +275 -0
  158. package/runtime/windows-uia-runner.ps1 +539 -0
@@ -0,0 +1,167 @@
1
+ import { guardedFetch } from "../core/net.js";
2
+ import { harOperationLabel, parseHar, shapeSignature } from "./har.js";
3
+ const SAFE_METHODS = new Set(["GET", "HEAD", "OPTIONS"]);
4
+ function makeFetchTransport(guard) {
5
+ return async (req) => {
6
+ const res = await guardedFetch(req.url, { method: req.method, headers: req.headers, body: req.body }, guard);
7
+ const headers = {};
8
+ res.headers.forEach((v, k) => {
9
+ headers[k.toLowerCase()] = v;
10
+ });
11
+ return { status: res.status, headers, bodyText: await res.text() };
12
+ };
13
+ }
14
+ const NOOP_COVERAGE = { skip() { }, bumpExtra() { } };
15
+ function safeJson(text) {
16
+ try {
17
+ return JSON.parse(text);
18
+ }
19
+ catch {
20
+ return undefined;
21
+ }
22
+ }
23
+ function errMessage(e) {
24
+ return (e instanceof Error ? e.message : String(e)).slice(0, 160);
25
+ }
26
+ export class HarDriver {
27
+ opts;
28
+ name = "har";
29
+ capabilities = { screenshot: false, probes: [] };
30
+ operations;
31
+ identity;
32
+ transport;
33
+ cov = NOOP_COVERAGE;
34
+ exercised = 0;
35
+ drifts = 0;
36
+ constructor(opts) {
37
+ this.opts = opts;
38
+ this.operations = parseHar(opts.har);
39
+ this.transport =
40
+ opts.transport ?? makeFetchTransport({ allowPrivate: opts.allowPrivateTarget, timeoutMs: opts.timeoutMs });
41
+ // A stable address identity for the single catalog state (the capture's origin if any).
42
+ let origin = "har-capture";
43
+ const first = this.operations[0];
44
+ if (first) {
45
+ try {
46
+ origin = new URL(first.url).origin;
47
+ }
48
+ catch {
49
+ /* keep default */
50
+ }
51
+ }
52
+ this.identity = origin;
53
+ }
54
+ attachCoverage(sink) {
55
+ this.cov = sink;
56
+ }
57
+ async start() {
58
+ /* stateless */
59
+ }
60
+ async close() {
61
+ /* stateless */
62
+ }
63
+ get entry() {
64
+ return { id: this.identity, scope: this.identity, label: this.identity };
65
+ }
66
+ entryAddress() {
67
+ return this.entry;
68
+ }
69
+ currentAddress() {
70
+ return this.entry;
71
+ }
72
+ inScope(addr) {
73
+ return addr.id === this.identity;
74
+ }
75
+ async goto(_addr) {
76
+ /* single state */
77
+ }
78
+ /** One Surface; one node per deduped recorded operation. Write entries are surfaced disabled
79
+ * in read-only mode (a counted coverage skip). */
80
+ async snapshot() {
81
+ const nodes = this.operations.map((op) => ({
82
+ id: harOperationLabel(op),
83
+ kind: "har-request",
84
+ label: harOperationLabel(op),
85
+ handle: op,
86
+ disabled: !SAFE_METHODS.has(op.method) && !this.opts.allowWrites,
87
+ }));
88
+ return { address: this.entry, title: "HAR capture", nodes, blindSpots: 0 };
89
+ }
90
+ /** EXERCISE — re-send the recorded request, compare the live response to the recording. */
91
+ async exercise(node) {
92
+ const op = node.handle;
93
+ if (!SAFE_METHODS.has(op.method) && !this.opts.allowWrites)
94
+ return {};
95
+ const signals = [];
96
+ let live;
97
+ try {
98
+ live = await this.send(op);
99
+ }
100
+ catch (e) {
101
+ this.exercised++;
102
+ signals.push(this.signal(op, "request-failed", "warn", `${harOperationLabel(op)} → transport error: ${errMessage(e)}`));
103
+ return { signals };
104
+ }
105
+ this.exercised++;
106
+ // STATUS drift — the live status no longer matches what was recorded.
107
+ if (live.status !== op.recorded.status) {
108
+ this.drifts++;
109
+ signals.push(this.signal(op, "contract-drift", "fail", `${harOperationLabel(op)} → status drift: recorded HTTP ${op.recorded.status}, live HTTP ${live.status}`));
110
+ }
111
+ else {
112
+ // SHAPE drift — same status, but the JSON body structure changed (field added/removed/retyped).
113
+ const recordedShape = safeJson(op.recorded.bodyText);
114
+ const liveShape = safeJson(live.bodyText);
115
+ if (recordedShape !== undefined && liveShape !== undefined) {
116
+ const a = shapeSignature(recordedShape);
117
+ const b = shapeSignature(liveShape);
118
+ if (a !== b) {
119
+ this.drifts++;
120
+ signals.push(this.signal(op, "contract-drift", "warn", `${harOperationLabel(op)} → response shape drift from the recorded body`));
121
+ }
122
+ }
123
+ }
124
+ return { signals };
125
+ }
126
+ signal(op, kind, severity, message) {
127
+ return {
128
+ kind,
129
+ severity,
130
+ message,
131
+ url: op.url,
132
+ signature: `${kind}|${harOperationLabel(op)}`.slice(0, 80),
133
+ action: harOperationLabel(op),
134
+ };
135
+ }
136
+ async send(op) {
137
+ const headers = { accept: "application/json" };
138
+ if (op.bodyContentType)
139
+ headers["content-type"] = op.bodyContentType;
140
+ if (this.opts.token)
141
+ headers["authorization"] = `Bearer ${this.opts.token}`;
142
+ return this.transport({ method: op.method, url: op.url, headers, body: op.body });
143
+ }
144
+ drainSignals() {
145
+ return [];
146
+ }
147
+ async screenshot() {
148
+ return Buffer.alloc(0);
149
+ }
150
+ outboundAddresses() {
151
+ return [];
152
+ }
153
+ auditExtra() {
154
+ const writeOps = this.operations.filter((op) => !SAFE_METHODS.has(op.method)).length;
155
+ return {
156
+ harAudit: {
157
+ operations: this.operations.length,
158
+ safeOperations: this.operations.length - writeOps,
159
+ writeOperations: writeOps,
160
+ exercised: this.exercised,
161
+ writesSkipped: this.opts.allowWrites ? 0 : writeOps,
162
+ drifts: this.drifts,
163
+ allowWrites: this.opts.allowWrites,
164
+ },
165
+ };
166
+ }
167
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"har-driver.js","sourceRoot":"","sources":["../../src/api/har-driver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,100 @@
1
+ // HAR (HTTP Archive) spec source — the API driver's FOURTH discovery mode. Recorded traffic
2
+ // (`log.entries[]`) becomes the operation set: each entry's request (method + URL + body) is an
3
+ // operation, and its recorded response is the CONTRACT we re-check against live. Route-template
4
+ // dedupe collapses `/users/1` and `/users/2` to a single `GET /users/{id}` operation. Leaf
5
+ // module — imports nothing from `core`/`web`.
6
+ function asObject(v) {
7
+ return v && typeof v === "object" && !Array.isArray(v) ? v : {};
8
+ }
9
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10
+ /** A path segment that is an identifier (numeric / uuid / long hex) → templated as `{id}`. */
11
+ function isIdLike(seg) {
12
+ return /^\d+$/.test(seg) || UUID_RE.test(seg) || /^[0-9a-f]{16,}$/i.test(seg);
13
+ }
14
+ /** Derive a route template from a URL path: id-like segments become `{id}`. */
15
+ export function templateOfPath(pathname) {
16
+ return pathname
17
+ .split("/")
18
+ .map((seg) => (isIdLike(seg) ? "{id}" : seg))
19
+ .join("/");
20
+ }
21
+ function headerValue(headers, name) {
22
+ if (!Array.isArray(headers))
23
+ return undefined;
24
+ const lower = name.toLowerCase();
25
+ for (const h of headers) {
26
+ const o = asObject(h);
27
+ if (typeof o.name === "string" && o.name.toLowerCase() === lower && typeof o.value === "string")
28
+ return o.value;
29
+ }
30
+ return undefined;
31
+ }
32
+ /**
33
+ * Parse a HAR document into deduped operations. The first entry per `METHOD template` wins
34
+ * (route-template dedupe); later entries that template-collapse to it are dropped.
35
+ */
36
+ export function parseHar(doc) {
37
+ const log = asObject(asObject(doc).log);
38
+ const entries = Array.isArray(log.entries) ? log.entries : [];
39
+ const seen = new Set();
40
+ const out = [];
41
+ for (const raw of entries) {
42
+ const entry = asObject(raw);
43
+ const request = asObject(entry.request);
44
+ const response = asObject(entry.response);
45
+ const method = typeof request.method === "string" ? request.method.toUpperCase() : undefined;
46
+ const url = typeof request.url === "string" ? request.url : undefined;
47
+ if (!method || !url)
48
+ continue;
49
+ let template;
50
+ try {
51
+ template = templateOfPath(new URL(url).pathname);
52
+ }
53
+ catch {
54
+ continue; // not a usable absolute URL
55
+ }
56
+ const key = `${method} ${template}`;
57
+ if (seen.has(key))
58
+ continue;
59
+ seen.add(key);
60
+ const postData = asObject(request.postData);
61
+ const body = typeof postData.text === "string" ? postData.text : undefined;
62
+ const bodyContentType = typeof postData.mimeType === "string" ? postData.mimeType : headerValue(request.headers, "content-type");
63
+ const content = asObject(response.content);
64
+ out.push({
65
+ method,
66
+ url,
67
+ template,
68
+ body,
69
+ bodyContentType,
70
+ recorded: {
71
+ status: typeof response.status === "number" ? response.status : 0,
72
+ bodyText: typeof content.text === "string" ? content.text : "",
73
+ mimeType: typeof content.mimeType === "string" ? content.mimeType : undefined,
74
+ },
75
+ });
76
+ }
77
+ return out;
78
+ }
79
+ /** "METHOD /template" — stable operation identity (node id/label, dedupe namespace). */
80
+ export function harOperationLabel(op) {
81
+ return `${op.method} ${op.template}`;
82
+ }
83
+ /**
84
+ * A stable, value-free STRUCTURAL signature of a JSON value — object key sets (sorted) + the
85
+ * scalar TYPES at each leaf, never the values themselves. Comparing the recorded vs. live
86
+ * signatures detects added/removed/retyped fields (contract drift) without leaking body data.
87
+ */
88
+ export function shapeSignature(value, depth = 0) {
89
+ if (depth > 6)
90
+ return "…";
91
+ if (Array.isArray(value))
92
+ return `[${value.length ? shapeSignature(value[0], depth + 1) : ""}]`;
93
+ if (value === null)
94
+ return "null";
95
+ if (typeof value === "object") {
96
+ const keys = Object.keys(value).sort();
97
+ return `{${keys.map((k) => `${k}:${shapeSignature(value[k], depth + 1)}`).join(",")}}`;
98
+ }
99
+ return typeof value;
100
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"har.js","sourceRoot":"","sources":["../../src/api/har.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ // I/O loaders for the API driver — read an OpenAPI document (JSON or YAML), a HAR capture, a
2
+ // GraphQL introspection result, and optional seed/role-seed data from a file path or URL. Kept
3
+ // SEPARATE from the drivers (which take already-parsed objects) so the drivers stay hermetically
4
+ // testable; only the CLI composition root touches these.
5
+ //
6
+ // `src/api` may import the new libs (`yaml`) + core; never `web`. This module is the one place
7
+ // `.yaml`/`.yml` specs are accepted: a content-sniff (a spec that starts with `{`/`[` is JSON,
8
+ // otherwise YAML — which is a JSON superset, so it parses both) makes it format-agnostic.
9
+ import { readFile } from "node:fs/promises";
10
+ import { parse as parseYaml } from "yaml";
11
+ import { guardedFetch } from "../core/net.js";
12
+ /** A spec body looks like JSON when it begins with an object/array delimiter. */
13
+ function looksLikeJson(text) {
14
+ const t = text.trimStart();
15
+ return t.startsWith("{") || t.startsWith("[");
16
+ }
17
+ /**
18
+ * Parse an OpenAPI spec body that may be JSON OR YAML. JSON-looking bodies go through
19
+ * `JSON.parse` first (fast, precise errors); anything else (or a JSON body that fails) is
20
+ * parsed as YAML. A spec that is neither raises a single clear error.
21
+ */
22
+ export function parseSpec(text, source) {
23
+ if (looksLikeJson(text)) {
24
+ try {
25
+ return JSON.parse(text);
26
+ }
27
+ catch {
28
+ /* fall through — maybe it is YAML that happens to start with a brace */
29
+ }
30
+ }
31
+ try {
32
+ return parseYaml(text);
33
+ }
34
+ catch (e) {
35
+ throw new Error(`OpenAPI spec ${source} is not valid JSON or YAML: ${e.message}`);
36
+ }
37
+ }
38
+ /** Load + parse an OpenAPI 3.x document (JSON or YAML) from an http(s) URL or a local file path.
39
+ * A URL source is fetched through the SSRF egress guard (fail-closed unless `allowPrivate`), so
40
+ * a `--openapi http://169.254.169.254/…` can't turn the spec load into a metadata read. */
41
+ export async function loadOpenApiDocument(source, guard = {}) {
42
+ if (/^https?:\/\//i.test(source)) {
43
+ const res = await guardedFetch(source, {}, guard);
44
+ if (!res.ok)
45
+ throw new Error(`failed to fetch OpenAPI spec ${source}: HTTP ${res.status}`);
46
+ return parseSpec(await res.text(), source);
47
+ }
48
+ return parseSpec(await readFile(source, "utf8"), source);
49
+ }
50
+ /** Load operator seed data (per-operation request overrides) from a JSON file, if configured. */
51
+ export async function loadSeedData(path) {
52
+ if (!path)
53
+ return undefined;
54
+ const text = await readFile(path, "utf8");
55
+ try {
56
+ return JSON.parse(text);
57
+ }
58
+ catch (e) {
59
+ throw new Error(`seed data ${path} is not valid JSON: ${e.message}`);
60
+ }
61
+ }
62
+ /** Load a HAR capture (always JSON per the HAR spec) from a local file path. */
63
+ export async function loadHar(path) {
64
+ const text = await readFile(path, "utf8");
65
+ try {
66
+ return JSON.parse(text);
67
+ }
68
+ catch (e) {
69
+ throw new Error(`HAR file ${path} is not valid JSON: ${e.message}`);
70
+ }
71
+ }
72
+ /** Load per-role seed data (role → per-operation overrides) for the multi-role IDOR harness. */
73
+ export async function loadRoleSeedData(path) {
74
+ if (!path)
75
+ return undefined;
76
+ const text = await readFile(path, "utf8");
77
+ try {
78
+ return JSON.parse(text);
79
+ }
80
+ catch (e) {
81
+ throw new Error(`role-seed data ${path} is not valid JSON: ${e.message}`);
82
+ }
83
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/api/load.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,159 @@
1
+ // Minimal OpenAPI 3.x ingest — the API driver's "discover the surface" half. We parse
2
+ // only what the checker needs to exercise an API and judge breakage: operations
3
+ // (method + path template + parameters + requestBody + documented responses) plus
4
+ // whether the operation requires auth. `$ref`s are resolved against `components`.
5
+ //
6
+ // Leaf module: imports NOTHING from `core` or `web`. It emits plain descriptors the
7
+ // driver maps onto the target-agnostic contract (so the dependency arrow stays
8
+ // api → core, never the reverse, and never api ↔ web).
9
+ const HTTP_METHODS = ["get", "put", "post", "delete", "options", "head", "patch", "trace"];
10
+ /**
11
+ * Resolves `#/...` JSON pointers against the root document. Follows chained `$ref`s with a
12
+ * guard so a cyclic schema can't spin forever. Anything without a `$ref` is returned as-is.
13
+ */
14
+ export class SpecResolver {
15
+ doc;
16
+ constructor(doc) {
17
+ this.doc = doc;
18
+ }
19
+ /** Follow `$ref`s until a concrete node (or a missing pointer) is reached. */
20
+ deref(node) {
21
+ let cur = node;
22
+ let guard = 0;
23
+ while (cur && typeof cur === "object" && typeof cur.$ref === "string" && guard++ < 64) {
24
+ cur = this.lookup(cur.$ref);
25
+ }
26
+ return cur;
27
+ }
28
+ lookup(ref) {
29
+ if (!ref.startsWith("#/"))
30
+ return undefined;
31
+ const parts = ref
32
+ .slice(2)
33
+ .split("/")
34
+ .map((p) => p.replace(/~1/g, "/").replace(/~0/g, "~"));
35
+ let cur = this.doc;
36
+ for (const p of parts) {
37
+ if (cur == null || typeof cur !== "object")
38
+ return undefined;
39
+ cur = cur[p];
40
+ }
41
+ return cur;
42
+ }
43
+ }
44
+ function asObject(value) {
45
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
46
+ }
47
+ function normalizeParam(raw) {
48
+ const name = typeof raw.name === "string" ? raw.name : undefined;
49
+ const location = raw.in;
50
+ if (!name || (location !== "path" && location !== "query" && location !== "header" && location !== "cookie")) {
51
+ return undefined;
52
+ }
53
+ return {
54
+ name,
55
+ in: location,
56
+ // A path parameter is always required per the spec; otherwise honor `required`.
57
+ required: location === "path" ? true : raw.required === true,
58
+ schema: raw.schema,
59
+ example: raw.example,
60
+ examples: raw.examples,
61
+ };
62
+ }
63
+ function mergeParams(pathLevel, opLevel) {
64
+ const byKey = new Map();
65
+ for (const p of pathLevel)
66
+ byKey.set(`${p.in}:${p.name}`, p);
67
+ for (const p of opLevel)
68
+ byKey.set(`${p.in}:${p.name}`, p); // op-level overrides path-level
69
+ return [...byKey.values()];
70
+ }
71
+ function parseRequestBody(raw, resolver) {
72
+ const rb = asObject(resolver.deref(raw));
73
+ const content = asObject(rb.content);
74
+ if (Object.keys(content).length === 0)
75
+ return undefined;
76
+ const out = {};
77
+ for (const [mt, body] of Object.entries(content))
78
+ out[mt] = asObject(resolver.deref(body));
79
+ return { required: rb.required === true, content: out };
80
+ }
81
+ function parseResponses(raw, resolver) {
82
+ const responses = asObject(raw);
83
+ const out = [];
84
+ for (const [statusKey, respRaw] of Object.entries(responses)) {
85
+ const resp = asObject(resolver.deref(respRaw));
86
+ const contentRaw = asObject(resp.content);
87
+ const content = {};
88
+ for (const [mt, body] of Object.entries(contentRaw))
89
+ content[mt] = asObject(resolver.deref(body));
90
+ out.push({ statusKey, content });
91
+ }
92
+ return out;
93
+ }
94
+ /** A security requirement list "requires auth" when at least one requirement object is
95
+ * non-empty. `security: []` explicitly opts OUT (public), so it does NOT require auth. */
96
+ function computesRequiresAuth(opSecurity, globalSecurity) {
97
+ const sec = opSecurity !== undefined ? opSecurity : globalSecurity;
98
+ if (!Array.isArray(sec))
99
+ return false;
100
+ return sec.some((req) => req && typeof req === "object" && Object.keys(req).length > 0);
101
+ }
102
+ /**
103
+ * Parse an OpenAPI 3.x document object into the operation descriptors the driver
104
+ * exercises. Returns the operations plus the resolver (shared so request building and
105
+ * response classification deref schemas against the same `components`).
106
+ */
107
+ export function parseOpenApi(doc) {
108
+ const resolver = new SpecResolver(doc);
109
+ const root = asObject(doc);
110
+ const paths = asObject(root.paths);
111
+ const globalSecurity = root.security;
112
+ const operations = [];
113
+ for (const [pathTemplate, pathItemRaw] of Object.entries(paths)) {
114
+ const pathItem = asObject(resolver.deref(pathItemRaw));
115
+ const pathParams = Array.isArray(pathItem.parameters)
116
+ ? pathItem.parameters.map((p) => normalizeParam(asObject(resolver.deref(p)))).filter((p) => !!p)
117
+ : [];
118
+ for (const method of HTTP_METHODS) {
119
+ const opRaw = pathItem[method];
120
+ if (!opRaw || typeof opRaw !== "object")
121
+ continue;
122
+ const op = asObject(resolver.deref(opRaw));
123
+ const opParams = Array.isArray(op.parameters)
124
+ ? op.parameters.map((p) => normalizeParam(asObject(resolver.deref(p)))).filter((p) => !!p)
125
+ : [];
126
+ operations.push({
127
+ method: method.toUpperCase(),
128
+ path: pathTemplate,
129
+ operationId: typeof op.operationId === "string" ? op.operationId : undefined,
130
+ summary: typeof op.summary === "string" ? op.summary : undefined,
131
+ parameters: mergeParams(pathParams, opParams),
132
+ requestBody: op.requestBody !== undefined ? parseRequestBody(op.requestBody, resolver) : undefined,
133
+ responses: parseResponses(op.responses, resolver),
134
+ requiresAuth: computesRequiresAuth(op.security, globalSecurity),
135
+ });
136
+ }
137
+ }
138
+ const info = asObject(root.info);
139
+ return { operations, resolver, title: typeof info.title === "string" ? info.title : undefined };
140
+ }
141
+ /** "METHOD /template" — stable operation identity (SurfaceNode id/label, dedupe namespace). */
142
+ export function operationLabel(op) {
143
+ return `${op.method} ${op.path}`;
144
+ }
145
+ /** The documented response whose key best matches `status`: exact → "NXX" range → default. */
146
+ export function responseFor(op, status) {
147
+ const cls = Math.floor(status / 100);
148
+ return (op.responses.find((r) => /^\d+$/.test(r.statusKey) && Number(r.statusKey) === status) ??
149
+ op.responses.find((r) => /^[1-5]xx$/i.test(r.statusKey) && Number(r.statusKey[0]) === cls) ??
150
+ op.responses.find((r) => r.statusKey === "default"));
151
+ }
152
+ /** Is `status` covered by ANY documented response (exact, range, or default)? */
153
+ export function isDocumented(op, status) {
154
+ return op.responses.length > 0 && responseFor(op, status) !== undefined;
155
+ }
156
+ /** Human-readable list of documented status keys (for the undocumented-status message). */
157
+ export function documentedStatusList(op) {
158
+ return op.responses.map((r) => r.statusKey).join(", ") || "(none)";
159
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi.js","sourceRoot":"","sources":["../../src/api/openapi.ts"],"names":[],"mappings":"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"}