@skyramp/mcp 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/index.js +47 -2
- package/build/playwright/PlaywrightTraceService.d.ts +8 -0
- package/build/playwright/PlaywrightTraceService.js +1 -0
- package/build/playwright/registerPlaywrightTools.js +42 -1
- package/build/prompts/enhance-assertions/sharedAssertionRules.js +19 -0
- package/build/prompts/pom-aware-code-reuse.js +17 -8
- package/build/prompts/test-maintenance/actionsInstructions.js +2 -2
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +1 -4
- package/build/prompts/test-recommendation/recommendationSections.d.ts +1 -1
- package/build/prompts/test-recommendation/recommendationSections.js +5 -5
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +13 -7
- package/build/prompts/testbot/testbot-prompts.d.ts +2 -12
- package/build/prompts/testbot/testbot-prompts.js +28 -16
- package/build/recommendation/discriminators.d.ts +7 -1
- package/build/recommendation/discriminators.js +16 -3
- package/build/resources/testbotResource.js +20 -4
- package/build/services/ScenarioGenerationService.js +5 -2
- package/build/services/TestExecutionService.d.ts +13 -8
- package/build/services/TestExecutionService.js +73 -26
- package/build/services/TestGenerationService.js +24 -9
- package/build/services/containerEnv.d.ts +12 -1
- package/build/services/containerEnv.js +118 -1
- package/build/tools/executeSkyrampTestTool.d.ts +9 -0
- package/build/tools/executeSkyrampTestTool.js +20 -6
- package/build/tools/execution-video-state.d.ts +21 -0
- package/build/tools/execution-video-state.js +51 -0
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +31 -11
- package/build/tools/generate-tests/planGuard.d.ts +5 -5
- package/build/tools/generate-tests/planGuard.js +5 -17
- package/build/tools/queryProxyMocksTool.js +0 -1
- package/build/tools/submitReportTool.d.ts +83 -10
- package/build/tools/submitReportTool.js +179 -29
- package/build/tools/test-management/actionsTool.js +52 -41
- package/build/tools/test-management/analyzeChangesTool.d.ts +11 -0
- package/build/tools/test-management/analyzeChangesTool.js +37 -33
- package/build/tools/test-management/analyzeTestHealthTool.js +3 -3
- package/build/tools/test-management/registerTestPlanTool.js +113 -31
- package/build/types/TestAnalysis.d.ts +7 -3
- package/build/types/TestExecution.d.ts +14 -0
- package/build/types/TestTypes.js +3 -2
- package/build/types/TestbotPromptOptions.d.ts +34 -0
- package/build/types/TestbotPromptOptions.js +1 -0
- package/build/types/TestbotReport.d.ts +10 -0
- package/build/types/TestbotReport.js +10 -1
- package/build/types/index.d.ts +2 -0
- package/build/types/index.js +1 -0
- package/build/utils/AnalysisStateManager.d.ts +36 -2
- package/build/utils/AnalysisStateManager.js +34 -13
- package/build/utils/frontendSelectors.js +0 -1
- package/build/utils/gitStaging.d.ts +5 -0
- package/build/utils/gitStaging.js +1 -1
- package/build/utils/pom-catalog.d.ts +23 -0
- package/build/utils/pom-catalog.js +30 -0
- package/build/utils/pom-scope/pom-files.d.ts +14 -0
- package/build/utils/pom-scope/pom-files.js +32 -6
- package/build/utils/pom-scope/testIdDiscovery.d.ts +40 -0
- package/build/utils/pom-scope/testIdDiscovery.js +104 -0
- package/build/utils/reportVerification.d.ts +7 -2
- package/build/utils/reportVerification.js +9 -3
- package/build/utils/scenarioDrafting.js +7 -1
- package/build/utils/skyrampMdContent.d.ts +1 -1
- package/build/utils/skyrampMdContent.js +1 -1
- package/build/utils/urlPath.d.ts +37 -0
- package/build/utils/urlPath.js +55 -0
- package/build/utils/utils.d.ts +45 -0
- package/build/utils/utils.js +50 -0
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/utils/workspaceAuth.d.ts +15 -15
- package/build/utils/workspaceAuth.js +32 -17
- package/build/workspace/queryParamResolution.d.ts +93 -0
- package/build/workspace/queryParamResolution.js +201 -0
- package/build/workspace/workspace.d.ts +104 -0
- package/build/workspace/workspace.js +24 -0
- package/node_modules/playwright/ThirdPartyNotices.txt +319 -266
- package/node_modules/playwright/lib/dom-analyzer/blueprint.js +154 -27
- package/node_modules/playwright/lib/dom-analyzer/crawler.js +2 -2
- package/node_modules/playwright/lib/mcp/browser/tools/pageBlueprint.js +1 -1
- package/node_modules/playwright/lib/mcp/browser/tools/sitemap.js +6 -2
- package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +3 -2
- package/node_modules/playwright/lib/mcp/skyramp/resultCode.js +4 -3
- package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +15 -3
- package/node_modules/playwright/lib/mcp/skyramp/specImport.js +781 -0
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +14 -3
- package/node_modules/playwright/lib/mcp/test/resultCode.test.js +2 -1
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +1 -1
- package/node_modules/playwright/lib/mcp/test/skyRampExport.test.js +30 -0
- package/node_modules/playwright/lib/transform/babelBundleImpl.js +200 -199
- package/node_modules/playwright/node_modules/playwright-core/ThirdPartyNotices.txt +3 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/.package-lock.json +3 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/adapter/aws-lambda/handler.js +16 -25
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/adapter/bun/websocket.js +3 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/adapter/lambda-edge/handler.js +20 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/adapter/aws-lambda/handler.js +16 -25
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/adapter/bun/websocket.js +3 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/adapter/lambda-edge/handler.js +20 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/client/client.js +10 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/client/utils.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/helper/css/common.js +3 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/helper/css/index.js +9 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/base.js +8 -14
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/components.js +41 -21
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/context.js +131 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/streaming.js +9 -7
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/bearer-auth/index.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/cache/index.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/compress/index.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/cors/index.js +2 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/etag/index.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/language/language.js +10 -32
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/method-override/index.js +5 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/serve-static/index.js +2 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/timing/timing.js +3 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/router/trie-router/node.js +9 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/body.js +12 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/buffer.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/ipaddr.js +6 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/validator/validator.js +3 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/client/client.js +10 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/client/utils.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/helper/css/common.js +3 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/helper/css/index.js +9 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/base.js +15 -15
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/components.js +42 -22
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/context.js +129 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/streaming.js +10 -8
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/bearer-auth/index.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/cache/index.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/compress/index.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/cors/index.js +2 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/etag/index.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/language/language.js +10 -32
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/method-override/index.js +5 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/serve-static/index.js +2 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/timing/timing.js +3 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/router/trie-router/node.js +9 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/adapter/aws-lambda/handler.d.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/adapter/lambda-edge/handler.d.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/helper/websocket/index.d.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/jsx/base.d.ts +1 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/jsx/context.d.ts +39 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/middleware/context-storage/index.d.ts +2 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/middleware/language/language.d.ts +18 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/utils/body.d.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/utils/types.d.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/body.js +12 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/buffer.js +2 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/ipaddr.js +6 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/validator/validator.js +3 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/package.json +29 -22
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/package-lock.json +3 -3
- package/node_modules/playwright/node_modules/playwright-core/lib/generated/pollingRecorderSource.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +5 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/specReader.js +781 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderApp.js +25 -6
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/skyramp/replayEngine.js +3 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/utils.js +2 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/htmlReport/index.html +253 -27
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/{codeMirrorModule-D0BjbCb7.js → codeMirrorModule-DtudTj_v.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-BpDwp16L.js +422 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/index.html +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-Bzd72-bG.js → codeMirrorModule-FNMuBzX1.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-Co9upU5h.js +1035 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.DXNIQ_dx.js +2 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.CIKB3XSv.js +5 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/generated/pollingRecorderSource.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +4 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/specReader.ts +1028 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderApp.ts +31 -8
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/skyramp/replayEngine.ts +1 -0
- package/node_modules/playwright/node_modules/playwright-core/src/utils.ts +1 -0
- package/node_modules/playwright/package.json +1 -1
- package/package.json +10 -6
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/tsconfig.build.tsbuildinfo +0 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/recorder/assets/index-lvTRGFx-.js +0 -193
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-DzxTioTK.js +0 -809
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.BGc30U3S.js +0 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.IaDrb29A.js +0 -5
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ScopedQueryParams } from "../workspace/queryParamResolution.js";
|
|
1
2
|
/**
|
|
2
3
|
* Reads `.skyramp/workspace.yml`.
|
|
3
4
|
*
|
|
@@ -133,20 +134,6 @@ export declare function getWorkspaceAuthConfig(repositoryPath: string): Promise<
|
|
|
133
134
|
/** True when any workspace service declares api.skipTLSVerify — the SUT serves
|
|
134
135
|
* self-signed TLS and generated tests must skip cert verification (SKYR-3961). */
|
|
135
136
|
export declare function getWorkspaceSkipTLSVerify(repositoryPath: string): Promise<boolean>;
|
|
136
|
-
/**
|
|
137
|
-
* Resolve api.defaultQueryParams for test generation — query params always
|
|
138
|
-
* attached to every generated request for this service (e.g. RBAC context
|
|
139
|
-
* an authorization layer requires beyond the auth header). Same
|
|
140
|
-
* first-matching-service convention as getWorkspaceAuthConfig (SKYR-4050).
|
|
141
|
-
*
|
|
142
|
-
* Unlike readWorkspaceConfigRaw (which delegates to WorkspaceConfigManager and
|
|
143
|
-
* resolves EXACTLY `<repositoryPath>/.skyramp/workspace.yml`), this walks up
|
|
144
|
-
* from `repositoryPath` to find the nearest `.skyramp/workspace.yml` — both
|
|
145
|
-
* call sites pass a nested test-output directory (e.g. `<repo>/tests/skyramp`),
|
|
146
|
-
* not the repo root, so an exact-match lookup would silently never find the
|
|
147
|
-
* config. Mirrors the sync fs.existsSync walk-up in
|
|
148
|
-
* generateBatchScenarioRestTool.ts.
|
|
149
|
-
*/
|
|
150
137
|
/**
|
|
151
138
|
* Walk up from `startDir` to find the nearest `.skyramp/workspace.yml`, checking
|
|
152
139
|
* `startDir` itself first and continuing through every ancestor INCLUDING the
|
|
@@ -156,7 +143,20 @@ export declare function getWorkspaceSkipTLSVerify(repositoryPath: string): Promi
|
|
|
156
143
|
* `existsSync` is injectable for testing; defaults to the real filesystem.
|
|
157
144
|
*/
|
|
158
145
|
export declare function findWorkspaceConfigPath(startDir: string, existsSync?: (p: string) => boolean): string | null;
|
|
159
|
-
|
|
146
|
+
/**
|
|
147
|
+
* Resolve api.defaultQueryParams together with api.queryParamOverrides for test
|
|
148
|
+
* generation (SKYR-4127). Same first-matching-service convention as
|
|
149
|
+
* getWorkspaceAuthConfig: the first service declaring EITHER key wins.
|
|
150
|
+
*
|
|
151
|
+
* Reads the YAML directly rather than through the Zod schema — the walk-up must
|
|
152
|
+
* stay cheap, and hand-rolled fixtures routinely omit fields the strict schema
|
|
153
|
+
* requires. normalizeOverrides therefore validates the override list itself,
|
|
154
|
+
* once per load rather than per request.
|
|
155
|
+
*
|
|
156
|
+
* Returns undefined when no config is found or no service declares either key,
|
|
157
|
+
* so callers can skip the merge entirely.
|
|
158
|
+
*/
|
|
159
|
+
export declare function getWorkspaceScopedQueryParams(repositoryPath: string): Promise<ScopedQueryParams | undefined>;
|
|
160
160
|
/**
|
|
161
161
|
* Merge workspace-declared default query params into a comma-separated
|
|
162
162
|
* "key=value,key2=value2" queryParams string (the format used by
|
|
@@ -2,6 +2,7 @@ import * as fs from "fs";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import yaml from "js-yaml";
|
|
4
4
|
import { WorkspaceConfigManager } from "../workspace/workspace.js";
|
|
5
|
+
import { normalizeBaseMap, normalizeOverrides } from "../workspace/queryParamResolution.js";
|
|
5
6
|
import { logger } from "./logger.js";
|
|
6
7
|
/**
|
|
7
8
|
* Reads `.skyramp/workspace.yml`.
|
|
@@ -363,20 +364,6 @@ export async function getWorkspaceSkipTLSVerify(repositoryPath) {
|
|
|
363
364
|
return false;
|
|
364
365
|
return (rawConfig.services ?? []).some((s) => s.api?.skipTLSVerify === true);
|
|
365
366
|
}
|
|
366
|
-
/**
|
|
367
|
-
* Resolve api.defaultQueryParams for test generation — query params always
|
|
368
|
-
* attached to every generated request for this service (e.g. RBAC context
|
|
369
|
-
* an authorization layer requires beyond the auth header). Same
|
|
370
|
-
* first-matching-service convention as getWorkspaceAuthConfig (SKYR-4050).
|
|
371
|
-
*
|
|
372
|
-
* Unlike readWorkspaceConfigRaw (which delegates to WorkspaceConfigManager and
|
|
373
|
-
* resolves EXACTLY `<repositoryPath>/.skyramp/workspace.yml`), this walks up
|
|
374
|
-
* from `repositoryPath` to find the nearest `.skyramp/workspace.yml` — both
|
|
375
|
-
* call sites pass a nested test-output directory (e.g. `<repo>/tests/skyramp`),
|
|
376
|
-
* not the repo root, so an exact-match lookup would silently never find the
|
|
377
|
-
* config. Mirrors the sync fs.existsSync walk-up in
|
|
378
|
-
* generateBatchScenarioRestTool.ts.
|
|
379
|
-
*/
|
|
380
367
|
/**
|
|
381
368
|
* Walk up from `startDir` to find the nearest `.skyramp/workspace.yml`, checking
|
|
382
369
|
* `startDir` itself first and continuing through every ancestor INCLUDING the
|
|
@@ -398,15 +385,43 @@ export function findWorkspaceConfigPath(startDir, existsSync = fs.existsSync) {
|
|
|
398
385
|
searchDir = parentDir;
|
|
399
386
|
}
|
|
400
387
|
}
|
|
401
|
-
|
|
388
|
+
/**
|
|
389
|
+
* Resolve api.defaultQueryParams together with api.queryParamOverrides for test
|
|
390
|
+
* generation (SKYR-4127). Same first-matching-service convention as
|
|
391
|
+
* getWorkspaceAuthConfig: the first service declaring EITHER key wins.
|
|
392
|
+
*
|
|
393
|
+
* Reads the YAML directly rather than through the Zod schema — the walk-up must
|
|
394
|
+
* stay cheap, and hand-rolled fixtures routinely omit fields the strict schema
|
|
395
|
+
* requires. normalizeOverrides therefore validates the override list itself,
|
|
396
|
+
* once per load rather than per request.
|
|
397
|
+
*
|
|
398
|
+
* Returns undefined when no config is found or no service declares either key,
|
|
399
|
+
* so callers can skip the merge entirely.
|
|
400
|
+
*/
|
|
401
|
+
export async function getWorkspaceScopedQueryParams(repositoryPath) {
|
|
402
402
|
const wsConfigPath = findWorkspaceConfigPath(repositoryPath);
|
|
403
403
|
if (!wsConfigPath)
|
|
404
404
|
return undefined;
|
|
405
405
|
try {
|
|
406
406
|
const raw = fs.readFileSync(wsConfigPath, "utf-8");
|
|
407
407
|
const parsed = yaml.load(raw);
|
|
408
|
-
const
|
|
409
|
-
|
|
408
|
+
const declaring = (parsed?.services ?? []).filter((s) => s?.api?.defaultQueryParams !== undefined || s?.api?.queryParamOverrides !== undefined);
|
|
409
|
+
const svc = declaring[0];
|
|
410
|
+
if (!svc)
|
|
411
|
+
return undefined;
|
|
412
|
+
// First-wins is deliberate (same convention as getWorkspaceAuthConfig) —
|
|
413
|
+
// but silently discarding a later service's config is easy to miss, so
|
|
414
|
+
// name what's being ignored (Finding 3, SKYR-4127 final review).
|
|
415
|
+
if (declaring.length > 1) {
|
|
416
|
+
logger.warning("Multiple services declare defaultQueryParams/queryParamOverrides — using the first, ignoring the rest", {
|
|
417
|
+
used: svc.serviceName,
|
|
418
|
+
ignored: declaring.slice(1).map((s) => s.serviceName),
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
base: normalizeBaseMap(svc.api.defaultQueryParams),
|
|
423
|
+
overrides: normalizeOverrides(svc.api.queryParamOverrides),
|
|
424
|
+
};
|
|
410
425
|
}
|
|
411
426
|
catch {
|
|
412
427
|
return undefined;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export interface QueryParamOverride {
|
|
2
|
+
/** picomatch glob matched against the request URL pathname. OpenAPI-style
|
|
3
|
+
* `{param}` placeholders are treated as a single-segment wildcard. */
|
|
4
|
+
pathPattern: string;
|
|
5
|
+
/**
|
|
6
|
+
* Known wart (Finding 6, SKYR-4127 final review): `values: {}` is a valid,
|
|
7
|
+
* deliberately-typed empty map. If its pathPattern is the single most
|
|
8
|
+
* specific match for a request, it wins and contributes nothing — silently
|
|
9
|
+
* suppressing a broader override that would otherwise have applied. Not
|
|
10
|
+
* validated against because an intentionally no-op override (e.g. "no
|
|
11
|
+
* extra params for this path, unlike its siblings") is a legitimate use.
|
|
12
|
+
*/
|
|
13
|
+
values: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
export interface ScopedQueryParams {
|
|
16
|
+
/** api.defaultQueryParams — applied to every request for the service. */
|
|
17
|
+
base: Record<string, string>;
|
|
18
|
+
/** api.queryParamOverrides — at most one applies to any given request. */
|
|
19
|
+
overrides: QueryParamOverride[];
|
|
20
|
+
}
|
|
21
|
+
/** Pathname of a full endpoint URL, or undefined if unparseable.
|
|
22
|
+
* `endpointURL` is contractually base URL + path, so the result includes any
|
|
23
|
+
* base-path prefix: `https://host/api/v1/persons/42` -> `/api/v1/persons/42`.
|
|
24
|
+
*
|
|
25
|
+
* Delegates to the shared helper so this module and the plan guard cannot
|
|
26
|
+
* disagree about what an endpointURL's path is — the shared version also
|
|
27
|
+
* restores `{param}` placeholders that `new URL()` percent-encodes, which
|
|
28
|
+
* patterns are authored with. */
|
|
29
|
+
export declare function extractPathname(endpointURL: string | undefined): string | undefined;
|
|
30
|
+
/** True when a resolved config would contribute no query params at all.
|
|
31
|
+
* Both call sites skip the merge entirely in that case, so an empty
|
|
32
|
+
* declaration leaves queryParams untouched rather than setting it to an empty
|
|
33
|
+
* value. Defined once so the two sites cannot drift on what "empty" means. */
|
|
34
|
+
export declare function contributesNothing(config: ScopedQueryParams): boolean;
|
|
35
|
+
/** Batch-scenario steps carry a bare path that may or may not be rooted. */
|
|
36
|
+
export declare function ensureLeadingSlash(p: string): string;
|
|
37
|
+
/** Rank for most-specific-wins: length of the literal prefix before the first
|
|
38
|
+
* wildcard, computed on the ORIGINAL pattern so `{id}` still counts. */
|
|
39
|
+
export declare function patternSpecificity(pathPattern: string): number;
|
|
40
|
+
/**
|
|
41
|
+
* Validate and coerce the raw `api.queryParamOverrides` value read from YAML.
|
|
42
|
+
*
|
|
43
|
+
* The read path in workspaceAuth.ts parses workspace.yml directly rather than
|
|
44
|
+
* through the Zod schema (the walk-up read must stay cheap), so nothing has
|
|
45
|
+
* validated this yet. Malformed entries are dropped with a warning instead of
|
|
46
|
+
* failing generation. Called once per load, not per request.
|
|
47
|
+
*/
|
|
48
|
+
export declare function normalizeOverrides(raw: unknown): QueryParamOverride[];
|
|
49
|
+
/**
|
|
50
|
+
* Validate and coerce the raw `api.defaultQueryParams` value read from YAML.
|
|
51
|
+
*
|
|
52
|
+
* Same reason `normalizeOverrides` exists: this read path deliberately bypasses
|
|
53
|
+
* the Zod schema, so nothing has checked the shape. Without this, a scalar
|
|
54
|
+
* declaration (`defaultQueryParams: just-a-string`) is spread by key and yields
|
|
55
|
+
* one query param per character — `0=j&1=u&2=s&…` sent to the server. A list
|
|
56
|
+
* behaves the same way, and a map with non-string values would reach
|
|
57
|
+
* mergeQueryParams* and be dropped there with a less specific warning.
|
|
58
|
+
*
|
|
59
|
+
* Anything that is not a plain object of string values degrades to an empty
|
|
60
|
+
* base map with a warning naming what was found.
|
|
61
|
+
*/
|
|
62
|
+
export declare function normalizeBaseMap(raw: unknown): Record<string, string>;
|
|
63
|
+
/**
|
|
64
|
+
* Effective query params for one request path: the base map with the single
|
|
65
|
+
* most-specific matching override layered on top.
|
|
66
|
+
*
|
|
67
|
+
* Overrides never merge with each other — at most one wins. Ranking is by
|
|
68
|
+
* literal prefix length, then total pattern length, then document order.
|
|
69
|
+
*
|
|
70
|
+
* Whenever the patterns differ in specificity the result is independent of how
|
|
71
|
+
* the YAML list is ordered. Document order is only reached when two patterns
|
|
72
|
+
* tie on BOTH earlier criteria, and reordering the list does change the winner
|
|
73
|
+
* in that case — which is why a tie emits an ambiguity warning naming both
|
|
74
|
+
* patterns rather than resolving silently.
|
|
75
|
+
*/
|
|
76
|
+
export declare function resolveQueryParamsForPath(config: ScopedQueryParams, pathname: string): Record<string, string>;
|
|
77
|
+
/** Environment variables this workspace's query params defer to, in declaration
|
|
78
|
+
* order and deduplicated (SKYR-4127).
|
|
79
|
+
*
|
|
80
|
+
* These references resolve where the test RUNS, so under Testbot they resolve
|
|
81
|
+
* inside the executor container — whose environment is a fixed allowlist. A
|
|
82
|
+
* name that never reaches the container resolves to nothing, and an unresolved
|
|
83
|
+
* query param is dropped from the request silently, producing a passing test
|
|
84
|
+
* that exercised the wrong identity. The executor uses this list to forward
|
|
85
|
+
* exactly what the workspace asked for.
|
|
86
|
+
*
|
|
87
|
+
* Deliberately derived from the declarations rather than from the ambient
|
|
88
|
+
* environment: nothing is forwarded that workspace.yml did not name.
|
|
89
|
+
*
|
|
90
|
+
* Both maps are read even though `defaultQueryParams` documents itself as
|
|
91
|
+
* literal-only — that is a doc contract the core loader does not enforce, and
|
|
92
|
+
* a declaration there would otherwise reproduce the original silent failure. */
|
|
93
|
+
export declare function extractEnvVarNames(config: ScopedQueryParams): string[];
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path-scoped resolution for workspace-declared query params (SKYR-4127).
|
|
3
|
+
*
|
|
4
|
+
* SKYR-4050 shipped a single flat `api.defaultQueryParams` map applied
|
|
5
|
+
* identically to every generated request. That is too coarse for APIs whose
|
|
6
|
+
* authorization context is per-endpoint (e.g. an RBAC role validated against a
|
|
7
|
+
* different allow-list per path). `api.queryParamOverrides` layers path-scoped
|
|
8
|
+
* values on top of that base map.
|
|
9
|
+
*
|
|
10
|
+
* Deliberately free of filesystem access so it unit-tests directly — reading
|
|
11
|
+
* workspace.yml lives in utils/workspaceAuth.ts.
|
|
12
|
+
*/
|
|
13
|
+
import picomatch from "picomatch";
|
|
14
|
+
import { logger } from "../utils/logger.js";
|
|
15
|
+
import { pathFromEndpointURL } from "../utils/urlPath.js";
|
|
16
|
+
/** Characters that terminate a pattern's literal prefix.
|
|
17
|
+
*
|
|
18
|
+
* `{` counts because an OpenAPI-style `{id}` placeholder becomes `*` before
|
|
19
|
+
* matching. `[` and `(` count because picomatch also treats character classes
|
|
20
|
+
* (`[0-9]`) and extglobs (`+(a|b)`) as wildcards — without them a pattern like
|
|
21
|
+
* `/v1/users/[0-9]*` would be ranked as though `[` were literal, scoring it
|
|
22
|
+
* more specific than it is and letting it beat a genuinely narrower pattern. */
|
|
23
|
+
const WILDCARD_CHARS = ["*", "?", "{", "[", "("];
|
|
24
|
+
/** Pathname of a full endpoint URL, or undefined if unparseable.
|
|
25
|
+
* `endpointURL` is contractually base URL + path, so the result includes any
|
|
26
|
+
* base-path prefix: `https://host/api/v1/persons/42` -> `/api/v1/persons/42`.
|
|
27
|
+
*
|
|
28
|
+
* Delegates to the shared helper so this module and the plan guard cannot
|
|
29
|
+
* disagree about what an endpointURL's path is — the shared version also
|
|
30
|
+
* restores `{param}` placeholders that `new URL()` percent-encodes, which
|
|
31
|
+
* patterns are authored with. */
|
|
32
|
+
export function extractPathname(endpointURL) {
|
|
33
|
+
return pathFromEndpointURL(endpointURL);
|
|
34
|
+
}
|
|
35
|
+
/** True when a resolved config would contribute no query params at all.
|
|
36
|
+
* Both call sites skip the merge entirely in that case, so an empty
|
|
37
|
+
* declaration leaves queryParams untouched rather than setting it to an empty
|
|
38
|
+
* value. Defined once so the two sites cannot drift on what "empty" means. */
|
|
39
|
+
export function contributesNothing(config) {
|
|
40
|
+
return Object.keys(config.base).length === 0 && config.overrides.length === 0;
|
|
41
|
+
}
|
|
42
|
+
/** Batch-scenario steps carry a bare path that may or may not be rooted. */
|
|
43
|
+
export function ensureLeadingSlash(p) {
|
|
44
|
+
return p.startsWith("/") ? p : `/${p}`;
|
|
45
|
+
}
|
|
46
|
+
/** Rank for most-specific-wins: length of the literal prefix before the first
|
|
47
|
+
* wildcard, computed on the ORIGINAL pattern so `{id}` still counts. */
|
|
48
|
+
export function patternSpecificity(pathPattern) {
|
|
49
|
+
let earliest = pathPattern.length;
|
|
50
|
+
for (const ch of WILDCARD_CHARS) {
|
|
51
|
+
const idx = pathPattern.indexOf(ch);
|
|
52
|
+
if (idx !== -1 && idx < earliest)
|
|
53
|
+
earliest = idx;
|
|
54
|
+
}
|
|
55
|
+
return earliest;
|
|
56
|
+
}
|
|
57
|
+
/** Rewrite OpenAPI-style `{param}` placeholders to a single-segment wildcard.
|
|
58
|
+
* Without this, picomatch reads `{id}` as brace alternation and matches only
|
|
59
|
+
* the literal string `/v1/persons/id`. Brace alternation is therefore not
|
|
60
|
+
* supported in patterns — an accepted trade. */
|
|
61
|
+
function toGlob(pathPattern) {
|
|
62
|
+
return pathPattern.replace(/\{[^/}]*\}/g, "*");
|
|
63
|
+
}
|
|
64
|
+
function isStringMap(v) {
|
|
65
|
+
if (!v || typeof v !== "object" || Array.isArray(v))
|
|
66
|
+
return false;
|
|
67
|
+
return Object.values(v).every((x) => typeof x === "string");
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Validate and coerce the raw `api.queryParamOverrides` value read from YAML.
|
|
71
|
+
*
|
|
72
|
+
* The read path in workspaceAuth.ts parses workspace.yml directly rather than
|
|
73
|
+
* through the Zod schema (the walk-up read must stay cheap), so nothing has
|
|
74
|
+
* validated this yet. Malformed entries are dropped with a warning instead of
|
|
75
|
+
* failing generation. Called once per load, not per request.
|
|
76
|
+
*/
|
|
77
|
+
export function normalizeOverrides(raw) {
|
|
78
|
+
if (!Array.isArray(raw)) {
|
|
79
|
+
if (raw !== undefined) {
|
|
80
|
+
logger.warning("Ignoring api.queryParamOverrides — expected a list", { got: typeof raw });
|
|
81
|
+
}
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
const result = [];
|
|
85
|
+
for (const entry of raw) {
|
|
86
|
+
const e = entry;
|
|
87
|
+
if (!e || typeof e !== "object" || typeof e.pathPattern !== "string" || e.pathPattern.length === 0) {
|
|
88
|
+
logger.warning("Skipping api.queryParamOverrides entry with a missing or empty pathPattern");
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (!isStringMap(e.values)) {
|
|
92
|
+
logger.warning("Skipping api.queryParamOverrides entry whose values are not a string map", {
|
|
93
|
+
pathPattern: e.pathPattern,
|
|
94
|
+
});
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
result.push({ pathPattern: e.pathPattern, values: e.values });
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Validate and coerce the raw `api.defaultQueryParams` value read from YAML.
|
|
103
|
+
*
|
|
104
|
+
* Same reason `normalizeOverrides` exists: this read path deliberately bypasses
|
|
105
|
+
* the Zod schema, so nothing has checked the shape. Without this, a scalar
|
|
106
|
+
* declaration (`defaultQueryParams: just-a-string`) is spread by key and yields
|
|
107
|
+
* one query param per character — `0=j&1=u&2=s&…` sent to the server. A list
|
|
108
|
+
* behaves the same way, and a map with non-string values would reach
|
|
109
|
+
* mergeQueryParams* and be dropped there with a less specific warning.
|
|
110
|
+
*
|
|
111
|
+
* Anything that is not a plain object of string values degrades to an empty
|
|
112
|
+
* base map with a warning naming what was found.
|
|
113
|
+
*/
|
|
114
|
+
export function normalizeBaseMap(raw) {
|
|
115
|
+
if (raw === undefined || raw === null)
|
|
116
|
+
return {};
|
|
117
|
+
if (isStringMap(raw))
|
|
118
|
+
return raw;
|
|
119
|
+
logger.warning("Ignoring api.defaultQueryParams — expected a map of string values", { got: Array.isArray(raw) ? "array" : typeof raw });
|
|
120
|
+
return {};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Effective query params for one request path: the base map with the single
|
|
124
|
+
* most-specific matching override layered on top.
|
|
125
|
+
*
|
|
126
|
+
* Overrides never merge with each other — at most one wins. Ranking is by
|
|
127
|
+
* literal prefix length, then total pattern length, then document order.
|
|
128
|
+
*
|
|
129
|
+
* Whenever the patterns differ in specificity the result is independent of how
|
|
130
|
+
* the YAML list is ordered. Document order is only reached when two patterns
|
|
131
|
+
* tie on BOTH earlier criteria, and reordering the list does change the winner
|
|
132
|
+
* in that case — which is why a tie emits an ambiguity warning naming both
|
|
133
|
+
* patterns rather than resolving silently.
|
|
134
|
+
*/
|
|
135
|
+
export function resolveQueryParamsForPath(config, pathname) {
|
|
136
|
+
const matches = config.overrides
|
|
137
|
+
.map((override, index) => ({ override, index }))
|
|
138
|
+
.filter(({ override }) => picomatch.isMatch(pathname, toGlob(override.pathPattern)));
|
|
139
|
+
if (matches.length === 0)
|
|
140
|
+
return { ...config.base };
|
|
141
|
+
matches.sort((a, b) => {
|
|
142
|
+
const specDiff = patternSpecificity(b.override.pathPattern) - patternSpecificity(a.override.pathPattern);
|
|
143
|
+
if (specDiff !== 0)
|
|
144
|
+
return specDiff;
|
|
145
|
+
// Known limitation (Finding 5, SKYR-4127 final review): at equal literal-
|
|
146
|
+
// prefix length, this tiebreak prefers the LONGER total pattern — which
|
|
147
|
+
// is not always the narrower one. E.g. "/v1/persons/**" (14 chars) beats
|
|
148
|
+
// "/v1/persons/*" (13 chars) even though "**" matches strictly more paths
|
|
149
|
+
// than "*". Length is a proxy for specificity, not a guarantee of it; not
|
|
150
|
+
// fixed here since it would change ranking behavior for existing configs.
|
|
151
|
+
const lenDiff = b.override.pathPattern.length - a.override.pathPattern.length;
|
|
152
|
+
if (lenDiff !== 0)
|
|
153
|
+
return lenDiff;
|
|
154
|
+
return a.index - b.index;
|
|
155
|
+
});
|
|
156
|
+
const [winner, runnerUp] = matches;
|
|
157
|
+
if (runnerUp &&
|
|
158
|
+
patternSpecificity(winner.override.pathPattern) === patternSpecificity(runnerUp.override.pathPattern) &&
|
|
159
|
+
winner.override.pathPattern.length === runnerUp.override.pathPattern.length) {
|
|
160
|
+
logger.warning("Ambiguous api.queryParamOverrides — two equally specific patterns match this path; using the first", {
|
|
161
|
+
pathname,
|
|
162
|
+
patterns: [winner.override.pathPattern, runnerUp.override.pathPattern],
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return { ...config.base, ...winner.override.values };
|
|
166
|
+
}
|
|
167
|
+
/** `env.VAR` / legacy `envs.VAR`, anchored so a literal that merely contains
|
|
168
|
+
* the prefix (`prod.env.HOST`) is not mistaken for a reference, and requiring
|
|
169
|
+
* at least one character of variable name. Case-sensitive, matching the
|
|
170
|
+
* schema's documented behavior: `ENV.VAR` is an ordinary literal. */
|
|
171
|
+
const ENV_REFERENCE = /^envs?\.(.+)$/;
|
|
172
|
+
/** Environment variables this workspace's query params defer to, in declaration
|
|
173
|
+
* order and deduplicated (SKYR-4127).
|
|
174
|
+
*
|
|
175
|
+
* These references resolve where the test RUNS, so under Testbot they resolve
|
|
176
|
+
* inside the executor container — whose environment is a fixed allowlist. A
|
|
177
|
+
* name that never reaches the container resolves to nothing, and an unresolved
|
|
178
|
+
* query param is dropped from the request silently, producing a passing test
|
|
179
|
+
* that exercised the wrong identity. The executor uses this list to forward
|
|
180
|
+
* exactly what the workspace asked for.
|
|
181
|
+
*
|
|
182
|
+
* Deliberately derived from the declarations rather than from the ambient
|
|
183
|
+
* environment: nothing is forwarded that workspace.yml did not name.
|
|
184
|
+
*
|
|
185
|
+
* Both maps are read even though `defaultQueryParams` documents itself as
|
|
186
|
+
* literal-only — that is a doc contract the core loader does not enforce, and
|
|
187
|
+
* a declaration there would otherwise reproduce the original silent failure. */
|
|
188
|
+
export function extractEnvVarNames(config) {
|
|
189
|
+
const names = new Set();
|
|
190
|
+
const collect = (values) => {
|
|
191
|
+
for (const value of Object.values(values)) {
|
|
192
|
+
const match = typeof value === "string" ? ENV_REFERENCE.exec(value) : null;
|
|
193
|
+
if (match)
|
|
194
|
+
names.add(match[1]);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
collect(config.base);
|
|
198
|
+
for (const override of config.overrides)
|
|
199
|
+
collect(override.values);
|
|
200
|
+
return [...names];
|
|
201
|
+
}
|
|
@@ -19,6 +19,34 @@ export declare const serviceSchema: z.ZodObject<{
|
|
|
19
19
|
* Explicit per-request queryParams always win on a key collision
|
|
20
20
|
* (SKYR-4050). */
|
|
21
21
|
defaultQueryParams: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
22
|
+
/** Path-scoped overrides layered on top of defaultQueryParams
|
|
23
|
+
* (SKYR-4127). For a given request, the single most-specific matching
|
|
24
|
+
* pathPattern wins and its values override the base map key by key;
|
|
25
|
+
* overrides never merge with each other. Patterns are picomatch globs
|
|
26
|
+
* matched against the request URL pathname, with OpenAPI-style
|
|
27
|
+
* `{param}` placeholders treated as a single-segment wildcard.
|
|
28
|
+
*
|
|
29
|
+
* The pathname includes any base-path prefix carried by baseUrl — a
|
|
30
|
+
* service at `https://host/api` needs `/api/v1/**`, not `/v1/**`. A
|
|
31
|
+
* pattern that omits the prefix silently matches nothing.
|
|
32
|
+
*
|
|
33
|
+
* Values may use `env.VAR` to defer to an environment variable read
|
|
34
|
+
* where the test runs rather than where it was generated — the
|
|
35
|
+
* generated test carries the lookup itself (os.getenv in Python,
|
|
36
|
+
* process.env in JS/TS, System.getenv in Java). The legacy `envs.`
|
|
37
|
+
* spelling is still accepted. Note the variable name is passed
|
|
38
|
+
* through verbatim and is case-sensitive; a near miss such as
|
|
39
|
+
* `ENV.VAR` is treated as an ordinary literal. */
|
|
40
|
+
queryParamOverrides: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
41
|
+
pathPattern: z.ZodString;
|
|
42
|
+
values: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
43
|
+
}, "strict", z.ZodTypeAny, {
|
|
44
|
+
values: Record<string, string>;
|
|
45
|
+
pathPattern: string;
|
|
46
|
+
}, {
|
|
47
|
+
values: Record<string, string>;
|
|
48
|
+
pathPattern: string;
|
|
49
|
+
}>, "many">>;
|
|
22
50
|
baseUrl: z.ZodOptional<z.ZodString>;
|
|
23
51
|
/** SUT serves self-signed TLS (e.g. a kind kube-apiserver) — generated
|
|
24
52
|
* tests should skip certificate verification for this service (SKYR-3961).
|
|
@@ -30,6 +58,10 @@ export declare const serviceSchema: z.ZodObject<{
|
|
|
30
58
|
schemaPath?: string | undefined;
|
|
31
59
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
32
60
|
defaultQueryParams?: Record<string, string> | undefined;
|
|
61
|
+
queryParamOverrides?: {
|
|
62
|
+
values: Record<string, string>;
|
|
63
|
+
pathPattern: string;
|
|
64
|
+
}[] | undefined;
|
|
33
65
|
baseUrl?: string | undefined;
|
|
34
66
|
skipTLSVerify?: boolean | undefined;
|
|
35
67
|
}, {
|
|
@@ -38,6 +70,10 @@ export declare const serviceSchema: z.ZodObject<{
|
|
|
38
70
|
schemaPath?: string | undefined;
|
|
39
71
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
40
72
|
defaultQueryParams?: Record<string, string> | undefined;
|
|
73
|
+
queryParamOverrides?: {
|
|
74
|
+
values: Record<string, string>;
|
|
75
|
+
pathPattern: string;
|
|
76
|
+
}[] | undefined;
|
|
41
77
|
baseUrl?: string | undefined;
|
|
42
78
|
skipTLSVerify?: boolean | undefined;
|
|
43
79
|
}>>;
|
|
@@ -114,6 +150,10 @@ export declare const serviceSchema: z.ZodObject<{
|
|
|
114
150
|
schemaPath?: string | undefined;
|
|
115
151
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
116
152
|
defaultQueryParams?: Record<string, string> | undefined;
|
|
153
|
+
queryParamOverrides?: {
|
|
154
|
+
values: Record<string, string>;
|
|
155
|
+
pathPattern: string;
|
|
156
|
+
}[] | undefined;
|
|
117
157
|
baseUrl?: string | undefined;
|
|
118
158
|
skipTLSVerify?: boolean | undefined;
|
|
119
159
|
} | undefined;
|
|
@@ -145,6 +185,10 @@ export declare const serviceSchema: z.ZodObject<{
|
|
|
145
185
|
schemaPath?: string | undefined;
|
|
146
186
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
147
187
|
defaultQueryParams?: Record<string, string> | undefined;
|
|
188
|
+
queryParamOverrides?: {
|
|
189
|
+
values: Record<string, string>;
|
|
190
|
+
pathPattern: string;
|
|
191
|
+
}[] | undefined;
|
|
148
192
|
baseUrl?: string | undefined;
|
|
149
193
|
skipTLSVerify?: boolean | undefined;
|
|
150
194
|
} | undefined;
|
|
@@ -212,6 +256,34 @@ export declare const workspaceConfigSchema: z.ZodObject<{
|
|
|
212
256
|
* Explicit per-request queryParams always win on a key collision
|
|
213
257
|
* (SKYR-4050). */
|
|
214
258
|
defaultQueryParams: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
259
|
+
/** Path-scoped overrides layered on top of defaultQueryParams
|
|
260
|
+
* (SKYR-4127). For a given request, the single most-specific matching
|
|
261
|
+
* pathPattern wins and its values override the base map key by key;
|
|
262
|
+
* overrides never merge with each other. Patterns are picomatch globs
|
|
263
|
+
* matched against the request URL pathname, with OpenAPI-style
|
|
264
|
+
* `{param}` placeholders treated as a single-segment wildcard.
|
|
265
|
+
*
|
|
266
|
+
* The pathname includes any base-path prefix carried by baseUrl — a
|
|
267
|
+
* service at `https://host/api` needs `/api/v1/**`, not `/v1/**`. A
|
|
268
|
+
* pattern that omits the prefix silently matches nothing.
|
|
269
|
+
*
|
|
270
|
+
* Values may use `env.VAR` to defer to an environment variable read
|
|
271
|
+
* where the test runs rather than where it was generated — the
|
|
272
|
+
* generated test carries the lookup itself (os.getenv in Python,
|
|
273
|
+
* process.env in JS/TS, System.getenv in Java). The legacy `envs.`
|
|
274
|
+
* spelling is still accepted. Note the variable name is passed
|
|
275
|
+
* through verbatim and is case-sensitive; a near miss such as
|
|
276
|
+
* `ENV.VAR` is treated as an ordinary literal. */
|
|
277
|
+
queryParamOverrides: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
278
|
+
pathPattern: z.ZodString;
|
|
279
|
+
values: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
280
|
+
}, "strict", z.ZodTypeAny, {
|
|
281
|
+
values: Record<string, string>;
|
|
282
|
+
pathPattern: string;
|
|
283
|
+
}, {
|
|
284
|
+
values: Record<string, string>;
|
|
285
|
+
pathPattern: string;
|
|
286
|
+
}>, "many">>;
|
|
215
287
|
baseUrl: z.ZodOptional<z.ZodString>;
|
|
216
288
|
/** SUT serves self-signed TLS (e.g. a kind kube-apiserver) — generated
|
|
217
289
|
* tests should skip certificate verification for this service (SKYR-3961).
|
|
@@ -223,6 +295,10 @@ export declare const workspaceConfigSchema: z.ZodObject<{
|
|
|
223
295
|
schemaPath?: string | undefined;
|
|
224
296
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
225
297
|
defaultQueryParams?: Record<string, string> | undefined;
|
|
298
|
+
queryParamOverrides?: {
|
|
299
|
+
values: Record<string, string>;
|
|
300
|
+
pathPattern: string;
|
|
301
|
+
}[] | undefined;
|
|
226
302
|
baseUrl?: string | undefined;
|
|
227
303
|
skipTLSVerify?: boolean | undefined;
|
|
228
304
|
}, {
|
|
@@ -231,6 +307,10 @@ export declare const workspaceConfigSchema: z.ZodObject<{
|
|
|
231
307
|
schemaPath?: string | undefined;
|
|
232
308
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
233
309
|
defaultQueryParams?: Record<string, string> | undefined;
|
|
310
|
+
queryParamOverrides?: {
|
|
311
|
+
values: Record<string, string>;
|
|
312
|
+
pathPattern: string;
|
|
313
|
+
}[] | undefined;
|
|
234
314
|
baseUrl?: string | undefined;
|
|
235
315
|
skipTLSVerify?: boolean | undefined;
|
|
236
316
|
}>>;
|
|
@@ -307,6 +387,10 @@ export declare const workspaceConfigSchema: z.ZodObject<{
|
|
|
307
387
|
schemaPath?: string | undefined;
|
|
308
388
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
309
389
|
defaultQueryParams?: Record<string, string> | undefined;
|
|
390
|
+
queryParamOverrides?: {
|
|
391
|
+
values: Record<string, string>;
|
|
392
|
+
pathPattern: string;
|
|
393
|
+
}[] | undefined;
|
|
310
394
|
baseUrl?: string | undefined;
|
|
311
395
|
skipTLSVerify?: boolean | undefined;
|
|
312
396
|
} | undefined;
|
|
@@ -338,6 +422,10 @@ export declare const workspaceConfigSchema: z.ZodObject<{
|
|
|
338
422
|
schemaPath?: string | undefined;
|
|
339
423
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
340
424
|
defaultQueryParams?: Record<string, string> | undefined;
|
|
425
|
+
queryParamOverrides?: {
|
|
426
|
+
values: Record<string, string>;
|
|
427
|
+
pathPattern: string;
|
|
428
|
+
}[] | undefined;
|
|
341
429
|
baseUrl?: string | undefined;
|
|
342
430
|
skipTLSVerify?: boolean | undefined;
|
|
343
431
|
} | undefined;
|
|
@@ -382,6 +470,10 @@ export declare const workspaceConfigSchema: z.ZodObject<{
|
|
|
382
470
|
schemaPath?: string | undefined;
|
|
383
471
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
384
472
|
defaultQueryParams?: Record<string, string> | undefined;
|
|
473
|
+
queryParamOverrides?: {
|
|
474
|
+
values: Record<string, string>;
|
|
475
|
+
pathPattern: string;
|
|
476
|
+
}[] | undefined;
|
|
385
477
|
baseUrl?: string | undefined;
|
|
386
478
|
skipTLSVerify?: boolean | undefined;
|
|
387
479
|
} | undefined;
|
|
@@ -426,6 +518,10 @@ export declare const workspaceConfigSchema: z.ZodObject<{
|
|
|
426
518
|
schemaPath?: string | undefined;
|
|
427
519
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
428
520
|
defaultQueryParams?: Record<string, string> | undefined;
|
|
521
|
+
queryParamOverrides?: {
|
|
522
|
+
values: Record<string, string>;
|
|
523
|
+
pathPattern: string;
|
|
524
|
+
}[] | undefined;
|
|
429
525
|
baseUrl?: string | undefined;
|
|
430
526
|
skipTLSVerify?: boolean | undefined;
|
|
431
527
|
} | undefined;
|
|
@@ -479,6 +575,10 @@ export declare function validateWorkspaceConfig(config: unknown): z.SafeParseRet
|
|
|
479
575
|
schemaPath?: string | undefined;
|
|
480
576
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
481
577
|
defaultQueryParams?: Record<string, string> | undefined;
|
|
578
|
+
queryParamOverrides?: {
|
|
579
|
+
values: Record<string, string>;
|
|
580
|
+
pathPattern: string;
|
|
581
|
+
}[] | undefined;
|
|
482
582
|
baseUrl?: string | undefined;
|
|
483
583
|
skipTLSVerify?: boolean | undefined;
|
|
484
584
|
} | undefined;
|
|
@@ -523,6 +623,10 @@ export declare function validateWorkspaceConfig(config: unknown): z.SafeParseRet
|
|
|
523
623
|
schemaPath?: string | undefined;
|
|
524
624
|
authType?: "none" | "token" | "bearer" | "basic" | "oauth" | "apiKey" | "cookie" | "session" | undefined;
|
|
525
625
|
defaultQueryParams?: Record<string, string> | undefined;
|
|
626
|
+
queryParamOverrides?: {
|
|
627
|
+
values: Record<string, string>;
|
|
628
|
+
pathPattern: string;
|
|
629
|
+
}[] | undefined;
|
|
526
630
|
baseUrl?: string | undefined;
|
|
527
631
|
skipTLSVerify?: boolean | undefined;
|
|
528
632
|
} | undefined;
|
|
@@ -61,6 +61,30 @@ export const serviceSchema = z
|
|
|
61
61
|
* Explicit per-request queryParams always win on a key collision
|
|
62
62
|
* (SKYR-4050). */
|
|
63
63
|
defaultQueryParams: z.record(z.string()).optional(),
|
|
64
|
+
/** Path-scoped overrides layered on top of defaultQueryParams
|
|
65
|
+
* (SKYR-4127). For a given request, the single most-specific matching
|
|
66
|
+
* pathPattern wins and its values override the base map key by key;
|
|
67
|
+
* overrides never merge with each other. Patterns are picomatch globs
|
|
68
|
+
* matched against the request URL pathname, with OpenAPI-style
|
|
69
|
+
* `{param}` placeholders treated as a single-segment wildcard.
|
|
70
|
+
*
|
|
71
|
+
* The pathname includes any base-path prefix carried by baseUrl — a
|
|
72
|
+
* service at `https://host/api` needs `/api/v1/**`, not `/v1/**`. A
|
|
73
|
+
* pattern that omits the prefix silently matches nothing.
|
|
74
|
+
*
|
|
75
|
+
* Values may use `env.VAR` to defer to an environment variable read
|
|
76
|
+
* where the test runs rather than where it was generated — the
|
|
77
|
+
* generated test carries the lookup itself (os.getenv in Python,
|
|
78
|
+
* process.env in JS/TS, System.getenv in Java). The legacy `envs.`
|
|
79
|
+
* spelling is still accepted. Note the variable name is passed
|
|
80
|
+
* through verbatim and is case-sensitive; a near miss such as
|
|
81
|
+
* `ENV.VAR` is treated as an ordinary literal. */
|
|
82
|
+
queryParamOverrides: z
|
|
83
|
+
.array(z.object({
|
|
84
|
+
pathPattern: z.string().min(1),
|
|
85
|
+
values: z.record(z.string()),
|
|
86
|
+
}).strict())
|
|
87
|
+
.optional(),
|
|
64
88
|
baseUrl: z.string().optional(),
|
|
65
89
|
/** SUT serves self-signed TLS (e.g. a kind kube-apiserver) — generated
|
|
66
90
|
* tests should skip certificate verification for this service (SKYR-3961).
|