@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,88 @@
|
|
|
1
|
+
import { n as installRootOf } from "./shared-resolve.js";
|
|
2
|
+
import { n as refusal } from "./shared-refusal.js";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
//#region src/services/peer.ts
|
|
6
|
+
const INSTALLERS = [
|
|
7
|
+
{
|
|
8
|
+
command: "bun add -d",
|
|
9
|
+
lockfile: "bun.lock",
|
|
10
|
+
name: "bun"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
command: "bun add -d",
|
|
14
|
+
lockfile: "bun.lockb",
|
|
15
|
+
name: "bun"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
command: "pnpm add -D",
|
|
19
|
+
lockfile: "pnpm-lock.yaml",
|
|
20
|
+
name: "pnpm"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
command: "yarn add -D",
|
|
24
|
+
lockfile: "yarn.lock",
|
|
25
|
+
name: "yarn"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
command: "npm install -D",
|
|
29
|
+
lockfile: "package-lock.json",
|
|
30
|
+
name: "npm"
|
|
31
|
+
}
|
|
32
|
+
];
|
|
33
|
+
const DEFAULT_INSTALLER = {
|
|
34
|
+
command: "npm install -D",
|
|
35
|
+
name: "npm"
|
|
36
|
+
};
|
|
37
|
+
const BUILDERS = {
|
|
38
|
+
bun: (peer) => `bun pm trust ${peer}`,
|
|
39
|
+
npm: (peer) => `npm install-scripts approve ${peer} && npm rebuild ${peer}`,
|
|
40
|
+
pnpm: (peer) => `add "${peer}" to \`onlyBuiltDependencies\` in package.json (or \`only-built-dependencies\` in .npmrc), then \`pnpm rebuild ${peer}\``,
|
|
41
|
+
yarn: (peer) => `yarn rebuild ${peer}`
|
|
42
|
+
};
|
|
43
|
+
const UNBUILT = /could not locate the bindings file|\.node['"]?$|invalid elf header|was compiled against a different node/iu;
|
|
44
|
+
function isUnbuilt(error) {
|
|
45
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
46
|
+
return UNBUILT.test(message);
|
|
47
|
+
}
|
|
48
|
+
const NATIVE_PEERS = /* @__PURE__ */ new Set(["better-sqlite3"]);
|
|
49
|
+
function installerAt(from) {
|
|
50
|
+
const root = installRootOf(from);
|
|
51
|
+
if (root === void 0) return DEFAULT_INSTALLER;
|
|
52
|
+
return INSTALLERS.find((candidate) => existsSync(resolve(root, candidate.lockfile))) ?? DEFAULT_INSTALLER;
|
|
53
|
+
}
|
|
54
|
+
function buildNote(peer, manager) {
|
|
55
|
+
if (!NATIVE_PEERS.has(peer) || manager !== "pnpm") return "";
|
|
56
|
+
return `; under pnpm the binding is not built unless the package is listed: add "${peer}" to \`onlyBuiltDependencies\` in package.json (or \`only-built-dependencies\` in .npmrc) and reinstall`;
|
|
57
|
+
}
|
|
58
|
+
/** A binding the package manager never built surfaces as a `bindings` stack trace; this turns it into a refusal naming the peer and the command. */
|
|
59
|
+
function requireBuiltPeer(peer, what, use, from = process.cwd()) {
|
|
60
|
+
try {
|
|
61
|
+
use();
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (!isUnbuilt(error)) throw error;
|
|
64
|
+
const { name } = installerAt(from);
|
|
65
|
+
throw new Error(refusal({
|
|
66
|
+
fix: BUILDERS[name](peer),
|
|
67
|
+
found: `${what} found \`${peer}\`, and its native binding is not built`,
|
|
68
|
+
read: "docs/start/set-up-a-project.md#what-to-install",
|
|
69
|
+
why: `A native peer is built by its install script, and ${name} runs a dependency's install script only once it is told to.`
|
|
70
|
+
}), { cause: error });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** `load` is the caller's own `import('<peer>')`: the bundler and the resolver need to see the bare specifier. */
|
|
74
|
+
async function loadPeer(peer, what, load, from = process.cwd()) {
|
|
75
|
+
try {
|
|
76
|
+
return await load();
|
|
77
|
+
} catch (error) {
|
|
78
|
+
const { command, name } = installerAt(from);
|
|
79
|
+
throw new Error(refusal("seam-is-an-optional-peer", {
|
|
80
|
+
fix: `${command} ${peer}${buildNote(peer, name)}`,
|
|
81
|
+
found: `${what} requires \`${peer}\`, and it is not installed`
|
|
82
|
+
}), { cause: error });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export { requireBuiltPeer as n, loadPeer as t };
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=shared-peer.js.map
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { n as refusal } from "./shared-refusal.js";
|
|
2
|
+
import { u as evidenceDirectory } from "./shared-artifacts.js";
|
|
3
|
+
import { c as scopeChain } from "./shared-landmarks.js";
|
|
4
|
+
import { n as CANDIDATE_LIMITS, o as ambiguousTargetError, r as candidatesOf, s as asWritten, t as scenarioFailure } from "./shared-scenario-failure.js";
|
|
5
|
+
import { mkdtempSync } from "node:fs";
|
|
6
|
+
import { resolve } from "node:path";
|
|
7
|
+
import { setTimeout } from "node:timers/promises";
|
|
8
|
+
//#region src/specifications/website/browser/page-scripts.ts
|
|
9
|
+
function extractPage() {
|
|
10
|
+
const links = [...document.querySelectorAll("link")].map((link) => ({
|
|
11
|
+
href: link.href,
|
|
12
|
+
hreflang: link.hreflang || void 0,
|
|
13
|
+
rel: link.rel,
|
|
14
|
+
type: link.type || void 0
|
|
15
|
+
}));
|
|
16
|
+
const metas = [...document.querySelectorAll("meta")].map((meta) => ({
|
|
17
|
+
content: meta.content,
|
|
18
|
+
name: meta.name || void 0,
|
|
19
|
+
property: meta.getAttribute("property") ?? void 0
|
|
20
|
+
}));
|
|
21
|
+
const jsonLdBlocks = [...document.querySelectorAll("script[type=\"application/ld+json\"]")].map((script) => script.textContent ?? "");
|
|
22
|
+
return {
|
|
23
|
+
html: document.documentElement.outerHTML,
|
|
24
|
+
jsonLdBlocks,
|
|
25
|
+
links,
|
|
26
|
+
metas,
|
|
27
|
+
text: document.body?.innerText ?? "",
|
|
28
|
+
title: document.title
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function seedStorage(state) {
|
|
32
|
+
if (state === "throws") {
|
|
33
|
+
for (const store of ["localStorage", "sessionStorage"]) Object.defineProperty(globalThis, store, {
|
|
34
|
+
configurable: true,
|
|
35
|
+
get: () => {
|
|
36
|
+
throw new DOMException("The operation is insecure.", "SecurityError");
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
for (const [store, entries] of [["localStorage", state.local], ["sessionStorage", state.session]]) for (const [key, value] of Object.entries(entries ?? {})) try {
|
|
42
|
+
if (globalThis[store].getItem(key) === null) globalThis[store].setItem(key, value);
|
|
43
|
+
} catch {}
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/specifications/website/browser/shared-browser.ts
|
|
47
|
+
/** Launched on the first ask, again once disconnected; a failed launch is not kept. */
|
|
48
|
+
function createSharedBrowser(launch) {
|
|
49
|
+
let shared;
|
|
50
|
+
const attempt = async () => {
|
|
51
|
+
try {
|
|
52
|
+
const browser = await launch();
|
|
53
|
+
browser.on("disconnected", () => {
|
|
54
|
+
shared = void 0;
|
|
55
|
+
});
|
|
56
|
+
return browser;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
shared = void 0;
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
return async () => {
|
|
63
|
+
shared ??= attempt();
|
|
64
|
+
return await shared;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/specifications/website/browser/website-visitor.ts
|
|
69
|
+
const VALUE_TIMEOUT_MS = 3e4;
|
|
70
|
+
const VALUE_POLL_MS = 100;
|
|
71
|
+
const FIELD_ROLES = [
|
|
72
|
+
"checkbox",
|
|
73
|
+
"combobox",
|
|
74
|
+
"radio",
|
|
75
|
+
"searchbox",
|
|
76
|
+
"slider",
|
|
77
|
+
"spinbutton",
|
|
78
|
+
"switch",
|
|
79
|
+
"textbox"
|
|
80
|
+
];
|
|
81
|
+
function locate(root, target) {
|
|
82
|
+
const scope = target.scope ? locate(root, target.scope) : root;
|
|
83
|
+
const exact = target.exact ?? true;
|
|
84
|
+
const name = target.name ?? "";
|
|
85
|
+
if (target.kind === "field") {
|
|
86
|
+
let field = scope.getByLabel(name, { exact });
|
|
87
|
+
for (const role of FIELD_ROLES) field = field.or(scope.getByRole(role, {
|
|
88
|
+
exact,
|
|
89
|
+
name
|
|
90
|
+
}));
|
|
91
|
+
return field;
|
|
92
|
+
}
|
|
93
|
+
if (target.kind === "testId") return scope.getByTestId(name);
|
|
94
|
+
if (target.kind === "text") return scope.getByText(name, { exact });
|
|
95
|
+
return scope.getByRole(target.kind, target.name === void 0 ? { exact } : {
|
|
96
|
+
exact,
|
|
97
|
+
name: target.name
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function isStrictViolation(error) {
|
|
101
|
+
return error instanceof Error && error.message.includes("strict mode violation");
|
|
102
|
+
}
|
|
103
|
+
async function act(page, target, action) {
|
|
104
|
+
try {
|
|
105
|
+
return await action(locate(page, target));
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (!isStrictViolation(error)) throw error;
|
|
108
|
+
const culprit = await findAmbiguousLevel(page, target);
|
|
109
|
+
const matches = await locate(page, culprit).evaluateAll(candidatesOf, CANDIDATE_LIMITS);
|
|
110
|
+
throw ambiguousTargetError({
|
|
111
|
+
target: culprit,
|
|
112
|
+
matches,
|
|
113
|
+
url: page.url()
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async function findAmbiguousLevel(page, target) {
|
|
118
|
+
for (const level of scopeChain(target).slice(0, -1)) if (await locate(page, level).count() > 1) return level;
|
|
119
|
+
return target;
|
|
120
|
+
}
|
|
121
|
+
function stateOnly(page, locator, target) {
|
|
122
|
+
if (target.focused === true) return locator.and(page.locator(":focus"));
|
|
123
|
+
if (target.disabled !== void 0) return locator.and(page.locator(target.disabled ? ":disabled" : ":enabled"));
|
|
124
|
+
if (target.selected !== void 0) return locator.and(page.locator(target.selected ? ":checked" : ":not(:checked)"));
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
function byPresence(target) {
|
|
128
|
+
return target.kind === "option";
|
|
129
|
+
}
|
|
130
|
+
async function currentValue(locator) {
|
|
131
|
+
try {
|
|
132
|
+
return await locator.inputValue({ timeout: VALUE_POLL_MS });
|
|
133
|
+
} catch (error) {
|
|
134
|
+
if (isStrictViolation(error)) throw error;
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function waitForValue(locator, target, wanted) {
|
|
139
|
+
const value = target.value ?? "";
|
|
140
|
+
const deadline = Date.now() + VALUE_TIMEOUT_MS;
|
|
141
|
+
for (;;) {
|
|
142
|
+
const actual = await currentValue(locator);
|
|
143
|
+
if (actual === null ? !wanted : actual === value === wanted) return;
|
|
144
|
+
if (Date.now() >= deadline) throw new Error(refusal({
|
|
145
|
+
fix: "correct the value the scenario states, or what the page puts in the field",
|
|
146
|
+
found: `${asWritten(target)} ${wanted ? "never held" : "still holds"} ${JSON.stringify(value)}${actual === null ? " (it is not on the page)" : `, it holds ${JSON.stringify(actual)}`}`,
|
|
147
|
+
read: "docs/reference/visitor.md#see-and-gone-are-the-synchronization",
|
|
148
|
+
why: "A verb waits for what it states to hold, within its budget, and never sleeps."
|
|
149
|
+
}));
|
|
150
|
+
await setTimeout(VALUE_POLL_MS);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async function settle(page, locator, target, present) {
|
|
154
|
+
if (target.value !== void 0) {
|
|
155
|
+
await waitForValue(locator, target, present);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const narrowed = stateOnly(page, locator, target);
|
|
159
|
+
if (narrowed !== null) {
|
|
160
|
+
await narrowed.waitFor({ state: present ? "attached" : "detached" });
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const shown = present ? "visible" : "hidden";
|
|
164
|
+
await locator.waitFor({ state: byPresence(target) ? present ? "attached" : "detached" : shown });
|
|
165
|
+
}
|
|
166
|
+
function createVisitor(page, baseUrl) {
|
|
167
|
+
return {
|
|
168
|
+
check: async (target) => {
|
|
169
|
+
await act(page, target, async (locator) => {
|
|
170
|
+
await locator.check();
|
|
171
|
+
});
|
|
172
|
+
},
|
|
173
|
+
click: async (target) => {
|
|
174
|
+
await act(page, target, async (locator) => {
|
|
175
|
+
await locator.click();
|
|
176
|
+
});
|
|
177
|
+
},
|
|
178
|
+
fill: async (target, value) => {
|
|
179
|
+
await act(page, target, async (locator) => {
|
|
180
|
+
await locator.fill(value);
|
|
181
|
+
});
|
|
182
|
+
},
|
|
183
|
+
goto: async (path) => {
|
|
184
|
+
await page.goto(`${baseUrl}${path}`, { waitUntil: "load" });
|
|
185
|
+
},
|
|
186
|
+
hover: async (target) => {
|
|
187
|
+
await act(page, target, async (locator) => {
|
|
188
|
+
await locator.hover();
|
|
189
|
+
});
|
|
190
|
+
},
|
|
191
|
+
press: async (key) => {
|
|
192
|
+
await page.keyboard.press(key);
|
|
193
|
+
},
|
|
194
|
+
gone: async (target) => {
|
|
195
|
+
await act(page, target, async (locator) => {
|
|
196
|
+
await settle(page, locator, target, false);
|
|
197
|
+
});
|
|
198
|
+
},
|
|
199
|
+
see: async (target) => {
|
|
200
|
+
await act(page, target, async (locator) => {
|
|
201
|
+
await settle(page, locator, target, true);
|
|
202
|
+
});
|
|
203
|
+
},
|
|
204
|
+
select: async (target, option) => {
|
|
205
|
+
await act(page, target, async (locator) => await locator.selectOption(option));
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
//#endregion
|
|
210
|
+
//#region src/specifications/website/browser/playwright.adapter.ts
|
|
211
|
+
const LAUNCH_OPTIONS = {
|
|
212
|
+
channel: "chromium-headless-shell",
|
|
213
|
+
headless: true
|
|
214
|
+
};
|
|
215
|
+
async function launchChromium() {
|
|
216
|
+
let chromium;
|
|
217
|
+
try {
|
|
218
|
+
({chromium} = await import("playwright"));
|
|
219
|
+
} catch {
|
|
220
|
+
throw new Error(refusal("seam-is-an-optional-peer", {
|
|
221
|
+
fix: "npm install -D playwright && npx playwright install chromium",
|
|
222
|
+
found: ".visit() requires `playwright`, and it is not installed"
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
return await chromium.launch(LAUNCH_OPTIONS);
|
|
226
|
+
}
|
|
227
|
+
/** invariant: with `'block'`, requests leaving the site are aborted; the stub backend stays reachable through `allowedOrigins`. */
|
|
228
|
+
async function blockExternal(context, options) {
|
|
229
|
+
const aborted = /* @__PURE__ */ new Set();
|
|
230
|
+
if (options.external !== "block") return aborted;
|
|
231
|
+
const allowed = new Set([options.baseUrl, ...options.allowedOrigins ?? []].map((at) => new URL(at).origin));
|
|
232
|
+
await context.route("**/*", (route) => {
|
|
233
|
+
const url = route.request().url();
|
|
234
|
+
if (allowed.has(new URL(url).origin)) route.continue();
|
|
235
|
+
else {
|
|
236
|
+
aborted.add(url);
|
|
237
|
+
route.abort();
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
return aborted;
|
|
241
|
+
}
|
|
242
|
+
function recordConsole(page, aborted) {
|
|
243
|
+
const consoleMessages = [];
|
|
244
|
+
page.on("console", (message) => {
|
|
245
|
+
if (message.type() === "error" && aborted.has(message.location().url)) return;
|
|
246
|
+
consoleMessages.push({
|
|
247
|
+
text: message.text(),
|
|
248
|
+
type: message.type()
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
page.on("pageerror", (error) => {
|
|
252
|
+
consoleMessages.push({
|
|
253
|
+
text: `Uncaught ${error.name}: ${error.message}`,
|
|
254
|
+
type: "error"
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
return consoleMessages;
|
|
258
|
+
}
|
|
259
|
+
const workerBrowsers = /* @__PURE__ */ new Map();
|
|
260
|
+
function workerBrowser(launch) {
|
|
261
|
+
let shared = workerBrowsers.get(launch);
|
|
262
|
+
if (shared === void 0) {
|
|
263
|
+
shared = createSharedBrowser(launch);
|
|
264
|
+
workerBrowsers.set(launch, shared);
|
|
265
|
+
}
|
|
266
|
+
return shared;
|
|
267
|
+
}
|
|
268
|
+
/** One browser per worker, launched lazily; each visit gets a fresh `BrowserContext`. */
|
|
269
|
+
var PlaywrightAdapter = class {
|
|
270
|
+
browser;
|
|
271
|
+
contexts = /* @__PURE__ */ new Set();
|
|
272
|
+
constructor(options = {}) {
|
|
273
|
+
this.browser = workerBrowser(options.launch ?? launchChromium);
|
|
274
|
+
}
|
|
275
|
+
async close() {
|
|
276
|
+
const open = [...this.contexts];
|
|
277
|
+
this.contexts.clear();
|
|
278
|
+
await Promise.all(open.map(async (context) => {
|
|
279
|
+
await context.close();
|
|
280
|
+
}));
|
|
281
|
+
}
|
|
282
|
+
async open(url, options) {
|
|
283
|
+
const context = await (await this.browser()).newContext({ extraHTTPHeaders: options.headers ?? {} });
|
|
284
|
+
this.contexts.add(context);
|
|
285
|
+
try {
|
|
286
|
+
const aborted = await blockExternal(context, options);
|
|
287
|
+
if (options.storage !== void 0) await context.addInitScript(seedStorage, options.storage);
|
|
288
|
+
const page = await context.newPage();
|
|
289
|
+
if (options.clock !== void 0) await page.clock.setFixedTime(new Date(options.clock));
|
|
290
|
+
const consoleMessages = recordConsole(page, aborted);
|
|
291
|
+
const response = await page.goto(url, { waitUntil: "load" });
|
|
292
|
+
if (options.scenario) try {
|
|
293
|
+
await options.scenario(createVisitor(page, options.baseUrl));
|
|
294
|
+
} catch (error) {
|
|
295
|
+
const evidence = await this.captureEvidence(page);
|
|
296
|
+
throw scenarioFailure(error, evidence, "visit");
|
|
297
|
+
}
|
|
298
|
+
const extraction = await page.evaluate(extractPage);
|
|
299
|
+
const tree = await page.locator("body").ariaSnapshot();
|
|
300
|
+
return {
|
|
301
|
+
consoleMessages,
|
|
302
|
+
status: response?.status() ?? 0,
|
|
303
|
+
tree,
|
|
304
|
+
url: page.url(),
|
|
305
|
+
...extraction
|
|
306
|
+
};
|
|
307
|
+
} finally {
|
|
308
|
+
this.contexts.delete(context);
|
|
309
|
+
await context.close();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/** Screenshot the failing state into the evidence folder; never masks the original error. */
|
|
313
|
+
async captureEvidence(page) {
|
|
314
|
+
try {
|
|
315
|
+
const dir = mkdtempSync(evidenceDirectory("website-"));
|
|
316
|
+
const path = resolve(dir, "failure.png");
|
|
317
|
+
await page.screenshot({
|
|
318
|
+
fullPage: true,
|
|
319
|
+
path
|
|
320
|
+
});
|
|
321
|
+
return path;
|
|
322
|
+
} catch {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
//#endregion
|
|
328
|
+
export { PlaywrightAdapter };
|
|
329
|
+
|
|
330
|
+
//# sourceMappingURL=shared-playwright.adapter.js.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { t as ServedProcess } from "./shared-serve.js";
|
|
2
|
+
import { n as refusal } from "./shared-refusal.js";
|
|
3
|
+
//#region src/services/process/process.adapter.ts
|
|
4
|
+
async function nothingToIsolate() {}
|
|
5
|
+
var ProcessHandle = class {
|
|
6
|
+
/** Embedded: nothing to pull, nothing to run in a container. */
|
|
7
|
+
defaultImage = "";
|
|
8
|
+
defaultPort = 0;
|
|
9
|
+
environment = {};
|
|
10
|
+
type = "process";
|
|
11
|
+
serviceName = null;
|
|
12
|
+
connectionString = "";
|
|
13
|
+
started = false;
|
|
14
|
+
served = null;
|
|
15
|
+
options;
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.options = options;
|
|
18
|
+
}
|
|
19
|
+
buildConnectionString(host, port) {
|
|
20
|
+
return `http://${host}:${port}`;
|
|
21
|
+
}
|
|
22
|
+
createDatabaseAdapter() {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
async healthcheck() {}
|
|
26
|
+
async initialize() {}
|
|
27
|
+
/** Isolation is the run's: one child per evaluation of the specification module, told apart by the minted id. */
|
|
28
|
+
isolation() {
|
|
29
|
+
return {
|
|
30
|
+
acquire: nothingToIsolate,
|
|
31
|
+
release: nothingToIsolate,
|
|
32
|
+
reset: nothingToIsolate
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
async reset() {}
|
|
36
|
+
/** Spawn it and wait until it answers; `services` is every sibling, already started. */
|
|
37
|
+
async spawnWith(root, services, runId) {
|
|
38
|
+
const stated = typeof this.options.env === "function" ? this.options.env(services) : this.options.env;
|
|
39
|
+
this.served = new ServedProcess(this.options, root, "process", {
|
|
40
|
+
...stated,
|
|
41
|
+
TEST_RUN_ID: runId
|
|
42
|
+
});
|
|
43
|
+
this.connectionString = await this.served.start();
|
|
44
|
+
this.started = true;
|
|
45
|
+
}
|
|
46
|
+
/** Terminate the process group. Idempotent. */
|
|
47
|
+
async shutdown() {
|
|
48
|
+
await this.served?.stop();
|
|
49
|
+
this.served = null;
|
|
50
|
+
this.started = false;
|
|
51
|
+
}
|
|
52
|
+
/** What it was declared as: what a specification reads to start it in a slot of its own. */
|
|
53
|
+
get spec() {
|
|
54
|
+
return this.options;
|
|
55
|
+
}
|
|
56
|
+
/** The base URL it came up on, known only once it is started. */
|
|
57
|
+
get url() {
|
|
58
|
+
if (!this.started) throw new Error(refusal({
|
|
59
|
+
fix: "read it from the started services record (`env: (services) => ...` or `server: (services) => ...`), never at declaration time",
|
|
60
|
+
found: "process().url read before the process was ready",
|
|
61
|
+
read: "docs/reference/services.md#process-the-one-shape-an-external-process-takes",
|
|
62
|
+
why: "A process's URL is known once it is ready."
|
|
63
|
+
}));
|
|
64
|
+
return this.connectionString;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
/** Declare an external process the framework owns for the life of the specification. */
|
|
68
|
+
function processService(options) {
|
|
69
|
+
return new ProcessHandle(options);
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
export { processService as n, ProcessHandle as t };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=shared-process.adapter.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { t as CATALOGUE_CHAPTER } from "./shared-chapter.js";
|
|
2
|
+
import { defineInvariant, refusalBlock } from "@jterrazz/typescript/rules";
|
|
3
|
+
//#region src/chain/refusals/rows.ts
|
|
4
|
+
const REFUSAL_WHY = {
|
|
5
|
+
"database-named-only-when-several-at-runtime": "With two SQL databases or more, a seed or a table names the `database:` it reads, and with exactly one it names none.",
|
|
6
|
+
"route-written-as-template-at-runtime": "A request written as its route template is handed exactly the parameters the template names.",
|
|
7
|
+
"disk-named-by-relative-path-at-runtime": "A verb that reads the disk takes a `file()` or a `directory()`, whose path starts with `./` or `../` and ends in `/` exactly when it names a directory.",
|
|
8
|
+
"one-verb-per-meaning": "`toMatch` on a result the framework produced compares the WHOLE result, to a value written in place or to a `file()`/`directory()` golden.",
|
|
9
|
+
"network-strict-after-first-stub": "From its first stub, a chain's network is strict: every outgoing request matches a declared stub.",
|
|
10
|
+
"target-matches-one-element": "A verb acts on exactly ONE element: a target that matches several is refused, never settled by the first.",
|
|
11
|
+
"seam-is-an-optional-peer": "Every seam is an optional peer of @jterrazz/test, installed by the project that specifies through it.",
|
|
12
|
+
"no-landmark-on-mobile": "An iOS screen has no ARIA landmark: a mobile verb is scoped with `within(testId(...), ...)`."
|
|
13
|
+
};
|
|
14
|
+
`${REFUSAL_WHY["route-written-as-template-at-runtime"]}`, `${REFUSAL_WHY["database-named-only-when-several-at-runtime"]}`, `${REFUSAL_WHY["disk-named-by-relative-path-at-runtime"]}`, `${REFUSAL_WHY["network-strict-after-first-stub"]}`, `${REFUSAL_WHY["one-verb-per-meaning"]}`, `${REFUSAL_WHY["target-matches-one-element"]}`, `${REFUSAL_WHY["no-landmark-on-mobile"]}`, `${REFUSAL_WHY["seam-is-an-optional-peer"]}`;
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/chain/refusals/refusal.ts
|
|
17
|
+
const invariant = defineInvariant({
|
|
18
|
+
bugOf: "the framework",
|
|
19
|
+
notOf: "the test",
|
|
20
|
+
package: "@jterrazz/test"
|
|
21
|
+
});
|
|
22
|
+
function refusal$1(rowOrLesson, teaching) {
|
|
23
|
+
if (typeof rowOrLesson !== "string") return refusalBlock(rowOrLesson);
|
|
24
|
+
if (teaching === void 0) throw invariant(`refusal('${rowOrLesson}') was handed no found and no fix for its case`);
|
|
25
|
+
return refusalBlock({
|
|
26
|
+
fix: teaching.fix,
|
|
27
|
+
found: teaching.found,
|
|
28
|
+
read: `${CATALOGUE_CHAPTER}#${rowOrLesson}`,
|
|
29
|
+
why: REFUSAL_WHY[rowOrLesson]
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { refusal$1 as n, invariant as t };
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=shared-refusal.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { holdsManifest, nearestRoot } from "@jterrazz/typescript/rules";
|
|
2
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
//#region lint/kit/project/resolve.ts
|
|
5
|
+
const installRootOf = (from) => nearestRoot(from, (directory) => existsSync(join(directory, "package.json")));
|
|
6
|
+
function discoverRoot(startDir) {
|
|
7
|
+
return nearestRoot(startDir, holdsManifest) ?? startDir;
|
|
8
|
+
}
|
|
9
|
+
function resolveRoot(root, callerDir) {
|
|
10
|
+
if (root === void 0 || root === "") return discoverRoot(callerDir);
|
|
11
|
+
return resolve(callerDir, root);
|
|
12
|
+
}
|
|
13
|
+
/** What a child process prints as `$PWD` (macOS resolves the tmpdir symlink); the path itself when it cannot resolve. */
|
|
14
|
+
function safeRealpath(path) {
|
|
15
|
+
try {
|
|
16
|
+
return realpathSync(path);
|
|
17
|
+
} catch {
|
|
18
|
+
return path;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { safeRealpath as i, installRootOf as n, resolveRoot as r, discoverRoot as t };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=shared-resolve.js.map
|