@jterrazz/test 16.1.1 → 17.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 +2 -2980
- package/dist/browser/services.js +2 -0
- package/dist/browser/stubs.js +2 -0
- package/dist/checker.d.ts +6 -1
- package/dist/checker.js +4534 -2628
- package/dist/config.d.ts +40 -0
- package/dist/config.js +309 -0
- package/dist/in-place.global-setup.d.ts +11 -0
- package/dist/in-place.global-setup.js +21 -0
- package/dist/index.d.ts +1653 -3004
- package/dist/index.js +5226 -3845
- package/dist/mobile-session.global-setup.d.ts +14 -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 +6127 -5693
- package/dist/route-coverage.global-setup.d.ts +42 -0
- package/dist/route-coverage.global-setup.js +2 -0
- package/dist/services.d.ts +3 -0
- package/dist/services.global-setup.d.ts +43 -0
- package/dist/services.global-setup.js +2 -0
- package/dist/services.js +597 -0
- package/dist/shared-appium.adapter.js +428 -0
- package/dist/shared-artifacts.js +32 -0
- package/dist/shared-chapter.js +7 -0
- package/dist/shared-container.port.d.ts +31 -0
- package/dist/shared-derived-projects.js +58 -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.browser.js +3191 -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-literate-plugin.js +183 -0
- package/dist/shared-manifest.js +1321 -0
- package/dist/shared-pause.js +11 -0
- package/dist/shared-peer.js +88 -0
- package/dist/shared-playwright.adapter.js +330 -0
- package/dist/shared-process.adapter.js +74 -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 +365 -0
- package/dist/shared-service-images.js +10 -0
- package/dist/shared-services.global-setup.js +179 -0
- package/dist/shared-session-ledger.js +301 -0
- package/dist/shared-setup-entry.js +12 -0
- package/dist/shared-source-scan.js +133 -0
- package/dist/shared-spec-document.js +365 -0
- package/dist/shared-specifications.js +26 -0
- package/dist/shared-stream-body.js +130 -0
- package/dist/shared-stub-server.js +190 -0
- package/dist/shared-temp-root.global-setup.js +80 -0
- package/dist/shared-test-baseline.js +26 -0
- package/dist/shared-test-section.js +58 -0
- package/dist/shared-testcontainers.adapter.js +203 -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 +2 -0
- package/dist/vitest.d.ts +97 -163
- package/dist/vitest.js +373 -525
- 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 +564 -0
- package/docs/guides/component-specs.md +252 -0
- package/docs/guides/develop.md +94 -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 +43 -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 +498 -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 +214 -0
- package/package.json +50 -30
- 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,190 @@
|
|
|
1
|
+
import { n as isStreamBody } from "./shared-exchange.js";
|
|
2
|
+
import { t as pause } from "./shared-pause.js";
|
|
3
|
+
import { n as StubQueue, t as toReadableStream } from "./shared-stream-body.js";
|
|
4
|
+
//#region src/stubs/delivery/msw-handlers.ts
|
|
5
|
+
const NEVER_CACHED = { "cache-control": "no-store" };
|
|
6
|
+
function toMswResponse(msw, response) {
|
|
7
|
+
const { body, headers, status = 200 } = response;
|
|
8
|
+
if (response.transport === "network-error") return msw.HttpResponse.error();
|
|
9
|
+
if (body === null || body === void 0) return new msw.HttpResponse(null, {
|
|
10
|
+
headers: {
|
|
11
|
+
...NEVER_CACHED,
|
|
12
|
+
...headers
|
|
13
|
+
},
|
|
14
|
+
status
|
|
15
|
+
});
|
|
16
|
+
if (isStreamBody(body)) return new msw.HttpResponse(toReadableStream(body), {
|
|
17
|
+
headers: {
|
|
18
|
+
...NEVER_CACHED,
|
|
19
|
+
"content-type": body.contentType,
|
|
20
|
+
...headers
|
|
21
|
+
},
|
|
22
|
+
status
|
|
23
|
+
});
|
|
24
|
+
if (typeof body === "string") return new msw.HttpResponse(body, {
|
|
25
|
+
headers: {
|
|
26
|
+
...NEVER_CACHED,
|
|
27
|
+
"content-type": "text/plain; charset=utf-8",
|
|
28
|
+
...headers
|
|
29
|
+
},
|
|
30
|
+
status
|
|
31
|
+
});
|
|
32
|
+
return msw.HttpResponse.json(body, {
|
|
33
|
+
headers: {
|
|
34
|
+
...NEVER_CACHED,
|
|
35
|
+
...headers
|
|
36
|
+
},
|
|
37
|
+
status
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function handlerOf(msw, method) {
|
|
41
|
+
return {
|
|
42
|
+
"*": msw.http.all,
|
|
43
|
+
DELETE: msw.http.delete,
|
|
44
|
+
GET: msw.http.get,
|
|
45
|
+
HEAD: msw.http.head,
|
|
46
|
+
OPTIONS: msw.http.options,
|
|
47
|
+
PATCH: msw.http.patch,
|
|
48
|
+
POST: msw.http.post,
|
|
49
|
+
PUT: msw.http.put
|
|
50
|
+
}[method.toUpperCase()];
|
|
51
|
+
}
|
|
52
|
+
function buildStubHandlers(msw, stubs, bypass) {
|
|
53
|
+
const queue = new StubQueue(stubs);
|
|
54
|
+
let violation = null;
|
|
55
|
+
const recordViolation = (method, url) => {
|
|
56
|
+
violation ??= queue.unmatchedError(method, url);
|
|
57
|
+
return msw.HttpResponse.json({ error: `@jterrazz/test strict stubs: unmatched request ${method} ${url}` }, {
|
|
58
|
+
headers: NEVER_CACHED,
|
|
59
|
+
status: 501
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
const handlers = [];
|
|
63
|
+
for (const route of queue.routes) for (const method of route.methods) {
|
|
64
|
+
const handlerFn = handlerOf(msw, method);
|
|
65
|
+
if (handlerFn === void 0) continue;
|
|
66
|
+
handlers.push(handlerFn(route.url, async ({ request }) => {
|
|
67
|
+
const observed = await observe(request);
|
|
68
|
+
const stub = queue.take(observed);
|
|
69
|
+
if (!stub) return recordViolation(request.method, request.url);
|
|
70
|
+
const response = typeof stub.response === "function" ? stub.response(observed) : stub.response;
|
|
71
|
+
await pause(response.delay ?? 0);
|
|
72
|
+
return toMswResponse(msw, response);
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
handlers.push(msw.http.all("*", ({ request }) => bypass?.(request.url) === true ? msw.passthrough() : recordViolation(request.method, request.url)));
|
|
76
|
+
return {
|
|
77
|
+
handlers,
|
|
78
|
+
violation: () => queue.verdict(violation)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async function observe(request) {
|
|
82
|
+
let body = null;
|
|
83
|
+
const rawText = await request.clone().text();
|
|
84
|
+
if (rawText) try {
|
|
85
|
+
body = JSON.parse(rawText);
|
|
86
|
+
} catch {
|
|
87
|
+
body = rawText;
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
body,
|
|
91
|
+
headers: Object.fromEntries(request.headers),
|
|
92
|
+
method: request.method.toUpperCase(),
|
|
93
|
+
url: request.url
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/stubs/delivery/same-process/fetch-refusals.ts
|
|
98
|
+
const PROBE = "http://request-init.invalid/";
|
|
99
|
+
const probeOf = (init) => init.body instanceof ReadableStream ? {
|
|
100
|
+
...init,
|
|
101
|
+
body: new Blob()
|
|
102
|
+
} : init;
|
|
103
|
+
/**
|
|
104
|
+
* Wraps `underlying` so a RequestInit that fetch itself refuses rejects with fetch's own TypeError, before any stub can serve it.
|
|
105
|
+
* @see https://fetch.spec.whatwg.org/#dom-request
|
|
106
|
+
*/
|
|
107
|
+
function refusingWhatFetchRefuses(underlying) {
|
|
108
|
+
return async (input, init) => {
|
|
109
|
+
if (init === void 0) return await underlying(input);
|
|
110
|
+
const { method } = new Request(PROBE, probeOf(init));
|
|
111
|
+
return await underlying(input, {
|
|
112
|
+
...init,
|
|
113
|
+
method
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/stubs/delivery/same-process/owned-body.ts
|
|
119
|
+
async function release(reader, reason) {
|
|
120
|
+
try {
|
|
121
|
+
await reader.cancel(reason);
|
|
122
|
+
} catch {}
|
|
123
|
+
}
|
|
124
|
+
function withOwnedBody(response) {
|
|
125
|
+
const { body } = response;
|
|
126
|
+
if (body === null) return response;
|
|
127
|
+
const reader = body.getReader();
|
|
128
|
+
const owned = new ReadableStream({
|
|
129
|
+
cancel: (reason) => {
|
|
130
|
+
release(reader, reason);
|
|
131
|
+
},
|
|
132
|
+
pull: async (controller) => {
|
|
133
|
+
const { done, value } = await reader.read();
|
|
134
|
+
if (done) controller.close();
|
|
135
|
+
else controller.enqueue(value);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
const reply = new Response(owned, {
|
|
139
|
+
headers: response.headers,
|
|
140
|
+
status: response.status,
|
|
141
|
+
statusText: response.statusText
|
|
142
|
+
});
|
|
143
|
+
Object.defineProperties(reply, {
|
|
144
|
+
redirected: { value: response.redirected },
|
|
145
|
+
type: { value: response.type },
|
|
146
|
+
url: { value: response.url }
|
|
147
|
+
});
|
|
148
|
+
return reply;
|
|
149
|
+
}
|
|
150
|
+
function owningBodies(underlying) {
|
|
151
|
+
return async (input, init) => withOwnedBody(await underlying(input, init));
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/stubs/delivery/same-process/stub-server.ts
|
|
155
|
+
async function loadMsw() {
|
|
156
|
+
const [{ setupServer }, msw] = await Promise.all([import("msw/node"), import("msw")]);
|
|
157
|
+
return {
|
|
158
|
+
msw,
|
|
159
|
+
setupServer: () => setupServer()
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
let serverInstance = null;
|
|
163
|
+
let installedFetch = null;
|
|
164
|
+
async function ensureStubServer() {
|
|
165
|
+
if (serverInstance !== null) return serverInstance;
|
|
166
|
+
const { setupServer } = await loadMsw();
|
|
167
|
+
const server = setupServer();
|
|
168
|
+
server.listen({ onUnhandledRequest: "bypass" });
|
|
169
|
+
serverInstance = server;
|
|
170
|
+
installedFetch = globalThis.fetch;
|
|
171
|
+
globalThis.fetch = owningBodies(refusingWhatFetchRefuses(installedFetch));
|
|
172
|
+
return server;
|
|
173
|
+
}
|
|
174
|
+
/** The trailing catch-all records any request no stub accepted; the chain's pipeline rethrows it. */
|
|
175
|
+
async function registerStubs(stubs) {
|
|
176
|
+
const server = await ensureStubServer();
|
|
177
|
+
const { msw } = await loadMsw();
|
|
178
|
+
const { handlers, violation } = buildStubHandlers(msw, stubs);
|
|
179
|
+
server.use(...handlers);
|
|
180
|
+
return {
|
|
181
|
+
cleanup: () => {
|
|
182
|
+
server.resetHandlers();
|
|
183
|
+
},
|
|
184
|
+
violation
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
//#endregion
|
|
188
|
+
export { registerStubs };
|
|
189
|
+
|
|
190
|
+
//# sourceMappingURL=shared-stub-server.js.map
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { a as EVIDENCE_DIR } from "./shared-artifacts.js";
|
|
2
|
+
import { t as setupEntry } from "./shared-setup-entry.js";
|
|
3
|
+
import { existsSync, mkdirSync, mkdtempSync, readdirSync, rmSync } from "node:fs";
|
|
4
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
//#region src/chain/paths/run-folders.ts
|
|
7
|
+
function isAlive(pid) {
|
|
8
|
+
try {
|
|
9
|
+
process.kill(pid, 0);
|
|
10
|
+
return true;
|
|
11
|
+
} catch (error) {
|
|
12
|
+
return error instanceof Error && "code" in error && error.code === "EPERM";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function runOf(entry) {
|
|
16
|
+
const pid = Number(entry.split("-")[0]);
|
|
17
|
+
return Number.isInteger(pid) && pid > 0 ? pid : null;
|
|
18
|
+
}
|
|
19
|
+
/** Leaves alone every name no run gave: the parent of temp roots is shared with whatever else a user keeps there. */
|
|
20
|
+
function sweepOrphanRoots(parent) {
|
|
21
|
+
for (const entry of readdirSync(parent)) {
|
|
22
|
+
const pid = runOf(entry);
|
|
23
|
+
if (pid !== null && !isAlive(pid)) rmSync(join(parent, entry), {
|
|
24
|
+
force: true,
|
|
25
|
+
recursive: true
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** Keeps a living run's evidence alone: a finished run's stays until the next run starts, and nothing else is ever kept. */
|
|
30
|
+
function sweepFinishedEvidence(folder) {
|
|
31
|
+
if (!existsSync(folder)) return;
|
|
32
|
+
for (const entry of readdirSync(folder)) {
|
|
33
|
+
const pid = runOf(entry);
|
|
34
|
+
if (pid === null || !isAlive(pid)) rmSync(join(folder, entry), {
|
|
35
|
+
force: true,
|
|
36
|
+
recursive: true
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/chain/paths/temp-root.global-setup.ts
|
|
42
|
+
/** Named by uid: a temp directory users share never holds a folder one of them cannot sweep. */
|
|
43
|
+
const TEMP_ROOTS_PARENT = `jterrazz-test-runs-${process.getuid?.() ?? "user"}`;
|
|
44
|
+
const TEMP_VARIABLES = [
|
|
45
|
+
"TMPDIR",
|
|
46
|
+
"TMP",
|
|
47
|
+
"TEMP"
|
|
48
|
+
];
|
|
49
|
+
function setVariables(values) {
|
|
50
|
+
for (const [name, value] of Object.entries(values)) if (value === void 0) Reflect.deleteProperty(process.env, name);
|
|
51
|
+
else process.env[name] = value;
|
|
52
|
+
}
|
|
53
|
+
function tempRootSetup() {
|
|
54
|
+
return setupEntry(import.meta.dirname, "temp-root");
|
|
55
|
+
}
|
|
56
|
+
function joinedRoot() {
|
|
57
|
+
const current = tmpdir();
|
|
58
|
+
return basename(dirname(current)) === TEMP_ROOTS_PARENT && basename(current).startsWith(`${process.pid}-`);
|
|
59
|
+
}
|
|
60
|
+
function setup() {
|
|
61
|
+
if (joinedRoot()) return () => {};
|
|
62
|
+
const parent = join(tmpdir(), TEMP_ROOTS_PARENT);
|
|
63
|
+
mkdirSync(parent, { recursive: true });
|
|
64
|
+
sweepOrphanRoots(parent);
|
|
65
|
+
sweepFinishedEvidence(resolve(EVIDENCE_DIR));
|
|
66
|
+
const root = mkdtempSync(join(parent, `${process.pid}-`));
|
|
67
|
+
const previous = Object.fromEntries(TEMP_VARIABLES.map((name) => [name, process.env[name]]));
|
|
68
|
+
setVariables(Object.fromEntries(TEMP_VARIABLES.map((name) => [name, root])));
|
|
69
|
+
return () => {
|
|
70
|
+
setVariables(previous);
|
|
71
|
+
rmSync(root, {
|
|
72
|
+
force: true,
|
|
73
|
+
recursive: true
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
export { setup as n, tempRootSetup as r, TEMP_ROOTS_PARENT as t };
|
|
79
|
+
|
|
80
|
+
//# sourceMappingURL=shared-temp-root.global-setup.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { t as TEST_SECTION } from "./shared-test-section.js";
|
|
2
|
+
import { labelOf, sectionOf, writeSection } from "@jterrazz/typescript/rules";
|
|
3
|
+
//#region lint/kit/baseline/test-baseline.ts
|
|
4
|
+
const ROUTES_KEY = "routes/unreached";
|
|
5
|
+
const ROUTES_LEDGER = `${labelOf(TEST_SECTION)} › ${ROUTES_KEY}`;
|
|
6
|
+
const heldOf = (root) => sectionOf(root, "test") ?? {};
|
|
7
|
+
const withoutRoutes = (counts) => Object.fromEntries(Object.entries(counts).filter(([key]) => key !== ROUTES_KEY));
|
|
8
|
+
function writeBaseline(root, counts) {
|
|
9
|
+
const held = heldOf(root)[ROUTES_KEY];
|
|
10
|
+
writeSection(root, TEST_SECTION, held === void 0 ? { ...counts } : {
|
|
11
|
+
...counts,
|
|
12
|
+
[ROUTES_KEY]: held
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
const heldUnreached = (root) => heldOf(root)[ROUTES_KEY] ?? 0;
|
|
16
|
+
function recordUnreached(root, count) {
|
|
17
|
+
const others = withoutRoutes(heldOf(root));
|
|
18
|
+
writeSection(root, TEST_SECTION, count === 0 ? others : {
|
|
19
|
+
...others,
|
|
20
|
+
[ROUTES_KEY]: count
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { writeBaseline as i, heldUnreached as n, recordUnreached as r, ROUTES_LEDGER as t };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=shared-test-baseline.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { t as isRecord } from "./shared-json-shape.js";
|
|
2
|
+
import { readSettings, readSettingsSync, refusalBlock } from "@jterrazz/typescript/rules";
|
|
3
|
+
//#region lint/kit/project/test-section.ts
|
|
4
|
+
const TEST_SECTION = "test";
|
|
5
|
+
const UNREAD = Object.freeze({
|
|
6
|
+
collections: [],
|
|
7
|
+
methodless: [],
|
|
8
|
+
production: { allow: {} }
|
|
9
|
+
});
|
|
10
|
+
function wordsOf(value) {
|
|
11
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string") ? value : void 0;
|
|
12
|
+
}
|
|
13
|
+
function reasonsOf(value) {
|
|
14
|
+
if (!isRecord(value)) return;
|
|
15
|
+
const reasons = {};
|
|
16
|
+
for (const [name, reason] of Object.entries(value)) {
|
|
17
|
+
if (typeof reason !== "string" || reason.trim() === "") return;
|
|
18
|
+
reasons[name] = reason;
|
|
19
|
+
}
|
|
20
|
+
return reasons;
|
|
21
|
+
}
|
|
22
|
+
const BASE_PATH = /^(?:\/[a-z0-9][\w.~-]*)+$/u;
|
|
23
|
+
const isMountPath = (value) => BASE_PATH.test(value);
|
|
24
|
+
function basePathOf(value) {
|
|
25
|
+
if (typeof value !== "string") return;
|
|
26
|
+
if (!isMountPath(value)) throw new Error(refusalBlock({
|
|
27
|
+
fix: "write the path the routes are mounted under from the root, with no slash at its end: test: { basePath: '/api' }",
|
|
28
|
+
found: `jterrazz.config.ts: test.basePath ${JSON.stringify(value)} is no mount path`,
|
|
29
|
+
read: "docs/reference/linting.md#test-settings-section",
|
|
30
|
+
why: "The base path is cut from every route before its subject folder is derived, so it is read as a route prefix is written: one leading slash, lowercase segments, no trailing slash."
|
|
31
|
+
}));
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
/** A key of the wrong shape is read as its default, never half-used: the type check refuses it first; a base path the type lets through and no route can carry is refused here. */
|
|
35
|
+
function testSettingsOf(test) {
|
|
36
|
+
const section = isRecord(test) ? test : {};
|
|
37
|
+
const allow = isRecord(section.production) ? reasonsOf(section.production.allow) : void 0;
|
|
38
|
+
const basePath = basePathOf(section.basePath);
|
|
39
|
+
return {
|
|
40
|
+
collections: wordsOf(section.collections) ?? UNREAD.collections,
|
|
41
|
+
methodless: Object.keys(reasonsOf(section.methodless) ?? {}),
|
|
42
|
+
production: allow === void 0 ? UNREAD.production : { allow },
|
|
43
|
+
...typeof section.routes === "string" ? { routes: section.routes } : {},
|
|
44
|
+
...basePath === void 0 ? {} : { basePath }
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** Every pass is sync, so the file is loaded synchronously, once per process; the runtime reads the same resolution, so the run and the checker agree. A file that does not resolve is no settings: the toolchain already reports it. */
|
|
48
|
+
const testSettingsAtSync = (root) => testSettingsOf(readSettingsSync(root).resolved.sections[TEST_SECTION]);
|
|
49
|
+
/** Whether the unit's settings list this package's preset, the one whose checker judges every specs tree under it. */
|
|
50
|
+
const listsTestPreset = (root) => readSettingsSync(root).resolved.presets.some((preset) => preset.package === "@jterrazz/test");
|
|
51
|
+
async function testSettingsAt(root) {
|
|
52
|
+
const { resolved } = await readSettings(root);
|
|
53
|
+
return testSettingsOf(resolved.sections[TEST_SECTION]);
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { testSettingsAtSync as i, listsTestPreset as n, testSettingsAt as r, TEST_SECTION as t };
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=shared-test-section.js.map
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { t as isRecord } from "./shared-json-shape.js";
|
|
2
|
+
import { t as invariant } from "./shared-refusal.js";
|
|
3
|
+
import { t as loadPeer } from "./shared-peer.js";
|
|
4
|
+
import { once } from "node:events";
|
|
5
|
+
import { createServer } from "node:http";
|
|
6
|
+
import { text } from "node:stream/consumers";
|
|
7
|
+
import { hostname } from "node:os";
|
|
8
|
+
//#region src/services/containers/run-containers.ts
|
|
9
|
+
function canonical(value) {
|
|
10
|
+
return isRecord(value) ? Object.fromEntries(Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b)).map(([key, entry]) => [key, canonical(entry)])) : value;
|
|
11
|
+
}
|
|
12
|
+
function keyOf(request) {
|
|
13
|
+
return JSON.stringify(canonical(request));
|
|
14
|
+
}
|
|
15
|
+
var RunContainers = class {
|
|
16
|
+
started = /* @__PURE__ */ new Map();
|
|
17
|
+
create;
|
|
18
|
+
ready;
|
|
19
|
+
/** `ready`'s rejection is the one answer every request gets, and no container is created. */
|
|
20
|
+
constructor(create, ready = async () => {}) {
|
|
21
|
+
this.create = create;
|
|
22
|
+
this.ready = ready;
|
|
23
|
+
}
|
|
24
|
+
async acquire(request) {
|
|
25
|
+
const key = keyOf(request);
|
|
26
|
+
let pending = this.started.get(key);
|
|
27
|
+
if (pending === void 0) {
|
|
28
|
+
pending = this.start(key, request);
|
|
29
|
+
this.started.set(key, pending);
|
|
30
|
+
}
|
|
31
|
+
const container = await pending;
|
|
32
|
+
return {
|
|
33
|
+
host: container.getHost(),
|
|
34
|
+
key,
|
|
35
|
+
port: container.getMappedPort(request.port)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
async logs(key, since) {
|
|
39
|
+
const pending = this.started.get(key);
|
|
40
|
+
if (pending === void 0) return "";
|
|
41
|
+
return await (await pending).getLogs(since);
|
|
42
|
+
}
|
|
43
|
+
async stop() {
|
|
44
|
+
const pending = [...this.started.values()];
|
|
45
|
+
this.started.clear();
|
|
46
|
+
await Promise.allSettled(pending.map(async (starting) => {
|
|
47
|
+
await (await starting).stop();
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
async start(key, request) {
|
|
51
|
+
try {
|
|
52
|
+
await this.ready();
|
|
53
|
+
const container = this.create(request);
|
|
54
|
+
await container.start();
|
|
55
|
+
return container;
|
|
56
|
+
} catch (error) {
|
|
57
|
+
this.started.delete(key);
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
function field(value, name) {
|
|
63
|
+
if (typeof value !== "object" || value === null) return;
|
|
64
|
+
return Reflect.get(value, name);
|
|
65
|
+
}
|
|
66
|
+
function isContainerRequest(value) {
|
|
67
|
+
return typeof field(value, "image") === "string" && typeof field(value, "port") === "number";
|
|
68
|
+
}
|
|
69
|
+
async function respond(containers, request, response) {
|
|
70
|
+
const url = new URL(request.url ?? "/", "http://run");
|
|
71
|
+
try {
|
|
72
|
+
if (request.method === "POST" && url.pathname === "/containers") {
|
|
73
|
+
const asked = JSON.parse(await text(request));
|
|
74
|
+
if (!isContainerRequest(asked)) throw invariant(`a worker asked for ${JSON.stringify(asked)}, no container request`);
|
|
75
|
+
const address = await containers.acquire(asked);
|
|
76
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
77
|
+
response.end(JSON.stringify(address));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (request.method === "GET" && url.pathname === "/logs") {
|
|
81
|
+
response.writeHead(200, { "content-type": "text/plain" });
|
|
82
|
+
const key = url.searchParams.get("key") ?? "";
|
|
83
|
+
const since = url.searchParams.get("since");
|
|
84
|
+
const moment = since === null ? void 0 : new Date(Number(since));
|
|
85
|
+
response.end(await containers.logs(key, moment));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
response.writeHead(404).end();
|
|
89
|
+
} catch (error) {
|
|
90
|
+
response.writeHead(500, { "content-type": "application/json" });
|
|
91
|
+
response.end(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async function serveContainers(containers) {
|
|
95
|
+
const server = createServer((request, response) => {
|
|
96
|
+
respond(containers, request, response);
|
|
97
|
+
});
|
|
98
|
+
server.listen(0, "127.0.0.1");
|
|
99
|
+
await once(server, "listening");
|
|
100
|
+
const address = server.address();
|
|
101
|
+
if (address === null || typeof address === "string") throw invariant("the run containers are served on no port");
|
|
102
|
+
const { port } = address;
|
|
103
|
+
return {
|
|
104
|
+
close: async () => {
|
|
105
|
+
server.close();
|
|
106
|
+
await once(server, "close");
|
|
107
|
+
},
|
|
108
|
+
url: `http://127.0.0.1:${port}`
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/services/containers/testcontainers.adapter.ts
|
|
113
|
+
const CONTAINER_LABEL = "com.jterrazz.test";
|
|
114
|
+
const REUSED_LABEL = "com.jterrazz.test.reuse";
|
|
115
|
+
/** `<host>:<pid>`: a sweep removes the container once that process is dead, whatever reaper session holds it. */
|
|
116
|
+
const OWNER_LABEL = "com.jterrazz.test.owner";
|
|
117
|
+
function processOwner() {
|
|
118
|
+
return `${hostname()}:${process.pid}`;
|
|
119
|
+
}
|
|
120
|
+
/** Never on CI, where a run starts from nothing and leaves nothing. */
|
|
121
|
+
function reuseEnabled(env) {
|
|
122
|
+
const onCi = env.CI !== void 0 && env.CI !== "" && env.CI !== "false";
|
|
123
|
+
return env.TESTCONTAINERS_REUSE_ENABLE === "true" && !onCi;
|
|
124
|
+
}
|
|
125
|
+
/** A reused container names no owner: its definition hash must not change with the process asking. */
|
|
126
|
+
function containerLabels(reuse, owner) {
|
|
127
|
+
return reuse ? {
|
|
128
|
+
[CONTAINER_LABEL]: "true",
|
|
129
|
+
[REUSED_LABEL]: "true"
|
|
130
|
+
} : {
|
|
131
|
+
[CONTAINER_LABEL]: "true",
|
|
132
|
+
[OWNER_LABEL]: owner
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
async function loadTestcontainers() {
|
|
136
|
+
return await loadPeer("testcontainers", "a declared service", async () => await import("testcontainers"));
|
|
137
|
+
}
|
|
138
|
+
var TestcontainersAdapter = class {
|
|
139
|
+
image;
|
|
140
|
+
containerPort;
|
|
141
|
+
env;
|
|
142
|
+
command;
|
|
143
|
+
tmpfs;
|
|
144
|
+
reuse;
|
|
145
|
+
container = null;
|
|
146
|
+
peer;
|
|
147
|
+
constructor(options, peer = loadTestcontainers) {
|
|
148
|
+
this.peer = peer;
|
|
149
|
+
this.image = options.image;
|
|
150
|
+
this.containerPort = options.port;
|
|
151
|
+
this.env = options.env ?? {};
|
|
152
|
+
this.command = options.command;
|
|
153
|
+
this.tmpfs = options.tmpfs;
|
|
154
|
+
this.reuse = options.reuse ?? reuseEnabled(process.env);
|
|
155
|
+
}
|
|
156
|
+
async start() {
|
|
157
|
+
const { GenericContainer, Wait } = await this.peer();
|
|
158
|
+
let builder = new GenericContainer(this.image).withExposedPorts(this.containerPort).withLabels(containerLabels(this.reuse, processOwner()));
|
|
159
|
+
for (const [key, value] of Object.entries(this.env)) builder = builder.withEnvironment({ [key]: value });
|
|
160
|
+
if (this.command !== void 0) builder = builder.withCommand(this.command);
|
|
161
|
+
if (this.tmpfs !== void 0) builder = builder.withTmpFs(this.tmpfs);
|
|
162
|
+
if (this.image.startsWith("postgres")) builder = builder.withWaitStrategy(Wait.forLogMessage(/database system is ready to accept connections/u, 2));
|
|
163
|
+
if (this.reuse) builder = builder.withReuse();
|
|
164
|
+
this.container = await builder.start();
|
|
165
|
+
}
|
|
166
|
+
async stop() {
|
|
167
|
+
if (this.container && !this.reuse) {
|
|
168
|
+
await this.container.stop();
|
|
169
|
+
this.container = null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
getMappedPort(containerPort) {
|
|
173
|
+
if (!this.container) throw invariant("a container was read before it started");
|
|
174
|
+
return this.container.getMappedPort(containerPort);
|
|
175
|
+
}
|
|
176
|
+
getHost() {
|
|
177
|
+
if (!this.container) throw invariant("a container was read before it started");
|
|
178
|
+
return this.container.getHost();
|
|
179
|
+
}
|
|
180
|
+
getConnectionString() {
|
|
181
|
+
return `${this.getHost()}:${this.getMappedPort(this.containerPort)}`;
|
|
182
|
+
}
|
|
183
|
+
async getLogs(since) {
|
|
184
|
+
if (!this.container) return "";
|
|
185
|
+
const stream = await this.container.logs(since === void 0 ? void 0 : { since: since.getTime() / 1e3 });
|
|
186
|
+
return await new Promise((resolve) => {
|
|
187
|
+
let output = "";
|
|
188
|
+
stream.on("data", (chunk) => {
|
|
189
|
+
output += Buffer.isBuffer(chunk) ? chunk.toString() : String(chunk);
|
|
190
|
+
});
|
|
191
|
+
stream.on("end", () => {
|
|
192
|
+
resolve(output);
|
|
193
|
+
});
|
|
194
|
+
setTimeout(() => {
|
|
195
|
+
resolve(output);
|
|
196
|
+
}, 1e3);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
//#endregion
|
|
201
|
+
export { field as a, RunContainers as i, REUSED_LABEL as n, serveContainers as o, TestcontainersAdapter as r, OWNER_LABEL as t };
|
|
202
|
+
|
|
203
|
+
//# 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
|