@jterrazz/test 16.1.1 → 18.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -171
- package/dist/browser/index.js +1 -2980
- package/dist/browser/services.js +1 -0
- package/dist/browser/stubs.js +1 -0
- package/dist/browser-shared-index.browser.js +3233 -0
- package/dist/checker.d.ts +6 -1
- package/dist/checker.js +3721 -2624
- package/dist/config.d.ts +40 -0
- package/dist/config.js +309 -0
- package/dist/in-place.global-setup.d.ts +12 -0
- package/dist/in-place.global-setup.js +21 -0
- package/dist/index.d.ts +1641 -3004
- package/dist/index.js +5095 -3835
- package/dist/mobile-session.global-setup.d.ts +15 -0
- package/dist/mobile-session.global-setup.js +31 -0
- package/dist/oxlint.cjs +6069 -5634
- package/dist/oxlint.d.cts +43 -7212
- package/dist/oxlint.d.ts +43 -7212
- package/dist/oxlint.js +3979 -5799
- package/dist/route-coverage.global-setup.d.ts +43 -0
- package/dist/route-coverage.global-setup.js +1 -0
- package/dist/services.d.ts +3 -0
- package/dist/services.global-setup.d.ts +43 -0
- package/dist/services.global-setup.js +1 -0
- package/dist/services.js +597 -0
- package/dist/shared-appium.adapter.js +428 -0
- package/dist/shared-artifacts.js +34 -0
- package/dist/shared-chapter.js +7 -0
- package/dist/shared-container.port.d.ts +31 -0
- package/dist/shared-derived-projects.js +73 -0
- package/dist/shared-device.port.d.ts +189 -0
- package/dist/shared-exchange.js +447 -0
- package/dist/shared-in-place.js +172 -0
- package/dist/shared-index.services.d.ts +346 -0
- package/dist/shared-index.stubs.d.ts +287 -0
- package/dist/shared-json-shape.js +17 -0
- package/dist/shared-landmarks.js +71 -0
- package/dist/shared-ledger.js +68 -0
- package/dist/shared-listened-port.js +19 -0
- package/dist/shared-literate-plugin.js +184 -0
- package/dist/shared-manifest.js +1321 -0
- package/dist/shared-pause.js +22 -0
- package/dist/shared-peer.js +88 -0
- package/dist/shared-playwright.adapter.js +328 -0
- package/dist/shared-process-alive.js +13 -0
- package/dist/shared-process.adapter.js +74 -0
- package/dist/shared-record-key.js +13 -0
- package/dist/shared-refusal.js +35 -0
- package/dist/shared-resolve.js +24 -0
- package/dist/shared-route-coverage.global-setup.js +187 -0
- package/dist/shared-scenario-failure.js +198 -0
- package/dist/shared-serve.js +407 -0
- package/dist/shared-service-images.js +10 -0
- package/dist/shared-services.global-setup.js +174 -0
- package/dist/shared-session-ledger.js +305 -0
- package/dist/shared-setup-entry.js +12 -0
- package/dist/shared-source-scan.js +133 -0
- package/dist/shared-spec-document.js +303 -0
- package/dist/shared-spec-grammar.js +66 -0
- package/dist/shared-specifications.js +26 -0
- package/dist/shared-specs-modules.js +1033 -0
- package/dist/shared-stub-reply.js +167 -0
- package/dist/shared-stub-server.js +142 -0
- package/dist/shared-temp-root.global-setup.js +73 -0
- package/dist/shared-test-baseline.js +26 -0
- package/dist/shared-test-section.js +58 -0
- package/dist/shared-testcontainers.adapter.js +306 -0
- package/dist/shared-tokens.js +55 -0
- package/dist/shared-yaml-document.js +119 -0
- package/dist/stubs.d.ts +2 -0
- package/dist/stubs.js +657 -0
- package/dist/temp-root.global-setup.d.ts +7 -0
- package/dist/temp-root.global-setup.js +1 -0
- package/dist/vitest.d.ts +93 -164
- package/dist/vitest.js +382 -550
- package/docs/README.md +82 -36
- package/docs/concepts/architecture.md +118 -0
- package/docs/concepts/conventions.md +333 -0
- package/docs/guides/api-specs.md +374 -0
- package/docs/guides/cli-specs.md +566 -0
- package/docs/guides/component-specs.md +252 -0
- package/docs/guides/develop.md +97 -0
- package/docs/guides/integration-specs.md +181 -0
- package/docs/guides/job-specs.md +180 -0
- package/docs/guides/lint-specs.md +174 -0
- package/docs/guides/mobile-specs.md +319 -0
- package/docs/guides/module-tests.md +149 -0
- package/docs/guides/release.md +44 -0
- package/docs/guides/spec-documents.md +189 -0
- package/docs/guides/website-specs.md +413 -0
- package/docs/reference/assertions.md +345 -0
- package/docs/reference/glossary.md +20 -0
- package/docs/reference/linting.md +483 -0
- package/docs/reference/services.md +236 -0
- package/docs/reference/stubs.md +500 -0
- package/docs/reference/time-and-doubles.md +137 -0
- package/docs/reference/tokens.md +258 -0
- package/docs/reference/visitor.md +221 -0
- package/docs/start/getting-started.md +187 -0
- package/docs/start/set-up-a-project.md +228 -0
- package/package.json +56 -36
- package/schema/spec.schema.json +43 -17
- package/dist/appium.adapter.js +0 -489
- package/dist/artifacts.js +0 -32
- package/dist/fixtures.js +0 -110
- package/dist/literate.js +0 -1747
- package/dist/match.js +0 -804
- package/dist/playwright.adapter.js +0 -438
- package/dist/server.js +0 -160
- package/dist/stream.js +0 -321
- package/dist/substring-warning.js +0 -282
- package/docs/01-architecture.md +0 -95
- package/docs/02-developing.md +0 -351
- package/docs/03-testing.md +0 -495
- package/docs/04-operating.md +0 -101
- package/docs/05-module-tests.md +0 -152
- package/docs/06-integration.md +0 -180
- package/docs/07-component.md +0 -246
- package/docs/08-website.md +0 -404
- package/docs/09-mobile.md +0 -270
- package/docs/10-api.md +0 -264
- package/docs/11-jobs.md +0 -157
- package/docs/12-cli.md +0 -677
- package/docs/13-elements.md +0 -248
- package/docs/14-assertions.md +0 -314
- package/docs/15-tokens.md +0 -253
- package/docs/16-contracts.md +0 -438
- package/docs/17-services.md +0 -188
- package/docs/18-conventions.md +0 -332
- package/docs/19-linting.md +0 -512
- package/docs/decisions/001-v9-conventions-on-three-channels.md +0 -50
- package/docs/decisions/002-the-statique-channel-holds-only-what-upstream-does-not.md +0 -49
- package/docs/decisions/003-a-rendered-component-is-a-facet-on-vitest-browser-mode.md +0 -84
- package/docs/decisions/004-a-module-against-real-services-or-a-golden-is-the-integration-facet.md +0 -81
- package/docs/decisions/005-the-doubles-ladder-is-closed.md +0 -86
- package/docs/decisions/006-the-catalogue-reaches-a-file-by-its-role.md +0 -245
- package/docs/decisions/007-compose-mode-leaves-the-package.md +0 -53
- package/docs/decisions/008-vitest-5-node-24-and-the-native-seams-as-optional-peers.md +0 -75
- package/docs/decisions/_template.md +0 -22
- package/docs/reference/classes/Matcher.md +0 -120
- package/docs/reference/classes/ProcessHandle.md +0 -368
- package/docs/reference/functions/defineContract.md +0 -39
- package/docs/reference/functions/defineContracts.md +0 -36
- package/docs/reference/functions/disabled.md +0 -28
- package/docs/reference/functions/enabled.md +0 -20
- package/docs/reference/functions/focused.md +0 -26
- package/docs/reference/functions/mockOf.md +0 -79
- package/docs/reference/functions/postgres.md +0 -31
- package/docs/reference/functions/process.md +0 -33
- package/docs/reference/functions/redis.md +0 -28
- package/docs/reference/functions/required.md +0 -41
- package/docs/reference/functions/selected.md +0 -26
- package/docs/reference/functions/sqlite.md +0 -33
- package/docs/reference/functions/testId.md +0 -20
- package/docs/reference/functions/text.md +0 -40
- package/docs/reference/functions/valued.md +0 -28
- package/docs/reference/functions/waitUntil.md +0 -46
- package/docs/reference/functions/within.md +0 -29
- package/docs/reference/index.md +0 -196
- package/docs/reference/interfaces/BaseResult.md +0 -87
- package/docs/reference/interfaces/CallResult.md +0 -160
- package/docs/reference/interfaces/CaptureScope.md +0 -82
- package/docs/reference/interfaces/CliResult.md +0 -253
- package/docs/reference/interfaces/ContainerAccessor.md +0 -166
- package/docs/reference/interfaces/DirectoryAccessor.md +0 -33
- package/docs/reference/interfaces/FetchResult.md +0 -180
- package/docs/reference/interfaces/FilesystemAccessor.md +0 -46
- package/docs/reference/interfaces/HttpResult.md +0 -126
- package/docs/reference/interfaces/JsonAccessor.md +0 -46
- package/docs/reference/interfaces/PageResult.md +0 -356
- package/docs/reference/interfaces/RenderResult.md +0 -72
- package/docs/reference/interfaces/RenderedText.md +0 -110
- package/docs/reference/interfaces/ResponseAccessor.md +0 -47
- package/docs/reference/interfaces/ScreenResult.md +0 -128
- package/docs/reference/interfaces/TableAccessor.md +0 -26
- package/docs/reference/interfaces/TextAccessor.md +0 -96
- package/docs/reference/type-aliases/ApiHandle.md +0 -68
- package/docs/reference/type-aliases/ApiSpecification.md +0 -213
- package/docs/reference/type-aliases/ApiSpecificationOptions.md +0 -68
- package/docs/reference/type-aliases/BrowserConsoleMessage.md +0 -30
- package/docs/reference/type-aliases/BrowserLinkElement.md +0 -50
- package/docs/reference/type-aliases/BrowserMetaElement.md +0 -40
- package/docs/reference/type-aliases/BrowserOpenOptions.md +0 -87
- package/docs/reference/type-aliases/BrowserPage.md +0 -132
- package/docs/reference/type-aliases/BrowserPort.md +0 -51
- package/docs/reference/type-aliases/CliEnv.md +0 -11
- package/docs/reference/type-aliases/CliHandle.md +0 -80
- package/docs/reference/type-aliases/CliInput.md +0 -36
- package/docs/reference/type-aliases/CliOutput.md +0 -46
- package/docs/reference/type-aliases/CliPort.md +0 -58
- package/docs/reference/type-aliases/CliSpecification.md +0 -144
- package/docs/reference/type-aliases/CliSpecificationOptions.md +0 -134
- package/docs/reference/type-aliases/ComponentChain.md +0 -156
- package/docs/reference/type-aliases/ComponentScenario.md +0 -20
- package/docs/reference/type-aliases/ComponentUi.md +0 -47
- package/docs/reference/type-aliases/ComponentVisitor.md +0 -10
- package/docs/reference/type-aliases/ContainerPort.md +0 -113
- package/docs/reference/type-aliases/Contract.md +0 -68
- package/docs/reference/type-aliases/ContractInput.md +0 -13
- package/docs/reference/type-aliases/ContractRequest.md +0 -95
- package/docs/reference/type-aliases/ContractResponder.md +0 -23
- package/docs/reference/type-aliases/ContractResponse.md +0 -78
- package/docs/reference/type-aliases/ContractResponseValue.md +0 -13
- package/docs/reference/type-aliases/Contracts.md +0 -48
- package/docs/reference/type-aliases/DatabaseKeys.md +0 -16
- package/docs/reference/type-aliases/DatabasePort.md +0 -72
- package/docs/reference/type-aliases/DeviceOpenOptions.md +0 -46
- package/docs/reference/type-aliases/DevicePort.md +0 -51
- package/docs/reference/type-aliases/DeviceScreen.md +0 -32
- package/docs/reference/type-aliases/DeviceTimeouts.md +0 -39
- package/docs/reference/type-aliases/DockerSpecConfig.md +0 -43
- package/docs/reference/type-aliases/DomMount.md +0 -21
- package/docs/reference/type-aliases/ElementKind.md +0 -27
- package/docs/reference/type-aliases/ElementMatch.md +0 -74
- package/docs/reference/type-aliases/ElementOptions.md +0 -29
- package/docs/reference/type-aliases/ElementRef.md +0 -128
- package/docs/reference/type-aliases/ExecOptions.md +0 -37
- package/docs/reference/type-aliases/FileAccessor.md +0 -54
- package/docs/reference/type-aliases/HonoApp.md +0 -31
- package/docs/reference/type-aliases/HttpContractFilter.md +0 -49
- package/docs/reference/type-aliases/HttpResponseInit.md +0 -46
- package/docs/reference/type-aliases/HttpStreamInit.md +0 -58
- package/docs/reference/type-aliases/IntegrationHandle.md +0 -46
- package/docs/reference/type-aliases/IntegrationSpecification.md +0 -111
- package/docs/reference/type-aliases/IntegrationSpecificationOptions.md +0 -42
- package/docs/reference/type-aliases/Intercept.md +0 -19
- package/docs/reference/type-aliases/InterceptOptions.md +0 -28
- package/docs/reference/type-aliases/InterceptScope.md +0 -11
- package/docs/reference/type-aliases/IsolationStrategy.md +0 -75
- package/docs/reference/type-aliases/JobHandle.md +0 -34
- package/docs/reference/type-aliases/JobsHandle.md +0 -57
- package/docs/reference/type-aliases/JobsResult.md +0 -16
- package/docs/reference/type-aliases/JobsSpecification.md +0 -94
- package/docs/reference/type-aliases/JobsSpecificationOptions.md +0 -56
- package/docs/reference/type-aliases/LandmarkKind.md +0 -20
- package/docs/reference/type-aliases/LiterateRunFlags.md +0 -27
- package/docs/reference/type-aliases/LiterateServeRegistration.md +0 -70
- package/docs/reference/type-aliases/MatchFixtureOptions.md +0 -27
- package/docs/reference/type-aliases/MatchableRequest.md +0 -61
- package/docs/reference/type-aliases/MatcherKind.md +0 -34
- package/docs/reference/type-aliases/MobileBackendOptions.md +0 -28
- package/docs/reference/type-aliases/MobileElementKind.md +0 -14
- package/docs/reference/type-aliases/MobileElementMatch.md +0 -60
- package/docs/reference/type-aliases/MobileElementRef.md +0 -20
- package/docs/reference/type-aliases/MobileHandle.md +0 -65
- package/docs/reference/type-aliases/MobileScenario.md +0 -20
- package/docs/reference/type-aliases/MobileSpecification.md +0 -51
- package/docs/reference/type-aliases/MobileSpecificationOptions.md +0 -126
- package/docs/reference/type-aliases/MobileVisitor.md +0 -81
- package/docs/reference/type-aliases/MockOfOptions.md +0 -26
- package/docs/reference/type-aliases/MockPort.md +0 -10
- package/docs/reference/type-aliases/PinnedClock.md +0 -10
- package/docs/reference/type-aliases/PostgresOptions.md +0 -32
- package/docs/reference/type-aliases/ProcessOptions.md +0 -112
- package/docs/reference/type-aliases/RedisOptions.md +0 -20
- package/docs/reference/type-aliases/RenderSubject.md +0 -12
- package/docs/reference/type-aliases/ScreenNode.md +0 -67
- package/docs/reference/type-aliases/ServerPort.md +0 -36
- package/docs/reference/type-aliases/ServerResponse.md +0 -46
- package/docs/reference/type-aliases/ServerSpec.md +0 -21
- package/docs/reference/type-aliases/ServiceHandle.md +0 -216
- package/docs/reference/type-aliases/ServiceRecord.md +0 -12
- package/docs/reference/type-aliases/SpecDocument.md +0 -84
- package/docs/reference/type-aliases/SpecEnvToken.md +0 -21
- package/docs/reference/type-aliases/SpecFileAssertion.md +0 -27
- package/docs/reference/type-aliases/SpecFixture.md +0 -30
- package/docs/reference/type-aliases/SpecKind.md +0 -8
- package/docs/reference/type-aliases/SpecRun.md +0 -108
- package/docs/reference/type-aliases/SpecServeEntry.md +0 -40
- package/docs/reference/type-aliases/SpecStream.md +0 -32
- package/docs/reference/type-aliases/SpecificationConfig.md +0 -290
- package/docs/reference/type-aliases/SqliteOptions.md +0 -36
- package/docs/reference/type-aliases/SseEvent.md +0 -58
- package/docs/reference/type-aliases/StreamBody.md +0 -61
- package/docs/reference/type-aliases/TextFilter.md +0 -22
- package/docs/reference/type-aliases/VisitScenario.md +0 -20
- package/docs/reference/type-aliases/Visitor.md +0 -215
- package/docs/reference/type-aliases/WaitUntilOptions.md +0 -46
- package/docs/reference/type-aliases/WebsiteBackendOptions.md +0 -37
- package/docs/reference/type-aliases/WebsiteHandle.md +0 -51
- package/docs/reference/type-aliases/WebsiteSpecification.md +0 -118
- package/docs/reference/type-aliases/WebsiteSpecificationOptions.md +0 -40
- package/docs/reference/variables/anthropic.md +0 -19
- package/docs/reference/variables/banner.md +0 -10
- package/docs/reference/variables/button.md +0 -10
- package/docs/reference/variables/clock.md +0 -16
- package/docs/reference/variables/complementary.md +0 -10
- package/docs/reference/variables/component.md +0 -10
- package/docs/reference/variables/content.md +0 -10
- package/docs/reference/variables/contentinfo.md +0 -10
- package/docs/reference/variables/dialog.md +0 -10
- package/docs/reference/variables/field.md +0 -10
- package/docs/reference/variables/form.md +0 -10
- package/docs/reference/variables/heading.md +0 -10
- package/docs/reference/variables/http.md +0 -40
- package/docs/reference/variables/intercept.md +0 -11
- package/docs/reference/variables/link.md +0 -10
- package/docs/reference/variables/listitem.md +0 -10
- package/docs/reference/variables/main.md +0 -10
- package/docs/reference/variables/match.md +0 -8
- package/docs/reference/variables/navigation.md +0 -10
- package/docs/reference/variables/openai.md +0 -21
- package/docs/reference/variables/option.md +0 -17
- package/docs/reference/variables/region.md +0 -10
- package/docs/reference/variables/row.md +0 -10
- package/docs/reference/variables/search.md +0 -10
- package/docs/reference/variables/specification.md +0 -8
- package/docs/reference/variables/status.md +0 -10
- package/docs/reference/variables/table.md +0 -10
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { t as isRecord } from "./shared-json-shape.js";
|
|
2
|
+
import { n as refusal, t as invariant } from "./shared-refusal.js";
|
|
3
|
+
import { t as listenedPort } from "./shared-listened-port.js";
|
|
4
|
+
import { t as loadPeer } from "./shared-peer.js";
|
|
5
|
+
import { execSync } from "node:child_process";
|
|
6
|
+
import { once } from "node:events";
|
|
7
|
+
import { createServer } from "node:http";
|
|
8
|
+
import { text } from "node:stream/consumers";
|
|
9
|
+
import { hostname } from "node:os";
|
|
10
|
+
//#region src/services/containers/docker-cli.ts
|
|
11
|
+
const DEFAULT_TIMEOUT = 1e4;
|
|
12
|
+
/** How every refusal of a run that asked for Docker names a daemon that did not answer. */
|
|
13
|
+
const DOCKER_ABSENT = "no Docker daemon answered";
|
|
14
|
+
/** Docker-aware mode reads what the daemon answers: a run where none answers is refused, never passed. */
|
|
15
|
+
function requireDockerDaemon() {
|
|
16
|
+
try {
|
|
17
|
+
execSync("docker version --format \"{{.Server.Version}}\"", {
|
|
18
|
+
stdio: "ignore",
|
|
19
|
+
timeout: DEFAULT_TIMEOUT
|
|
20
|
+
});
|
|
21
|
+
} catch (error) {
|
|
22
|
+
throw new Error(refusal({
|
|
23
|
+
fix: "start Docker (Docker Desktop, OrbStack, colima or another) and run again",
|
|
24
|
+
found: `specification.cli({ docker }) asked for Docker, and ${DOCKER_ABSENT}`,
|
|
25
|
+
read: "docs/guides/cli-specs.md#docker-aware-mode",
|
|
26
|
+
why: "Docker-aware mode reads the containers a run spawned, so it needs a Docker daemon."
|
|
27
|
+
}), { cause: error });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function findContainersByLabel(key, value) {
|
|
31
|
+
try {
|
|
32
|
+
return execSync(`docker ps -aq -f label=${key}=${value}`, {
|
|
33
|
+
encoding: "utf8",
|
|
34
|
+
timeout: DEFAULT_TIMEOUT
|
|
35
|
+
}).split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
36
|
+
} catch {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** A field of a value read off docker's JSON or a failed process: absent wherever the value does not hold it. */
|
|
41
|
+
function fieldOf(value, ...keys) {
|
|
42
|
+
if (typeof value !== "object" || value === null) return;
|
|
43
|
+
const key = keys.find((candidate) => candidate in value);
|
|
44
|
+
return key === void 0 ? void 0 : Reflect.get(value, key);
|
|
45
|
+
}
|
|
46
|
+
const streamOf = (error, key) => {
|
|
47
|
+
const stream = fieldOf(error, key);
|
|
48
|
+
return typeof stream === "string" ? stream : void 0;
|
|
49
|
+
};
|
|
50
|
+
function inspectContainer(id) {
|
|
51
|
+
const raw = execSync(`docker inspect ${id}`, {
|
|
52
|
+
encoding: "utf8",
|
|
53
|
+
timeout: DEFAULT_TIMEOUT
|
|
54
|
+
});
|
|
55
|
+
const parsed = JSON.parse(raw);
|
|
56
|
+
if (!Array.isArray(parsed)) return parsed;
|
|
57
|
+
return parsed[0];
|
|
58
|
+
}
|
|
59
|
+
function removeContainers(ids) {
|
|
60
|
+
if (ids.length === 0) return;
|
|
61
|
+
try {
|
|
62
|
+
execSync(`docker rm -f ${ids.join(" ")}`, {
|
|
63
|
+
encoding: "utf8",
|
|
64
|
+
stdio: "pipe",
|
|
65
|
+
timeout: DEFAULT_TIMEOUT
|
|
66
|
+
});
|
|
67
|
+
} catch {}
|
|
68
|
+
}
|
|
69
|
+
function containerLogs(id) {
|
|
70
|
+
try {
|
|
71
|
+
return execSync(`docker logs ${id}`, {
|
|
72
|
+
encoding: "utf8",
|
|
73
|
+
stdio: [
|
|
74
|
+
"ignore",
|
|
75
|
+
"pipe",
|
|
76
|
+
"pipe"
|
|
77
|
+
],
|
|
78
|
+
timeout: DEFAULT_TIMEOUT
|
|
79
|
+
});
|
|
80
|
+
} catch (error) {
|
|
81
|
+
return streamOf(error, "stdout") ?? streamOf(error, "stderr") ?? "";
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function readContainerFile(id, path) {
|
|
85
|
+
return execSync(`docker exec ${id} cat ${JSON.stringify(path)}`, {
|
|
86
|
+
encoding: "utf8",
|
|
87
|
+
timeout: DEFAULT_TIMEOUT
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function execInContainer(id, argv) {
|
|
91
|
+
try {
|
|
92
|
+
const quoted = argv.map((a) => JSON.stringify(a)).join(" ");
|
|
93
|
+
return {
|
|
94
|
+
exitCode: 0,
|
|
95
|
+
stderr: "",
|
|
96
|
+
stdout: execSync(`docker exec ${id} ${quoted}`, {
|
|
97
|
+
encoding: "utf8",
|
|
98
|
+
stdio: [
|
|
99
|
+
"ignore",
|
|
100
|
+
"pipe",
|
|
101
|
+
"pipe"
|
|
102
|
+
],
|
|
103
|
+
timeout: DEFAULT_TIMEOUT
|
|
104
|
+
})
|
|
105
|
+
};
|
|
106
|
+
} catch (error) {
|
|
107
|
+
const status = fieldOf(error, "status");
|
|
108
|
+
return {
|
|
109
|
+
exitCode: typeof status === "number" ? status : 1,
|
|
110
|
+
stderr: streamOf(error, "stderr") ?? (error instanceof Error ? error.message : ""),
|
|
111
|
+
stdout: streamOf(error, "stdout") ?? ""
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/services/containers/run-containers.ts
|
|
117
|
+
function canonical(value) {
|
|
118
|
+
return isRecord(value) ? Object.fromEntries(Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b)).map(([key, entry]) => [key, canonical(entry)])) : value;
|
|
119
|
+
}
|
|
120
|
+
function keyOf(request) {
|
|
121
|
+
return JSON.stringify(canonical(request));
|
|
122
|
+
}
|
|
123
|
+
var RunContainers = class {
|
|
124
|
+
started = /* @__PURE__ */ new Map();
|
|
125
|
+
create;
|
|
126
|
+
ready;
|
|
127
|
+
/** `ready`'s rejection is the one answer every request gets, and no container is created. */
|
|
128
|
+
constructor(create, ready = async () => {}) {
|
|
129
|
+
this.create = create;
|
|
130
|
+
this.ready = ready;
|
|
131
|
+
}
|
|
132
|
+
async acquire(request) {
|
|
133
|
+
const key = keyOf(request);
|
|
134
|
+
let pending = this.started.get(key);
|
|
135
|
+
if (pending === void 0) {
|
|
136
|
+
pending = this.start(key, request);
|
|
137
|
+
this.started.set(key, pending);
|
|
138
|
+
}
|
|
139
|
+
const container = await pending;
|
|
140
|
+
return {
|
|
141
|
+
host: container.getHost(),
|
|
142
|
+
key,
|
|
143
|
+
port: container.getMappedPort(request.port)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
async logs(key, since) {
|
|
147
|
+
const pending = this.started.get(key);
|
|
148
|
+
if (pending === void 0) return "";
|
|
149
|
+
return await (await pending).getLogs(since);
|
|
150
|
+
}
|
|
151
|
+
async stop() {
|
|
152
|
+
const pending = [...this.started.values()];
|
|
153
|
+
this.started.clear();
|
|
154
|
+
await Promise.allSettled(pending.map(async (starting) => {
|
|
155
|
+
await (await starting).stop();
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
async start(key, request) {
|
|
159
|
+
try {
|
|
160
|
+
await this.ready();
|
|
161
|
+
const container = this.create(request);
|
|
162
|
+
await container.start();
|
|
163
|
+
return container;
|
|
164
|
+
} catch (error) {
|
|
165
|
+
this.started.delete(key);
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
function field(value, name) {
|
|
171
|
+
if (typeof value !== "object" || value === null) return;
|
|
172
|
+
return Reflect.get(value, name);
|
|
173
|
+
}
|
|
174
|
+
function isContainerRequest(value) {
|
|
175
|
+
return typeof field(value, "image") === "string" && typeof field(value, "port") === "number";
|
|
176
|
+
}
|
|
177
|
+
async function respond(containers, request, response) {
|
|
178
|
+
const url = new URL(request.url ?? "/", "http://run");
|
|
179
|
+
try {
|
|
180
|
+
if (request.method === "POST" && url.pathname === "/containers") {
|
|
181
|
+
const asked = JSON.parse(await text(request));
|
|
182
|
+
if (!isContainerRequest(asked)) throw invariant(`a worker asked for ${JSON.stringify(asked)}, no container request`);
|
|
183
|
+
const address = await containers.acquire(asked);
|
|
184
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
185
|
+
response.end(JSON.stringify(address));
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (request.method === "GET" && url.pathname === "/logs") {
|
|
189
|
+
response.writeHead(200, { "content-type": "text/plain" });
|
|
190
|
+
const key = url.searchParams.get("key") ?? "";
|
|
191
|
+
const since = url.searchParams.get("since");
|
|
192
|
+
const moment = since === null ? void 0 : new Date(Number(since));
|
|
193
|
+
response.end(await containers.logs(key, moment));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
response.writeHead(404).end();
|
|
197
|
+
} catch (error) {
|
|
198
|
+
response.writeHead(500, { "content-type": "application/json" });
|
|
199
|
+
response.end(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async function serveContainers(containers) {
|
|
203
|
+
const server = createServer((request, response) => {
|
|
204
|
+
respond(containers, request, response);
|
|
205
|
+
});
|
|
206
|
+
return {
|
|
207
|
+
close: async () => {
|
|
208
|
+
server.close();
|
|
209
|
+
await once(server, "close");
|
|
210
|
+
},
|
|
211
|
+
url: `http://127.0.0.1:${await listenedPort(server, { host: "127.0.0.1" })}`
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
//#endregion
|
|
215
|
+
//#region src/services/containers/testcontainers.adapter.ts
|
|
216
|
+
const CONTAINER_LABEL = "com.jterrazz.test";
|
|
217
|
+
const REUSED_LABEL = "com.jterrazz.test.reuse";
|
|
218
|
+
/** `<host>:<pid>`: a sweep removes the container once that process is dead, whatever reaper session holds it. */
|
|
219
|
+
const OWNER_LABEL = "com.jterrazz.test.owner";
|
|
220
|
+
function processOwner() {
|
|
221
|
+
return `${hostname()}:${process.pid}`;
|
|
222
|
+
}
|
|
223
|
+
/** Never on CI, where a run starts from nothing and leaves nothing. */
|
|
224
|
+
function reuseEnabled(env) {
|
|
225
|
+
const onCi = env.CI !== void 0 && env.CI !== "" && env.CI !== "false";
|
|
226
|
+
return env.TESTCONTAINERS_REUSE_ENABLE === "true" && !onCi;
|
|
227
|
+
}
|
|
228
|
+
/** A reused container names no owner: its definition hash must not change with the process asking. */
|
|
229
|
+
function containerLabels(reuse, owner) {
|
|
230
|
+
return reuse ? {
|
|
231
|
+
[CONTAINER_LABEL]: "true",
|
|
232
|
+
[REUSED_LABEL]: "true"
|
|
233
|
+
} : {
|
|
234
|
+
[CONTAINER_LABEL]: "true",
|
|
235
|
+
[OWNER_LABEL]: owner
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
async function loadTestcontainers() {
|
|
239
|
+
return await loadPeer("testcontainers", "a declared service", async () => await import("testcontainers"));
|
|
240
|
+
}
|
|
241
|
+
var TestcontainersAdapter = class {
|
|
242
|
+
image;
|
|
243
|
+
containerPort;
|
|
244
|
+
env;
|
|
245
|
+
command;
|
|
246
|
+
tmpfs;
|
|
247
|
+
reuse;
|
|
248
|
+
container = null;
|
|
249
|
+
peer;
|
|
250
|
+
constructor(options, peer = loadTestcontainers) {
|
|
251
|
+
this.peer = peer;
|
|
252
|
+
this.image = options.image;
|
|
253
|
+
this.containerPort = options.port;
|
|
254
|
+
this.env = options.env ?? {};
|
|
255
|
+
this.command = options.command;
|
|
256
|
+
this.tmpfs = options.tmpfs;
|
|
257
|
+
this.reuse = options.reuse ?? reuseEnabled(process.env);
|
|
258
|
+
}
|
|
259
|
+
async start() {
|
|
260
|
+
const { GenericContainer, Wait } = await this.peer();
|
|
261
|
+
let builder = new GenericContainer(this.image).withExposedPorts(this.containerPort).withLabels(containerLabels(this.reuse, processOwner()));
|
|
262
|
+
for (const [key, value] of Object.entries(this.env)) builder = builder.withEnvironment({ [key]: value });
|
|
263
|
+
if (this.command !== void 0) builder = builder.withCommand(this.command);
|
|
264
|
+
if (this.tmpfs !== void 0) builder = builder.withTmpFs(this.tmpfs);
|
|
265
|
+
if (this.image.startsWith("postgres")) builder = builder.withWaitStrategy(Wait.forLogMessage(/database system is ready to accept connections/u, 2));
|
|
266
|
+
if (this.reuse) builder = builder.withReuse();
|
|
267
|
+
this.container = await builder.start();
|
|
268
|
+
}
|
|
269
|
+
async stop() {
|
|
270
|
+
if (this.container && !this.reuse) {
|
|
271
|
+
await this.container.stop();
|
|
272
|
+
this.container = null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
getMappedPort(containerPort) {
|
|
276
|
+
if (!this.container) throw invariant("a container was read before it started");
|
|
277
|
+
return this.container.getMappedPort(containerPort);
|
|
278
|
+
}
|
|
279
|
+
getHost() {
|
|
280
|
+
if (!this.container) throw invariant("a container was read before it started");
|
|
281
|
+
return this.container.getHost();
|
|
282
|
+
}
|
|
283
|
+
getConnectionString() {
|
|
284
|
+
return `${this.getHost()}:${this.getMappedPort(this.containerPort)}`;
|
|
285
|
+
}
|
|
286
|
+
async getLogs(since) {
|
|
287
|
+
if (!this.container) return "";
|
|
288
|
+
const stream = await this.container.logs(since === void 0 ? void 0 : { since: since.getTime() / 1e3 });
|
|
289
|
+
return await new Promise((resolve) => {
|
|
290
|
+
let output = "";
|
|
291
|
+
stream.on("data", (chunk) => {
|
|
292
|
+
output += Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk);
|
|
293
|
+
});
|
|
294
|
+
stream.on("end", () => {
|
|
295
|
+
resolve(output);
|
|
296
|
+
});
|
|
297
|
+
setTimeout(() => {
|
|
298
|
+
resolve(output);
|
|
299
|
+
}, 1e3);
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
//#endregion
|
|
304
|
+
export { field as a, containerLogs as c, findContainersByLabel as d, inspectContainer as f, requireDockerDaemon as h, RunContainers as i, execInContainer as l, removeContainers as m, REUSED_LABEL as n, serveContainers as o, readContainerFile as p, TestcontainersAdapter as r, DOCKER_ABSENT as s, OWNER_LABEL as t, fieldOf as u };
|
|
305
|
+
|
|
306
|
+
//# sourceMappingURL=shared-testcontainers.adapter.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//#region lint/kit/project/tokens.ts
|
|
2
|
+
const TOKEN_KINDS = [
|
|
3
|
+
"any",
|
|
4
|
+
"base64",
|
|
5
|
+
"date",
|
|
6
|
+
"duration",
|
|
7
|
+
"email",
|
|
8
|
+
"float",
|
|
9
|
+
"hex",
|
|
10
|
+
"int",
|
|
11
|
+
"ip",
|
|
12
|
+
"iso8601",
|
|
13
|
+
"number",
|
|
14
|
+
"path",
|
|
15
|
+
"port",
|
|
16
|
+
"semver",
|
|
17
|
+
"sha",
|
|
18
|
+
"string",
|
|
19
|
+
"time",
|
|
20
|
+
"ulid",
|
|
21
|
+
"url",
|
|
22
|
+
"uuid",
|
|
23
|
+
"workdir"
|
|
24
|
+
];
|
|
25
|
+
const VALID_TOKEN = /^[A-Za-z][A-Za-z0-9]*(?:#[\w.-]+)?$/u;
|
|
26
|
+
const BRACE_BLOCK = /\{\{[^{}]*\}\}/gu;
|
|
27
|
+
const LEADING_WORD = /^[A-Za-z][A-Za-z0-9]*/u;
|
|
28
|
+
const KNOWN = new Set(TOKEN_KINDS);
|
|
29
|
+
function verdictOf(inner) {
|
|
30
|
+
if (VALID_TOKEN.test(inner)) {
|
|
31
|
+
const [kind = ""] = inner.split("#");
|
|
32
|
+
return KNOWN.has(kind) ? "known" : "unknown";
|
|
33
|
+
}
|
|
34
|
+
const [word = ""] = LEADING_WORD.exec(inner) ?? [];
|
|
35
|
+
return KNOWN.has(word) ? "malformed" : void 0;
|
|
36
|
+
}
|
|
37
|
+
function tokensOf(text, verdict) {
|
|
38
|
+
return text.split("\n").flatMap((lineText, index) => [...lineText.matchAll(BRACE_BLOCK)].flatMap(([token]) => verdictOf(token.slice(2, -2)) === verdict ? [{
|
|
39
|
+
line: index + 1,
|
|
40
|
+
token
|
|
41
|
+
}] : []));
|
|
42
|
+
}
|
|
43
|
+
function findUnknownTokens(text) {
|
|
44
|
+
return tokensOf(text, "unknown");
|
|
45
|
+
}
|
|
46
|
+
function findMalformedRefs(text) {
|
|
47
|
+
return tokensOf(text, "malformed");
|
|
48
|
+
}
|
|
49
|
+
function findKnownTokens(text) {
|
|
50
|
+
return tokensOf(text, "known");
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { findUnknownTokens as i, findKnownTokens as n, findMalformedRefs as r, TOKEN_KINDS as t };
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=shared-tokens.js.map
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { LineCounter, Scalar, Scalar as Scalar$1, isMap, isNode, isNode as isNode$1, isPair, isScalar, isSeq, parseDocument, visit } from "yaml";
|
|
2
|
+
//#region lint/kit/project/yaml-document.ts
|
|
3
|
+
function nodePairs(map) {
|
|
4
|
+
return map.items.filter((pair) => isNode(pair.key) && (pair.value === null || isNode(pair.value)));
|
|
5
|
+
}
|
|
6
|
+
function nodeItems(seq) {
|
|
7
|
+
return seq.items.filter((item) => isNode(item));
|
|
8
|
+
}
|
|
9
|
+
const KEY_LINE = /^(?<indent> +)(?:(?:- )?[A-Za-z_$'"][^\n]*:|- [[{])/u;
|
|
10
|
+
function detectIndent(text) {
|
|
11
|
+
let smallest = 0;
|
|
12
|
+
for (const line of text.split("\n")) {
|
|
13
|
+
const indent = KEY_LINE.exec(line)?.groups?.indent;
|
|
14
|
+
if (indent !== void 0 && (smallest === 0 || indent.length < smallest)) smallest = indent.length;
|
|
15
|
+
}
|
|
16
|
+
return smallest === 0 ? 2 : smallest;
|
|
17
|
+
}
|
|
18
|
+
const DEEPEST_STATABLE_STEP = 9;
|
|
19
|
+
function stateIndentation(written, ctx) {
|
|
20
|
+
if (!/^[|>][1-9]/u.test(written)) return written;
|
|
21
|
+
const step = ctx.indentStep.length;
|
|
22
|
+
if (step > DEEPEST_STATABLE_STEP) throw new Error(`A block scalar indented by ${step} spaces cannot state its indentation: YAML writes that indicator as a single digit, so ${DEEPEST_STATABLE_STEP} spaces is the deepest step a document may be written with.`);
|
|
23
|
+
return `${written[0]}${step}${written.slice(2)}`;
|
|
24
|
+
}
|
|
25
|
+
function isScalarTag(tag) {
|
|
26
|
+
return typeof tag !== "string" && tag.collection === void 0;
|
|
27
|
+
}
|
|
28
|
+
function statingItsIndentation(tag) {
|
|
29
|
+
const write = tag.stringify;
|
|
30
|
+
if (write === void 0) return tag;
|
|
31
|
+
return {
|
|
32
|
+
...tag,
|
|
33
|
+
stringify: (item, ctx, onComment, onChompKeep) => stateIndentation(write(item, ctx, onComment, onChompKeep), ctx)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function parseYamlSource(text) {
|
|
37
|
+
const lineCounter = new LineCounter();
|
|
38
|
+
return {
|
|
39
|
+
document: parseDocument(text, {
|
|
40
|
+
customTags: (tags) => tags.map((tag) => isScalarTag(tag) ? statingItsIndentation(tag) : tag),
|
|
41
|
+
lineCounter
|
|
42
|
+
}),
|
|
43
|
+
indent: detectIndent(text),
|
|
44
|
+
lineAt: (offset) => lineCounter.linePos(offset).line,
|
|
45
|
+
text
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function yamlSyntaxErrors(source) {
|
|
49
|
+
return source.document.errors.map((error) => ({
|
|
50
|
+
line: source.lineAt(error.pos[0]),
|
|
51
|
+
message: error.message
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
function blockScalar(text) {
|
|
55
|
+
const scalar = new Scalar(text);
|
|
56
|
+
scalar.type = Scalar.BLOCK_LITERAL;
|
|
57
|
+
return scalar;
|
|
58
|
+
}
|
|
59
|
+
function lineStart(text, offset) {
|
|
60
|
+
return text.lastIndexOf("\n", offset - 1) + 1;
|
|
61
|
+
}
|
|
62
|
+
function afterOwner(text, offset) {
|
|
63
|
+
return text.slice(0, offset).trimEnd().length;
|
|
64
|
+
}
|
|
65
|
+
function indentOf(text, offset) {
|
|
66
|
+
const line = text.slice(lineStart(text, offset));
|
|
67
|
+
return line.length - line.trimStart().length;
|
|
68
|
+
}
|
|
69
|
+
function flowCollections(text) {
|
|
70
|
+
const found = [];
|
|
71
|
+
const take = (node) => {
|
|
72
|
+
if (node.flow !== true) return;
|
|
73
|
+
const { range } = node;
|
|
74
|
+
if (range === null || range === void 0) return;
|
|
75
|
+
const [start, end] = range;
|
|
76
|
+
const from = afterOwner(text, start);
|
|
77
|
+
found.push({
|
|
78
|
+
ownerIndent: indentOf(text, from),
|
|
79
|
+
range: [from, end],
|
|
80
|
+
text: text.slice(from, end),
|
|
81
|
+
value: JSON.stringify(node.toJSON())
|
|
82
|
+
});
|
|
83
|
+
return visit.SKIP;
|
|
84
|
+
};
|
|
85
|
+
visit(parseDocument(text), {
|
|
86
|
+
Map: (_key, node) => take(node),
|
|
87
|
+
Seq: (_key, node) => take(node)
|
|
88
|
+
});
|
|
89
|
+
return found;
|
|
90
|
+
}
|
|
91
|
+
function restoreUntouchedFlow(rendered, text) {
|
|
92
|
+
const sources = flowCollections(text);
|
|
93
|
+
const taken = /* @__PURE__ */ new Set();
|
|
94
|
+
const splices = [];
|
|
95
|
+
for (const target of flowCollections(rendered)) {
|
|
96
|
+
const at = sources.findIndex((candidate, index) => !taken.has(index) && candidate.value === target.value && candidate.ownerIndent === target.ownerIndent);
|
|
97
|
+
const source = sources[at];
|
|
98
|
+
if (source === void 0) continue;
|
|
99
|
+
taken.add(at);
|
|
100
|
+
splices.push({
|
|
101
|
+
range: target.range,
|
|
102
|
+
text: source.text
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
let out = rendered;
|
|
106
|
+
for (const splice of splices.toReversed()) out = out.slice(0, splice.range[0]) + splice.text + out.slice(splice.range[1]);
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
/** `lineWidth: 0`: a golden is compared byte for byte, so nothing folds a long line. */
|
|
110
|
+
function renderYamlSource(source) {
|
|
111
|
+
return restoreUntouchedFlow(source.document.toString({
|
|
112
|
+
indent: source.indent,
|
|
113
|
+
lineWidth: 0
|
|
114
|
+
}), source.text);
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
export { isPair as a, nodeItems as c, renderYamlSource as d, yamlSyntaxErrors as f, isNode$1 as i, nodePairs as l, blockScalar as n, isScalar as o, isMap as r, isSeq as s, Scalar$1 as t, parseYamlSource as u };
|
|
118
|
+
|
|
119
|
+
//# sourceMappingURL=shared-yaml-document.js.map
|
package/dist/stubs.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { C as StubRequest, E as StubResponseValue, S as StreamBody, T as StubResponse, _ as StubInput, a as openai, b as defineStubs, c as SseEvent, d as anthropic, f as TextFilter, g as Stub, i as InterceptScope, l as http, n as Intercept, o as HttpResponseInit, r as InterceptOptions, s as HttpStreamInit, t as intercept, u as HttpStubFilter, v as Stubs, w as StubResponder, x as MatchableRequest, y as defineStub } from "./shared-index.stubs.js";
|
|
2
|
+
export { type HttpResponseInit, type HttpStreamInit, type HttpStubFilter, type Intercept, type InterceptOptions, type InterceptScope, type MatchableRequest, type SseEvent, type StreamBody, type Stub, type StubInput, type StubRequest, type StubResponder, type StubResponse, type StubResponseValue, type Stubs, type TextFilter, anthropic, defineStub, defineStubs, http, intercept, openai };
|