From bffd86231372eda110a84fa9274d40a9820228bd Mon Sep 17 00:00:00 2001 From: Rakesh P R Date: Tue, 1 Sep 2026 17:49:19 +0530 Subject: [PATCH] fix: make generated contract checks CRLF-safe --- .prettierrc.json | 1 + openapi/motifuse.openapi.json | 1024 +++++++++++++++++++++++++++++++-- scripts/check-generated.mjs | 4 +- src/generated/schema.d.ts | 332 ++++++++++- tests/contract.test.mjs | 8 +- 5 files changed, 1274 insertions(+), 95 deletions(-) diff --git a/.prettierrc.json b/.prettierrc.json index 90abee2..3a7d5dd 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,5 +1,6 @@ { "printWidth": 100, + "endOfLine": "auto", "semi": true, "singleQuote": false, "trailingComma": "all" diff --git a/openapi/motifuse.openapi.json b/openapi/motifuse.openapi.json index 206a3ce..339ffdf 100644 --- a/openapi/motifuse.openapi.json +++ b/openapi/motifuse.openapi.json @@ -4,7 +4,7 @@ "info": { "title": "Motifuse Developer API", "version": "1.0.0", - "summary": "One server-to-server API for DocForge, Reconova, and Spectrace.", + "summary": "One server-to-server API for DocForge, Reconova, and SpecTrace.", "description": "The stable Motifuse /v1 contract. API keys are secrets and must never be embedded in browser JavaScript, mobile bundles, or public repositories. Long-running operations are asynchronous and return jobs or resources linked to jobs.", "contact": { "name": "Motifuse API Support", @@ -27,7 +27,7 @@ "description": "Direct-upload data profiling and cleaning workflows." }, { - "name": "Spectrace", + "name": "SpecTrace", "description": "Deterministic document change comparison and review." }, { @@ -87,6 +87,20 @@ "responses": { "200": { "description": "Cursor-paginated templates.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -142,6 +156,7 @@ } ], "x-required-scopes": [ + "docforge:read", "jobs:read" ], "parameters": [ @@ -172,6 +187,20 @@ "responses": { "200": { "description": "Cursor-paginated jobs.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -215,7 +244,7 @@ "post": { "operationId": "docforgeCreateGeneration", "summary": "Start a document generation", - "description": "Pins a published template version, reserves server-owned quota once, stores canonical input, and dispatches an asynchronous worker job.", + "description": "Pins a published template version, reserves quota once, records canonical input, and queues asynchronous generation.", "tags": [ "DocForge", "Jobs" @@ -263,6 +292,20 @@ "responses": { "200": { "description": "Idempotent replay of an existing generation.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -273,6 +316,20 @@ }, "202": { "description": "Generation accepted.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -328,6 +385,7 @@ } ], "x-required-scopes": [ + "docforge:read", "jobs:read" ], "parameters": [ @@ -349,6 +407,20 @@ "responses": { "200": { "description": "Generation state.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -404,6 +476,7 @@ } ], "x-required-scopes": [ + "docforge:generate", "jobs:write" ], "parameters": [ @@ -425,6 +498,20 @@ "responses": { "202": { "description": "Cancellation accepted or completed.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -480,6 +567,7 @@ } ], "x-required-scopes": [ + "docforge:read", "files:read" ], "parameters": [ @@ -514,6 +602,20 @@ "responses": { "200": { "description": "Short-lived signed R2 download.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -572,6 +674,7 @@ } ], "x-required-scopes": [ + "reconova:read", "files:read" ], "parameters": [ @@ -602,6 +705,20 @@ "responses": { "200": { "description": "Cursor-paginated files and processing status.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -655,6 +772,7 @@ } ], "x-required-scopes": [ + "reconova:write", "files:write" ], "parameters": [ @@ -683,6 +801,20 @@ "responses": { "201": { "description": "Upload authorization.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -738,6 +870,7 @@ } ], "x-required-scopes": [ + "reconova:read", "files:read" ], "parameters": [ @@ -759,6 +892,20 @@ "responses": { "200": { "description": "File metadata and current status.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -815,6 +962,7 @@ } ], "x-required-scopes": [ + "reconova:write", "files:write" ], "parameters": [ @@ -839,6 +987,20 @@ "responses": { "200": { "description": "The already-completed upload and existing job.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -849,6 +1011,20 @@ }, "201": { "description": "Upload verified; intake job created.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -859,6 +1035,20 @@ }, "202": { "description": "Upload verified; dispatch deferred safely.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -914,6 +1104,7 @@ } ], "x-required-scopes": [ + "reconova:read", "files:read" ], "parameters": [ @@ -935,6 +1126,20 @@ "responses": { "200": { "description": "Short-lived signed R2 download.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -1027,6 +1232,20 @@ "responses": { "201": { "description": "Cleaning job created.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -1037,6 +1256,20 @@ }, "202": { "description": "Cleaning job accepted.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -1092,6 +1325,7 @@ } ], "x-required-scopes": [ + "reconova:read", "jobs:read" ], "parameters": [ @@ -1113,6 +1347,20 @@ "responses": { "200": { "description": "Job state.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -1168,6 +1416,7 @@ } ], "x-required-scopes": [ + "reconova:read", "jobs:read" ], "parameters": [ @@ -1198,6 +1447,20 @@ "responses": { "200": { "description": "Cursor-paginated jobs.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -1253,6 +1516,7 @@ } ], "x-required-scopes": [ + "reconova:process", "jobs:write" ], "parameters": [ @@ -1274,6 +1538,20 @@ "responses": { "202": { "description": "Cancellation accepted or completed.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -1320,7 +1598,7 @@ "operationId": "spectraceListProjects", "summary": "List projects", "tags": [ - "Spectrace" + "SpecTrace" ], "security": [ { @@ -1358,6 +1636,20 @@ "responses": { "200": { "description": "Cursor-paginated projects.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -1402,7 +1694,7 @@ "operationId": "spectraceCreateProject", "summary": "Create a project", "tags": [ - "Spectrace" + "SpecTrace" ], "security": [ { @@ -1441,6 +1733,20 @@ "responses": { "201": { "description": "Project created.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -1487,7 +1793,7 @@ "operationId": "spectraceGetProject", "summary": "Get a project", "tags": [ - "Spectrace" + "SpecTrace" ], "security": [ { @@ -1505,7 +1811,7 @@ "name": "project_id", "in": "path", "required": true, - "description": "Spectrace project ID.", + "description": "SpecTrace project ID.", "schema": { "type": "string", "minLength": 3, @@ -1516,6 +1822,20 @@ "responses": { "200": { "description": "Project.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -1560,7 +1880,7 @@ "operationId": "spectraceUpdateProject", "summary": "Update a project", "tags": [ - "Spectrace" + "SpecTrace" ], "security": [ { @@ -1578,7 +1898,7 @@ "name": "project_id", "in": "path", "required": true, - "description": "Spectrace project ID.", + "description": "SpecTrace project ID.", "schema": { "type": "string", "minLength": 3, @@ -1607,6 +1927,20 @@ "responses": { "200": { "description": "Updated project.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -1651,7 +1985,7 @@ "operationId": "spectraceDeleteProject", "summary": "Delete a project", "tags": [ - "Spectrace" + "SpecTrace" ], "security": [ { @@ -1669,7 +2003,7 @@ "name": "project_id", "in": "path", "required": true, - "description": "Spectrace project ID.", + "description": "SpecTrace project ID.", "schema": { "type": "string", "minLength": 3, @@ -1680,6 +2014,20 @@ "responses": { "200": { "description": "Project scheduled for retention-safe deletion.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -1738,7 +2086,7 @@ "operationId": "spectraceListProjectFiles", "summary": "List project documents and versions", "tags": [ - "Spectrace", + "SpecTrace", "Files" ], "security": [ @@ -1747,6 +2095,7 @@ } ], "x-required-scopes": [ + "spectrace:read", "files:read" ], "parameters": [ @@ -1757,7 +2106,7 @@ "name": "project_id", "in": "path", "required": true, - "description": "Spectrace project ID.", + "description": "SpecTrace project ID.", "schema": { "type": "string", "minLength": 3, @@ -1788,6 +2137,20 @@ "responses": { "200": { "description": "Project files.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -1833,7 +2196,7 @@ "summary": "Authorize a quarantined direct upload", "description": "The file is written directly to R2 quarantine. Completion verifies bytes and queues malware scanning before extraction can begin.", "tags": [ - "Spectrace", + "SpecTrace", "Files" ], "security": [ @@ -1842,6 +2205,7 @@ } ], "x-required-scopes": [ + "spectrace:write", "files:write" ], "parameters": [ @@ -1855,7 +2219,7 @@ "name": "project_id", "in": "path", "required": true, - "description": "Spectrace project ID.", + "description": "SpecTrace project ID.", "schema": { "type": "string", "minLength": 3, @@ -1883,6 +2247,20 @@ "responses": { "201": { "description": "Quarantined upload authorization.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -1929,7 +2307,7 @@ "operationId": "spectraceGetFile", "summary": "Get file processing state", "tags": [ - "Spectrace", + "SpecTrace", "Files" ], "security": [ @@ -1938,6 +2316,7 @@ } ], "x-required-scopes": [ + "spectrace:read", "files:read" ], "parameters": [ @@ -1948,7 +2327,7 @@ "name": "file_id", "in": "path", "required": true, - "description": "Spectrace version/file ID.", + "description": "SpecTrace version/file ID.", "schema": { "type": "string", "minLength": 3, @@ -1959,6 +2338,20 @@ "responses": { "200": { "description": "File state.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -2005,7 +2398,7 @@ "operationId": "spectraceCompleteFileUpload", "summary": "Verify upload and queue security scanning", "tags": [ - "Spectrace", + "SpecTrace", "Files", "Jobs" ], @@ -2015,6 +2408,7 @@ } ], "x-required-scopes": [ + "spectrace:write", "files:write" ], "parameters": [ @@ -2028,7 +2422,7 @@ "name": "file_id", "in": "path", "required": true, - "description": "Spectrace version/file ID.", + "description": "SpecTrace version/file ID.", "schema": { "type": "string", "minLength": 3, @@ -2039,6 +2433,20 @@ "responses": { "200": { "description": "Upload verified; security processing queued, or an existing completed upload returned.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -2085,7 +2493,7 @@ "operationId": "spectraceCreateFileDownload", "summary": "Create a short-lived source download", "tags": [ - "Spectrace", + "SpecTrace", "Files" ], "security": [ @@ -2094,6 +2502,7 @@ } ], "x-required-scopes": [ + "spectrace:read", "files:read" ], "parameters": [ @@ -2104,7 +2513,7 @@ "name": "file_id", "in": "path", "required": true, - "description": "Spectrace version/file ID.", + "description": "SpecTrace version/file ID.", "schema": { "type": "string", "minLength": 3, @@ -2166,7 +2575,7 @@ "operationId": "spectraceListComparisons", "summary": "List project comparisons", "tags": [ - "Spectrace" + "SpecTrace" ], "security": [ { @@ -2184,7 +2593,7 @@ "name": "project_id", "in": "path", "required": true, - "description": "Spectrace project ID.", + "description": "SpecTrace project ID.", "schema": { "type": "string", "minLength": 3, @@ -2215,6 +2624,20 @@ "responses": { "200": { "description": "Comparisons.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -2258,9 +2681,9 @@ "post": { "operationId": "spectraceCreateComparison", "summary": "Start deterministic comparison", - "description": "Runs the deterministic comparison engine. Generative AI is not used in this path.", + "description": "Queues the deterministic comparison engine and returns immediately. Generative AI is not used in this path.", "tags": [ - "Spectrace", + "SpecTrace", "Jobs" ], "security": [ @@ -2269,8 +2692,7 @@ } ], "x-required-scopes": [ - "spectrace:compare", - "jobs:write" + "spectrace:compare" ], "parameters": [ { @@ -2283,7 +2705,7 @@ "name": "project_id", "in": "path", "required": true, - "description": "Spectrace project ID.", + "description": "SpecTrace project ID.", "schema": { "type": "string", "minLength": 3, @@ -2309,6 +2731,20 @@ "responses": { "200": { "description": "Existing comparison returned idempotently.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -2317,8 +2753,22 @@ } } }, - "201": { - "description": "Comparison and job created.", + "202": { + "description": "Comparison accepted and queued.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -2365,7 +2815,7 @@ "operationId": "spectraceGetComparison", "summary": "Get a comparison", "tags": [ - "Spectrace" + "SpecTrace" ], "security": [ { @@ -2383,7 +2833,7 @@ "name": "comparison_id", "in": "path", "required": true, - "description": "Spectrace comparison ID.", + "description": "SpecTrace comparison ID.", "schema": { "type": "string", "minLength": 3, @@ -2394,6 +2844,20 @@ "responses": { "200": { "description": "Comparison state and summary.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -2440,7 +2904,7 @@ "operationId": "spectraceListFindings", "summary": "List comparison findings", "tags": [ - "Spectrace" + "SpecTrace" ], "security": [ { @@ -2458,7 +2922,7 @@ "name": "comparison_id", "in": "path", "required": true, - "description": "Spectrace comparison ID.", + "description": "SpecTrace comparison ID.", "schema": { "type": "string", "minLength": 3, @@ -2527,6 +2991,20 @@ "responses": { "200": { "description": "Cursor-paginated findings.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -2573,7 +3051,7 @@ "operationId": "spectraceGetFinding", "summary": "Get a finding", "tags": [ - "Spectrace" + "SpecTrace" ], "security": [ { @@ -2591,7 +3069,7 @@ "name": "finding_id", "in": "path", "required": true, - "description": "Spectrace finding ID.", + "description": "SpecTrace finding ID.", "schema": { "type": "string", "minLength": 3, @@ -2602,6 +3080,20 @@ "responses": { "200": { "description": "Finding with evidence and locators.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -2646,7 +3138,7 @@ "operationId": "spectraceReviewFinding", "summary": "Update finding review state", "tags": [ - "Spectrace" + "SpecTrace" ], "security": [ { @@ -2664,7 +3156,7 @@ "name": "finding_id", "in": "path", "required": true, - "description": "Spectrace finding ID.", + "description": "SpecTrace finding ID.", "schema": { "type": "string", "minLength": 3, @@ -2691,6 +3183,20 @@ "responses": { "200": { "description": "Reviewed finding.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -2736,9 +3242,9 @@ "get": { "operationId": "spectraceExportComparison", "summary": "Download a comparison export", - "description": "Returns only export formats implemented by Spectrace (CSV or JSON, selected by query).", + "description": "Returns only export formats implemented by SpecTrace (CSV or JSON, selected by query).", "tags": [ - "Spectrace", + "SpecTrace", "Files" ], "security": [ @@ -2747,6 +3253,7 @@ } ], "x-required-scopes": [ + "spectrace:read", "files:read" ], "parameters": [ @@ -2757,7 +3264,7 @@ "name": "comparison_id", "in": "path", "required": true, - "description": "Spectrace comparison ID.", + "description": "SpecTrace comparison ID.", "schema": { "type": "string", "minLength": 3, @@ -2829,9 +3336,9 @@ "/spectrace/jobs/{job_id}": { "get": { "operationId": "spectraceGetJob", - "summary": "Get Spectrace worker job status", + "summary": "Get SpecTrace job status", "tags": [ - "Spectrace", + "SpecTrace", "Jobs" ], "security": [ @@ -2840,6 +3347,7 @@ } ], "x-required-scopes": [ + "spectrace:read", "jobs:read" ], "parameters": [ @@ -2850,7 +3358,7 @@ "name": "job_id", "in": "path", "required": true, - "description": "Spectrace job ID.", + "description": "SpecTrace job ID.", "schema": { "type": "string", "minLength": 3, @@ -2861,6 +3369,20 @@ "responses": { "200": { "description": "Job state.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -2905,9 +3427,9 @@ "/spectrace/jobs/{job_id}/cancel": { "post": { "operationId": "spectraceCancelJob", - "summary": "Cancel a Spectrace job", + "summary": "Cancel a SpecTrace job", "tags": [ - "Spectrace", + "SpecTrace", "Jobs" ], "security": [ @@ -2916,6 +3438,7 @@ } ], "x-required-scopes": [ + "spectrace:compare", "jobs:write" ], "parameters": [ @@ -2926,7 +3449,7 @@ "name": "job_id", "in": "path", "required": true, - "description": "Spectrace job ID.", + "description": "SpecTrace job ID.", "schema": { "type": "string", "minLength": 3, @@ -2937,6 +3460,20 @@ "responses": { "202": { "description": "Cancellation accepted.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -3001,6 +3538,20 @@ "responses": { "200": { "description": "Webhook endpoints.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -3081,6 +3632,20 @@ "responses": { "201": { "description": "Endpoint created; signing secret shown once.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -3146,6 +3711,20 @@ "responses": { "200": { "description": "Server-owned usage balances, limits, remaining capacity, and reset timestamps.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -3187,10 +3766,109 @@ } } }, + "/logs": { + "get": { + "operationId": "listApiRequestLogs", + "summary": "List redacted API request logs", + "tags": [ + "Jobs", + "Files" + ], + "security": [ + { + "bearerAuth": [] + } + ], + "x-required-scopes": [ + "logs:read" + ], + "parameters": [ + { + "$ref": "#/components/parameters/RequestId" + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 20 + } + }, + { + "name": "cursor", + "in": "query", + "schema": { + "type": [ + "string", + "null" + ] + } + } + ], + "responses": { + "200": { + "description": "Workspace-, environment-, and entitled-product-filtered request metadata.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiRequestList" + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "409": { + "$ref": "#/components/responses/Conflict" + }, + "422": { + "$ref": "#/components/responses/Unprocessable" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + }, + "500": { + "$ref": "#/components/responses/InternalError" + }, + "503": { + "$ref": "#/components/responses/Unavailable" + } + } + } + }, "/webhooks/{endpoint_id}": { "patch": { "operationId": "updateWebhookEndpoint", - "summary": "Pause, enable, or rotate a webhook endpoint secret", + "summary": "Edit, pause, enable, or rotate a webhook endpoint", "tags": [ "Webhooks" ], @@ -3233,6 +3911,34 @@ "disabled" ] }, + "events": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "job.created", + "job.completed", + "job.failed", + "document.generated", + "document.delivered", + "document.delivery_failed", + "document.expired", + "approval.requested", + "approval.decided", + "docforge.generation.created", + "docforge.generation.completed", + "docforge.generation.failed", + "reconova.job.completed", + "reconova.job.failed", + "spectrace.file.ready", + "spectrace.file.failed", + "spectrace.comparison.completed", + "spectrace.comparison.failed" + ] + } + }, "rotate_secret": { "type": "boolean" } @@ -3240,6 +3946,9 @@ }, "example": { "status": "active", + "events": [ + "spectrace.comparison.completed" + ], "rotate_secret": false } } @@ -3248,6 +3957,20 @@ "responses": { "200": { "description": "Updated endpoint. A rotated secret is shown once and overlaps for 24 hours.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -3320,7 +4043,21 @@ ], "responses": { "204": { - "description": "Endpoint deleted." + "description": "Endpoint deleted.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + } }, "400": { "$ref": "#/components/responses/BadRequest" @@ -3398,6 +4135,20 @@ "responses": { "200": { "description": "Redacted delivery attempts.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -3473,6 +4224,20 @@ "responses": { "202": { "description": "Replay queued.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/RequestId" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimitLimit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimitRemaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimitReset" + } + }, "content": { "application/json": { "schema": { @@ -4607,6 +5372,27 @@ "type": "integer", "minimum": 1 } + }, + "RateLimitLimit": { + "description": "Workspace/product requests permitted in the current window.", + "schema": { + "type": "integer", + "minimum": 1 + } + }, + "RateLimitRemaining": { + "description": "Workspace/product requests remaining in the current window.", + "schema": { + "type": "integer", + "minimum": 0 + } + }, + "RateLimitReset": { + "description": "Seconds until the current rate-limit window resets.", + "schema": { + "type": "integer", + "minimum": 0 + } } }, "responses": { @@ -4854,7 +5640,7 @@ } }, "Unavailable": { - "description": "The product or worker dispatch is temporarily unavailable.", + "description": "The product or its processing capacity is temporarily unavailable.", "headers": { "X-Request-Id": { "$ref": "#/components/headers/RequestId" @@ -4867,9 +5653,9 @@ }, "example": { "type": "https://motifuse.com/problems/service-unavailable", - "title": "The product or worker dispatch is temporarily unavailable.", + "title": "The product or its processing capacity is temporarily unavailable.", "status": 503, - "detail": "The product or worker dispatch is temporarily unavailable.", + "detail": "The product or its processing capacity is temporarily unavailable.", "instance": "/api/v1/example", "code": "service_unavailable", "request_id": "req_01H…" @@ -6388,6 +7174,98 @@ } } }, + "ApiRequest": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "object", + "request_id", + "environment", + "product", + "method", + "endpoint", + "status", + "latency_ms", + "timestamp" + ], + "properties": { + "id": { + "type": "string" + }, + "object": { + "const": "api_request" + }, + "request_id": { + "type": "string" + }, + "environment": { + "type": "string", + "enum": [ + "live", + "test" + ] + }, + "product": { + "type": "string", + "enum": [ + "docforge", + "reconova", + "spectrace" + ] + }, + "method": { + "type": "string" + }, + "endpoint": { + "type": "string" + }, + "status": { + "type": "integer", + "minimum": 100, + "maximum": 599 + }, + "latency_ms": { + "type": "integer", + "minimum": 0 + }, + "api_key_prefix": { + "type": [ + "string", + "null" + ] + }, + "timestamp": { + "type": "string", + "format": "date-time" + } + } + }, + "ApiRequestList": { + "type": "object", + "required": [ + "data", + "has_more", + "next_cursor" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApiRequest" + } + }, + "has_more": { + "type": "boolean" + }, + "next_cursor": { + "type": [ + "string", + "null" + ] + } + } + }, "UsageMetric": { "type": "object", "additionalProperties": true, @@ -6465,12 +7343,8 @@ }, "products": { "type": "object", + "description": "Only products both currently API-entitled and authorized by this key are returned.", "additionalProperties": false, - "required": [ - "docforge", - "reconova", - "spectrace" - ], "properties": { "docforge": { "$ref": "#/components/schemas/ProductUsage" @@ -6483,6 +7357,27 @@ } } }, + "authorized_products": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "docforge", + "reconova", + "spectrace" + ] + } + }, + "period_start": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "note": { + "type": "string" + }, "generated_at": { "type": "string", "format": "date-time" @@ -6492,9 +7387,10 @@ } }, "x-motifuse": { + "authorization_model": "A Motifuse key is scoped to one workspace and may authorize multiple currently entitled products. A stored scope never grants or replaces a current product subscription, and every request re-checks the workspace entitlement.", "environments": { - "live": "Production resources.", - "test": "Currently isolated only for DocForge; Reconova and Spectrace refuse test keys rather than touching live records." + "live": "The only fully supported environment across all products; automations always run live.", + "test": "DocForge compatibility evaluation mode separates jobs, outputs, webhooks, logs, and usage but shares published template definitions read-only, so it is not a full sandbox. Reconova and SpecTrace refuse test keys before touching product records." }, "scopes": [ "docforge:read", @@ -6517,6 +7413,16 @@ ], "idempotency_retention_hours": 24, "request_log_retention_days": 30, + "rate_limits": { + "live": { + "limit": 1000, + "windowSeconds": 3600 + }, + "test": { + "limit": 120, + "windowSeconds": 3600 + } + }, "generative_ai": { "spectrace_comparison": false, "docforge_generation": false, diff --git a/scripts/check-generated.mjs b/scripts/check-generated.mjs index 4540db2..f9e1ba8 100644 --- a/scripts/check-generated.mjs +++ b/scripts/check-generated.mjs @@ -13,7 +13,9 @@ const actual = banner + astToString(await openapiTS(source, { alphabetize: true, defaultNonNullable: false })); const expected = await readFile(generated, "utf8"); -if (expected !== actual) { +const normalizeLineEndings = (value) => value.replace(/\r\n?/g, "\n"); + +if (normalizeLineEndings(expected) !== normalizeLineEndings(actual)) { console.error("Generated OpenAPI declarations are stale. Run npm run openapi:generate."); process.exitCode = 1; } else { diff --git a/src/generated/schema.d.ts b/src/generated/schema.d.ts index d8db3b0..8e911d2 100644 --- a/src/generated/schema.d.ts +++ b/src/generated/schema.d.ts @@ -16,7 +16,7 @@ export interface paths { put?: never; /** * Start a document generation - * @description Pins a published template version, reserves server-owned quota once, stores canonical input, and dispatches an asynchronous worker job. + * @description Pins a published template version, reserves quota once, records canonical input, and queues asynchronous generation. */ post: operations["docforgeCreateGeneration"]; delete?: never; @@ -93,6 +93,23 @@ export interface paths { patch?: never; trace?: never; }; + "/logs": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** List redacted API request logs */ + get: operations["listApiRequestLogs"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/reconova/files": { parameters: { query?: never; @@ -256,7 +273,7 @@ export interface paths { }; /** * Download a comparison export - * @description Returns only export formats implemented by Spectrace (CSV or JSON, selected by query). + * @description Returns only export formats implemented by SpecTrace (CSV or JSON, selected by query). */ get: operations["spectraceExportComparison"]; put?: never; @@ -360,7 +377,7 @@ export interface paths { path?: never; cookie?: never; }; - /** Get Spectrace worker job status */ + /** Get SpecTrace job status */ get: operations["spectraceGetJob"]; put?: never; post?: never; @@ -379,7 +396,7 @@ export interface paths { }; get?: never; put?: never; - /** Cancel a Spectrace job */ + /** Cancel a SpecTrace job */ post: operations["spectraceCancelJob"]; delete?: never; options?: never; @@ -436,7 +453,7 @@ export interface paths { put?: never; /** * Start deterministic comparison - * @description Runs the deterministic comparison engine. Generative AI is not used in this path. + * @description Queues the deterministic comparison engine and returns immediately. Generative AI is not used in this path. */ post: operations["spectraceCreateComparison"]; delete?: never; @@ -515,7 +532,7 @@ export interface paths { delete: operations["deleteWebhookEndpoint"]; options?: never; head?: never; - /** Pause, enable, or rotate a webhook endpoint secret */ + /** Edit, pause, enable, or rotate a webhook endpoint */ patch: operations["updateWebhookEndpoint"]; trace?: never; }; @@ -864,6 +881,28 @@ export interface webhooks { } export interface components { schemas: { + ApiRequest: { + api_key_prefix?: string | null; + endpoint: string; + /** @enum {string} */ + environment: "live" | "test"; + id: string; + latency_ms: number; + method: string; + /** @constant */ + object: "api_request"; + /** @enum {string} */ + product: "docforge" | "reconova" | "spectrace"; + request_id: string; + status: number; + /** Format: date-time */ + timestamp: string; + }; + ApiRequestList: { + data: components["schemas"]["ApiRequest"][]; + has_more: boolean; + next_cursor: string | null; + }; Comparison: { baseline_version_id?: string; confidence?: number | null; @@ -1175,16 +1214,21 @@ export interface components { url: string; }; Usage: { + authorized_products?: ("docforge" | "reconova" | "spectrace")[]; /** @enum {string} */ environment: "live" | "test"; /** Format: date-time */ generated_at: string; + note?: string; /** @constant */ object: "usage"; + /** Format: date-time */ + period_start?: string | null; + /** @description Only products both currently API-entitled and authorized by this key are returned. */ products: { - docforge: components["schemas"]["ProductUsage"]; - reconova: components["schemas"]["ProductUsage"]; - spectrace: components["schemas"]["ProductUsage"]; + docforge?: components["schemas"]["ProductUsage"]; + reconova?: components["schemas"]["ProductUsage"]; + spectrace?: components["schemas"]["ProductUsage"]; }; }; UsageMetric: { @@ -1459,7 +1503,7 @@ export interface components { "application/problem+json": components["schemas"]["Problem"]; }; }; - /** @description The product or worker dispatch is temporarily unavailable. */ + /** @description The product or its processing capacity is temporarily unavailable. */ Unavailable: { headers: { "X-Request-Id": components["headers"]["RequestId"]; @@ -1469,9 +1513,9 @@ export interface components { /** * @example { * "type": "https://motifuse.com/problems/service-unavailable", - * "title": "The product or worker dispatch is temporarily unavailable.", + * "title": "The product or its processing capacity is temporarily unavailable.", * "status": 503, - * "detail": "The product or worker dispatch is temporarily unavailable.", + * "detail": "The product or its processing capacity is temporarily unavailable.", * "instance": "/api/v1/example", * "code": "service_unavailable", * "request_id": "req_01H…" @@ -1510,6 +1554,12 @@ export interface components { }; requestBodies: never; headers: { + /** @description Workspace/product requests permitted in the current window. */ + RateLimitLimit: number; + /** @description Workspace/product requests remaining in the current window. */ + RateLimitRemaining: number; + /** @description Seconds until the current rate-limit window resets. */ + RateLimitReset: number; /** @description Correlation ID for support and logs. */ RequestId: string; /** @description Seconds to wait before retrying. */ @@ -1537,6 +1587,10 @@ export interface operations { /** @description Cursor-paginated jobs. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -1592,6 +1646,10 @@ export interface operations { /** @description Idempotent replay of an existing generation. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -1601,6 +1659,10 @@ export interface operations { /** @description Generation accepted. */ 202: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -1637,6 +1699,10 @@ export interface operations { /** @description Generation state. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -1673,6 +1739,10 @@ export interface operations { /** @description Cancellation accepted or completed. */ 202: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -1718,6 +1788,10 @@ export interface operations { /** @description Short-lived signed R2 download. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -1755,6 +1829,10 @@ export interface operations { /** @description Cursor-paginated templates. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -1773,6 +1851,46 @@ export interface operations { 503: components["responses"]["Unavailable"]; }; }; + listApiRequestLogs: { + parameters: { + query?: { + cursor?: string | null; + limit?: number; + }; + header?: { + /** @description Optional caller correlation ID. Motifuse returns a request ID on every response. */ + "X-Request-Id"?: components["parameters"]["RequestId"]; + }; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Workspace-, environment-, and entitled-product-filtered request metadata. */ + 200: { + headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ApiRequestList"]; + }; + }; + 400: components["responses"]["BadRequest"]; + 401: components["responses"]["Unauthorized"]; + 402: components["responses"]["PaymentRequired"]; + 403: components["responses"]["Forbidden"]; + 404: components["responses"]["NotFound"]; + 409: components["responses"]["Conflict"]; + 422: components["responses"]["Unprocessable"]; + 429: components["responses"]["RateLimited"]; + 500: components["responses"]["InternalError"]; + 503: components["responses"]["Unavailable"]; + }; + }; reconovaListFiles: { parameters: { query?: { @@ -1791,6 +1909,10 @@ export interface operations { /** @description Cursor-paginated files and processing status. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -1837,6 +1959,10 @@ export interface operations { /** @description Upload authorization. */ 201: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -1873,6 +1999,10 @@ export interface operations { /** @description File metadata and current status. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -1911,6 +2041,10 @@ export interface operations { /** @description The already-completed upload and existing job. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -1920,6 +2054,10 @@ export interface operations { /** @description Upload verified; intake job created. */ 201: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -1929,6 +2067,10 @@ export interface operations { /** @description Upload verified; dispatch deferred safely. */ 202: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -1965,6 +2107,10 @@ export interface operations { /** @description Short-lived signed R2 download. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2002,6 +2148,10 @@ export interface operations { /** @description Cursor-paginated jobs. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2038,6 +2188,10 @@ export interface operations { /** @description Job state. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2074,6 +2228,10 @@ export interface operations { /** @description Cancellation accepted or completed. */ 202: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2126,6 +2284,10 @@ export interface operations { /** @description Cleaning job created. */ 201: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2135,6 +2297,10 @@ export interface operations { /** @description Cleaning job accepted. */ 202: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2161,7 +2327,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace comparison ID. */ + /** @description SpecTrace comparison ID. */ comparison_id: string; }; cookie?: never; @@ -2171,6 +2337,10 @@ export interface operations { /** @description Comparison state and summary. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2199,7 +2369,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace comparison ID. */ + /** @description SpecTrace comparison ID. */ comparison_id: string; }; cookie?: never; @@ -2242,7 +2412,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace comparison ID. */ + /** @description SpecTrace comparison ID. */ comparison_id: string; }; cookie?: never; @@ -2252,6 +2422,10 @@ export interface operations { /** @description Cursor-paginated findings. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2278,7 +2452,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace version/file ID. */ + /** @description SpecTrace version/file ID. */ file_id: string; }; cookie?: never; @@ -2288,6 +2462,10 @@ export interface operations { /** @description File state. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2316,7 +2494,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace version/file ID. */ + /** @description SpecTrace version/file ID. */ file_id: string; }; cookie?: never; @@ -2326,6 +2504,10 @@ export interface operations { /** @description Upload verified; security processing queued, or an existing completed upload returned. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2352,7 +2534,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace version/file ID. */ + /** @description SpecTrace version/file ID. */ file_id: string; }; cookie?: never; @@ -2389,7 +2571,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace finding ID. */ + /** @description SpecTrace finding ID. */ finding_id: string; }; cookie?: never; @@ -2399,6 +2581,10 @@ export interface operations { /** @description Finding with evidence and locators. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2425,7 +2611,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace finding ID. */ + /** @description SpecTrace finding ID. */ finding_id: string; }; cookie?: never; @@ -2447,6 +2633,10 @@ export interface operations { /** @description Reviewed finding. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2473,7 +2663,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace job ID. */ + /** @description SpecTrace job ID. */ job_id: string; }; cookie?: never; @@ -2483,6 +2673,10 @@ export interface operations { /** @description Job state. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2509,7 +2703,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace job ID. */ + /** @description SpecTrace job ID. */ job_id: string; }; cookie?: never; @@ -2519,6 +2713,10 @@ export interface operations { /** @description Cancellation accepted. */ 202: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2555,6 +2753,10 @@ export interface operations { /** @description Cursor-paginated projects. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2604,6 +2806,10 @@ export interface operations { /** @description Project created. */ 201: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2630,7 +2836,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace project ID. */ + /** @description SpecTrace project ID. */ project_id: string; }; cookie?: never; @@ -2640,6 +2846,10 @@ export interface operations { /** @description Project. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2666,7 +2876,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace project ID. */ + /** @description SpecTrace project ID. */ project_id: string; }; cookie?: never; @@ -2676,6 +2886,10 @@ export interface operations { /** @description Project scheduled for retention-safe deletion. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2706,7 +2920,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace project ID. */ + /** @description SpecTrace project ID. */ project_id: string; }; cookie?: never; @@ -2730,6 +2944,10 @@ export interface operations { /** @description Updated project. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2759,7 +2977,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace project ID. */ + /** @description SpecTrace project ID. */ project_id: string; }; cookie?: never; @@ -2769,6 +2987,10 @@ export interface operations { /** @description Comparisons. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2797,7 +3019,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace project ID. */ + /** @description SpecTrace project ID. */ project_id: string; }; cookie?: never; @@ -2818,15 +3040,23 @@ export interface operations { /** @description Existing comparison returned idempotently. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { "application/json": components["schemas"]["Comparison"]; }; }; - /** @description Comparison and job created. */ - 201: { + /** @description Comparison accepted and queued. */ + 202: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2856,7 +3086,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace project ID. */ + /** @description SpecTrace project ID. */ project_id: string; }; cookie?: never; @@ -2866,6 +3096,10 @@ export interface operations { /** @description Project files. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2894,7 +3128,7 @@ export interface operations { "X-Request-Id"?: components["parameters"]["RequestId"]; }; path: { - /** @description Spectrace project ID. */ + /** @description SpecTrace project ID. */ project_id: string; }; cookie?: never; @@ -2917,6 +3151,10 @@ export interface operations { /** @description Quarantined upload authorization. */ 201: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2950,6 +3188,10 @@ export interface operations { /** @description Server-owned usage balances, limits, remaining capacity, and reset timestamps. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -2983,6 +3225,10 @@ export interface operations { /** @description Webhook endpoints. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -3030,6 +3276,10 @@ export interface operations { /** @description Endpoint created; signing secret shown once. */ 201: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -3066,6 +3316,10 @@ export interface operations { /** @description Endpoint deleted. */ 204: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content?: never; @@ -3100,10 +3354,14 @@ export interface operations { /** * @example { * "status": "active", + * "events": [ + * "spectrace.comparison.completed" + * ], * "rotate_secret": false * } */ "application/json": { + events?: ("job.created" | "job.completed" | "job.failed" | "document.generated" | "document.delivered" | "document.delivery_failed" | "document.expired" | "approval.requested" | "approval.decided" | "docforge.generation.created" | "docforge.generation.completed" | "docforge.generation.failed" | "reconova.job.completed" | "reconova.job.failed" | "spectrace.file.ready" | "spectrace.file.failed" | "spectrace.comparison.completed" | "spectrace.comparison.failed")[]; rotate_secret?: boolean; /** @enum {string} */ status?: "active" | "disabled"; @@ -3114,6 +3372,10 @@ export interface operations { /** @description Updated endpoint. A rotated secret is shown once and overlaps for 24 hours. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -3150,6 +3412,10 @@ export interface operations { /** @description Redacted delivery attempts. */ 200: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { @@ -3186,6 +3452,10 @@ export interface operations { /** @description Replay queued. */ 202: { headers: { + "RateLimit-Limit": components["headers"]["RateLimitLimit"]; + "RateLimit-Remaining": components["headers"]["RateLimitRemaining"]; + "RateLimit-Reset": components["headers"]["RateLimitReset"]; + "X-Request-Id": components["headers"]["RequestId"]; [name: string]: unknown; }; content: { diff --git a/tests/contract.test.mjs b/tests/contract.test.mjs index 1f9e27f..a6c9d6a 100644 --- a/tests/contract.test.mjs +++ b/tests/contract.test.mjs @@ -10,17 +10,17 @@ test("the vendored OpenAPI contract is the unified production v1 surface", () => assert.equal(document.openapi, "3.1.2"); assert.equal(document.info.version, "1.0.0"); assert.equal(document.servers[0].url, "https://motifuse.com/api/v1"); - assert.equal(Object.keys(document.paths).length, 31); + assert.equal(Object.keys(document.paths).length, 32); const operations = Object.values(document.paths).flatMap((item) => ["get", "post", "patch", "delete"].flatMap((method) => (item[method] ? [item[method]] : [])), ); - assert.equal(operations.length, 41); - assert.equal(new Set(operations.map((operation) => operation.operationId)).size, 41); + assert.equal(operations.length, 42); + assert.equal(new Set(operations.map((operation) => operation.operationId)).size, 42); }); test("the contract includes only supported products and named typed collection responses", () => { for (const path of Object.keys(document.paths)) { - assert.match(path, /^\/(docforge|reconova|spectrace|webhooks|usage)/); + assert.match(path, /^\/(docforge|reconova|spectrace|webhooks|usage|logs)/); } assert.equal( document.paths["/spectrace/comparisons/{comparison_id}/findings"].get.responses[200].content[