@smthrs/kernel 1.0.0-rc.5 → 1.0.0-rc.7
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 +18 -18
- package/dist/cjs/ChildProcessSpawner.d.ts +4 -4
- package/dist/cjs/ChildProcessSpawner.d.ts.map +1 -1
- package/dist/cjs/ChildProcessSpawner.js +4 -4
- package/dist/cjs/ChildProcessSpawner.js.map +1 -1
- package/dist/cjs/CommandLine.d.ts +2 -2
- package/dist/cjs/CommandLine.d.ts.map +1 -1
- package/dist/cjs/CommandLine.js.map +1 -1
- package/dist/cjs/ContainedSpawner.d.ts +2 -2
- package/dist/cjs/ContainedSpawner.d.ts.map +1 -1
- package/dist/cjs/ContainedSpawner.js +2 -2
- package/dist/cjs/ContainedSpawner.js.map +2 -2
- package/dist/cjs/FileSystem.d.ts.map +1 -1
- package/dist/cjs/FileSystem.js +4 -3
- package/dist/cjs/FileSystem.js.map +2 -2
- package/dist/cjs/HostServices.d.ts +3 -3
- package/dist/cjs/HostServices.d.ts.map +1 -1
- package/dist/cjs/HostServices.js +2 -2
- package/dist/cjs/HostServices.js.map +1 -1
- package/dist/cjs/HttpClient.d.ts +5 -5
- package/dist/cjs/HttpClient.d.ts.map +1 -1
- package/dist/cjs/HttpClient.js +9 -9
- package/dist/cjs/HttpClient.js.map +1 -1
- package/dist/cjs/ProcessConfinement.d.ts +1 -1
- package/dist/cjs/ProcessConfinement.d.ts.map +1 -1
- package/dist/cjs/ProcessConfinement.js.map +1 -1
- package/dist/cjs/Rooted.d.ts +2 -2
- package/dist/cjs/Rooted.d.ts.map +1 -1
- package/dist/cjs/Rooted.js +2 -2
- package/dist/cjs/Rooted.js.map +1 -1
- package/dist/cjs/internal/Containment.d.ts +1 -1
- package/dist/cjs/internal/Containment.d.ts.map +1 -1
- package/dist/cjs/internal/Containment.js.map +1 -1
- package/dist/cjs/internal/Pipeline.d.ts +2 -2
- package/dist/cjs/internal/Pipeline.d.ts.map +1 -1
- package/dist/cjs/internal/Pipeline.js +2 -2
- package/dist/cjs/internal/Pipeline.js.map +2 -2
- package/dist/cjs/test/HostContract.d.ts +5 -5
- package/dist/cjs/test/HostContract.d.ts.map +1 -1
- package/dist/cjs/test/HostContract.js +4 -4
- package/dist/cjs/test/HostContract.js.map +1 -1
- package/dist/esm/ChildProcessSpawner.d.ts +4 -4
- package/dist/esm/ChildProcessSpawner.d.ts.map +1 -1
- package/dist/esm/ChildProcessSpawner.js +5 -5
- package/dist/esm/ChildProcessSpawner.js.map +1 -1
- package/dist/esm/CommandLine.d.ts +2 -2
- package/dist/esm/CommandLine.d.ts.map +1 -1
- package/dist/esm/CommandLine.js +1 -1
- package/dist/esm/CommandLine.js.map +1 -1
- package/dist/esm/ContainedSpawner.d.ts +2 -2
- package/dist/esm/ContainedSpawner.d.ts.map +1 -1
- package/dist/esm/ContainedSpawner.js +2 -2
- package/dist/esm/ContainedSpawner.js.map +1 -1
- package/dist/esm/FileSystem.d.ts.map +1 -1
- package/dist/esm/FileSystem.js +5 -4
- package/dist/esm/FileSystem.js.map +1 -1
- package/dist/esm/HostServices.d.ts +3 -3
- package/dist/esm/HostServices.d.ts.map +1 -1
- package/dist/esm/HostServices.js +2 -2
- package/dist/esm/HostServices.js.map +1 -1
- package/dist/esm/HttpClient.d.ts +5 -5
- package/dist/esm/HttpClient.d.ts.map +1 -1
- package/dist/esm/HttpClient.js +9 -9
- package/dist/esm/HttpClient.js.map +1 -1
- package/dist/esm/ProcessConfinement.d.ts +1 -1
- package/dist/esm/ProcessConfinement.d.ts.map +1 -1
- package/dist/esm/ProcessConfinement.js.map +1 -1
- package/dist/esm/Rooted.d.ts +2 -2
- package/dist/esm/Rooted.d.ts.map +1 -1
- package/dist/esm/Rooted.js +2 -2
- package/dist/esm/Rooted.js.map +1 -1
- package/dist/esm/internal/Containment.d.ts +1 -1
- package/dist/esm/internal/Containment.d.ts.map +1 -1
- package/dist/esm/internal/Containment.js.map +1 -1
- package/dist/esm/internal/Pipeline.d.ts +2 -2
- package/dist/esm/internal/Pipeline.d.ts.map +1 -1
- package/dist/esm/internal/Pipeline.js +2 -2
- package/dist/esm/internal/Pipeline.js.map +1 -1
- package/dist/esm/test/HostContract.d.ts +5 -5
- package/dist/esm/test/HostContract.d.ts.map +1 -1
- package/dist/esm/test/HostContract.js +4 -4
- package/dist/esm/test/HostContract.js.map +1 -1
- package/docs/api.md +2 -2
- package/docs/guides/adapt-a-new-host-platform.md +3 -3
- package/docs/installation.md +3 -3
- package/docs/quickstart.md +1 -1
- package/docs/testing.md +1 -1
- package/package.json +10 -11
- package/src/ChildProcessSpawner.ts +5 -5
- package/src/CommandLine.ts +2 -2
- package/src/ContainedSpawner.ts +2 -6
- package/src/FileSystem.ts +4 -4
- package/src/HostServices.ts +3 -3
- package/src/HttpClient.ts +9 -9
- package/src/ProcessConfinement.ts +1 -1
- package/src/Rooted.ts +2 -2
- package/src/internal/Containment.ts +1 -1
- package/src/internal/Pipeline.ts +2 -2
- package/src/test/HostContract.ts +5 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/test/HostContract.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Shared contract suite for complete Host bundles.\n *\n * This module is emitted as ESM, CJS, and declarations and exported from\n * `@smthrs/kernel/test/contract`. It intentionally has a Vitest peer because\n * it registers a reusable behavioral contract for third-party Host bundles.\n *\n * @since 1.0.0-rc.0\n */\n\n// Every case here runs on real elapsed time \u2014 subprocess spawns, file locks,\n// mtimes, and poll loops \u2014 so the suite uses `it.live`; `it.effect`'s\n// TestClock never advances for them.\n\nimport { describe, expect, it } from \"@effect/vitest\"\nimport * as JjService from \"@smthrs/jj\"\nimport type { Jj, JjErrorCode } from \"@smthrs/jj\"\nimport { Deferred, Effect, Fiber, FileSystem, type Layer, Option, Path, Stream } from \"effect\"\nimport { HttpClient } from \"effect/unstable/http/HttpClient\"\nimport * as HttpClientRequest from \"effect/unstable/http/HttpClientRequest\"\nimport type * as HttpClientResponse from \"effect/unstable/http/HttpClientResponse\"\nimport * as ChildProcess from \"effect/unstable/process/ChildProcess\"\nimport { type ChildProcessHandle, ChildProcessSpawner } from \"effect/unstable/process/ChildProcessSpawner\"\nimport { tmpdir } from \"node:os\"\nimport { join } from \"node:path\"\nimport * as FileSystemBatchContract from \"./FileSystemBatchContract.ts\"\n\nexport { check as checkFileSystemBatch } from \"./FileSystemBatchContract.ts\"\n\n/**\n * A capability that must fail with a stable typed code.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface FailureCapability<Code extends string> {\n readonly expected: \"failure\"\n readonly code: Code\n}\n\n/**\n * Successful filesystem contract options.\n *\n * `scratchPath` is required only for hosts whose filesystem does not accept an\n * OS temp path \u2014 an in-memory double, say. Omitting it takes\n * {@link defaultScratchPath}, which is process-scoped and outside the working\n * tree.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface FileSystemSuccess {\n readonly expected: \"success\"\n readonly scratchPath?: string | undefined\n /** Operations this otherwise available host deliberately refuses. */\n readonly unsupported?: Partial<Record<FileSystemOperation, string>> | undefined\n}\n\n/**\n * Every method on Effect's closed filesystem service.\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const FileSystemOperations = [\n \"access\",\n \"copy\",\n \"copyFile\",\n \"chmod\",\n \"chown\",\n \"glob\",\n \"exists\",\n \"link\",\n \"makeDirectory\",\n \"makeTempDirectory\",\n \"makeTempDirectoryScoped\",\n \"makeTempFile\",\n \"makeTempFileScoped\",\n \"open\",\n \"readDirectory\",\n \"readFile\",\n \"readFileString\",\n \"readLink\",\n \"realPath\",\n \"remove\",\n \"rename\",\n \"sink\",\n \"stat\",\n \"stream\",\n \"symlink\",\n \"truncate\",\n \"utimes\",\n \"watch\",\n \"writeFile\",\n \"writeFileString\"\n] as const\n\n/**\n * One method of Effect's closed filesystem service.\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type FileSystemOperation = typeof FileSystemOperations[number]\n\n/**\n * Successful Path contract expectation.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface PathSuccess {\n readonly expected: \"success\"\n}\n\n/**\n * Successful `ChildProcessSpawner` contract options.\n *\n * Defaults use the current Node or Bun executable. In-process browser\n * doubles can provide their own scripted commands and expected output.\n *\n * There is no timeout case: a wall-clock budget is `Effect.timeout` around any\n * effect, not a per-spawn option, so there is nothing host-specific left to\n * assert.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface ChildProcessSuccess {\n readonly expected: \"success\"\n readonly execCommand?: ChildProcess.Command | undefined\n readonly expectedStdout?: string | undefined\n readonly streamCommand?: ChildProcess.Command | undefined\n readonly expectedStreamText?: string | undefined\n /** A command carrying `cwd` and `env` options through to the child. */\n readonly optionsCommand?: ChildProcess.Command | undefined\n readonly expectedOptionsStdout?: string | undefined\n /**\n * How the host handles a command fed from a `stdin` stream: the expected\n * stdout when it supports one, or the typed code when it does not.\n */\n readonly stdin?:\n | { readonly command: ChildProcess.Command; readonly expectedStdout: string }\n | FailureCapability<string>\n | undefined\n /** A two-leg pipeline, or the exact typed refusal for hosts without one. */\n readonly pipeline?:\n | { readonly command: ChildProcess.Command; readonly expectedStdout: string }\n | FailureCapability<string>\n | undefined\n readonly interruptCommand?: ChildProcess.Command | undefined\n}\n\n/**\n * Successful Jj contract expectation.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface JjSuccess {\n readonly expected: \"success\"\n /** Makes the first and second snapshots observably different. */\n readonly prepareChange?: ((phase: \"first\" | \"second\") => Effect.Effect<void, unknown>) | undefined\n /** Nonexistent path at which the contract may add one temporary workspace. */\n readonly workspacePath?: string | undefined\n /** Path whose repository root the host must resolve. */\n readonly rootFrom?: string | undefined\n /** Optional methods this otherwise available backend explicitly refuses. */\n readonly unsupported?: Partial<Record<\"root\" | \"revert\", JjErrorCode>> | undefined\n}\n\n/**\n * Every method on the closed Jj host service.\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const JjOperations = [\n \"snapshot\",\n \"restore\",\n \"diff\",\n \"workspaceAdd\",\n \"workspaceForget\",\n \"status\",\n \"root\",\n \"revert\"\n] as const\n\n/**\n * One method of the closed Jj host service.\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type JjOperation = typeof JjOperations[number]\n\n/**\n * Successful HTTP contract probe.\n *\n * A request is explicit so the shared suite never invents a live network call.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface HttpClientProbe {\n /** Exact request the host must execute. */\n readonly request: HttpClientRequest.HttpClientRequest\n /** Adapter-specific response proof. */\n readonly assertResponse: (response: HttpClientResponse.HttpClientResponse) => void\n}\n\n/**\n * Successful read/write/redirect HTTP contract probes.\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface HttpClientSuccess {\n readonly expected: \"success\"\n /** Safe read method. */\n readonly read: HttpClientProbe\n /** Body-carrying write method. */\n readonly write: HttpClientProbe\n /** Manual redirect response; the host must not follow it below the guard. */\n readonly redirect: HttpClientProbe\n}\n\n/**\n * Complete capability expectations for the closed five-tag Host surface.\n * Unsupported capabilities are asserted with their code and are never skipped.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface HostContractCapabilities {\n readonly fileSystem: FileSystemSuccess | FailureCapability<string>\n readonly path: PathSuccess | FailureCapability<string>\n readonly childProcess: ChildProcessSuccess | FailureCapability<string>\n readonly jj: JjSuccess | FailureCapability<JjErrorCode>\n readonly httpClient: HttpClientSuccess | FailureCapability<string>\n}\n\n/**\n * The full layer output required by the contract.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type HostContractLayer = Layer.Layer<\n FileSystem.FileSystem | Path.Path | ChildProcessSpawner | Jj | HttpClient,\n unknown\n>\n\n/**\n * Normalizes the code a Host failure is identified by, across the three shapes\n * the closed surface produces: a `code` field (`JjError`), a nested\n * `reason._tag` (`PlatformError`, `HttpClientError`), and a bare `_tag`.\n * Anything else is uncoded.\n *\n * @category testing\n * @since 1.0.0-rc.0\n */\nexport const errorCode = (error: unknown): string | undefined => {\n if (typeof error !== \"object\" || error === null) return undefined\n if (\"code\" in error && typeof error.code === \"string\") return error.code\n if (\n \"reason\" in error &&\n typeof error.reason === \"object\" &&\n error.reason !== null &&\n \"_tag\" in error.reason &&\n typeof error.reason._tag === \"string\"\n ) {\n return error.reason._tag\n }\n if (\"_tag\" in error && typeof error._tag === \"string\") return error._tag\n return undefined\n}\n\n/**\n * Asserts that `effect` fails with `code`. Succeeding is itself a contract\n * violation: a capability declared unsupported must never quietly work.\n *\n * @category testing\n * @since 1.0.0-rc.0\n */\nexport const assertFailure = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n code: string\n): Effect.Effect<void, never, R> =>\n effect.pipe(\n Effect.match({\n onFailure: (error) => {\n expect(errorCode(error)).toBe(code)\n },\n onSuccess: () => {\n throw new Error(`expected typed failure ${code}`)\n }\n })\n )\n\nlet scratchSeq = 0\n\n/**\n * Allocates the scratch file path a contract bundle writes through when the\n * caller declares no `scratchPath` of its own.\n *\n * The path is absolute under the OS temp directory and scoped by both process\n * id and an allocation counter. A repo-relative default would put the file in\n * the working tree, where it is neither ignored by git nor private to one test\n * process: concurrent `vitest run` invocations would then race the same path,\n * and the suite's `ensuring` remove could truncate a sibling's file between its\n * write and its read.\n *\n * @category testing\n * @since 1.0.0-rc.0\n */\nexport const defaultScratchPath = (suite: string): string => {\n const slug = suite.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-+|-+$/g, \"\") || \"host\"\n // Ambient `process.pid` on purpose: this is a Node-only test harness that\n // must name a path unique to THIS operating-system process before any layer\n // exists to ask. Injecting it would only move the same read one frame out.\n return join(tmpdir(), `flows-host-contract-${process.pid}-${++scratchSeq}-${slug}`)\n}\n\n/** Everything a {@link HostContractLayer} provides; a case may use nothing else. */\ntype HostServices = Layer.Success<HostContractLayer>\n\nconst provide = <A, E>(\n effect: Effect.Effect<A, E, HostServices>,\n layer: HostContractLayer\n): Effect.Effect<A, E | unknown> => effect.pipe(Effect.provide(layer))\n\nconst run = <A, E>(effect: Effect.Effect<A, E, HostServices>, layer: HostContractLayer) => provide(effect, layer)\n\nconst unsupported = ChildProcess.make(\"host-contract-unsupported\")\n\nconst unsupportedChildProcess = (operation: \"string\" | \"stream\", code: string) =>\n Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n yield* operation === \"stream\"\n ? assertFailure(Stream.runDrain(spawner.streamString(unsupported)), code)\n : assertFailure(spawner.string(unsupported), code)\n })\n\nconst fileSystemProbe = (\n fs: FileSystem.FileSystem,\n operation: FileSystemOperation,\n root: string\n): Effect.Effect<unknown, unknown> => {\n const at = (name: string): string => `${root}/${operation}-${name}`\n const source = `${root}/source.txt`\n const bytes = new TextEncoder().encode(\"host-contract\")\n switch (operation) {\n case \"access\":\n return fs.access(source)\n case \"copy\":\n return fs.copy(source, at(\"copy.txt\"))\n case \"copyFile\":\n return fs.copyFile(source, at(\"copy-file.txt\"))\n case \"chmod\":\n return fs.chmod(source, 0o600)\n case \"chown\":\n return Effect.flatMap(fs.stat(source), (info) =>\n fs.chown(\n source,\n Option.getOrElse(info.uid, () => 0),\n Option.getOrElse(info.gid, () => 0)\n ))\n case \"glob\":\n return fs.glob(\"**/*.txt\", { root })\n case \"exists\":\n return Effect.tap(fs.exists(source), (exists) => Effect.sync(() => expect(exists).toBe(true)))\n case \"link\":\n return fs.link(source, at(\"hard-link.txt\"))\n case \"makeDirectory\":\n return fs.makeDirectory(at(\"directory\"), { recursive: true })\n case \"makeTempDirectory\":\n return fs.makeTempDirectory({ directory: root })\n case \"makeTempDirectoryScoped\":\n return Effect.scoped(fs.makeTempDirectoryScoped({ directory: root }))\n case \"makeTempFile\":\n return fs.makeTempFile({ directory: root })\n case \"makeTempFileScoped\":\n return Effect.scoped(fs.makeTempFileScoped({ directory: root }))\n case \"open\":\n return Effect.scoped(fs.open(source, { flag: \"r\" }))\n case \"readDirectory\":\n return Effect.tap(fs.readDirectory(root), (entries) =>\n Effect.sync(() => {\n expect(Array.isArray(entries)).toBe(true)\n expect(entries.every((entry) => typeof entry === \"string\")).toBe(true)\n }))\n case \"readFile\":\n return Effect.gen(function*() {\n const first = yield* fs.readFile(source)\n expect(first).toBeInstanceOf(Uint8Array)\n const original = first[0]\n expect(original).toBeDefined()\n first[0] = original! ^ 0xff\n const second = yield* fs.readFile(source)\n expect(second[0]).toBe(original)\n })\n case \"readFileString\":\n return fs.readFileString(source)\n case \"readLink\": {\n const link = at(\"symbolic-link.txt\")\n return fs.symlink(source, link).pipe(Effect.andThen(fs.readLink(link)))\n }\n case \"realPath\":\n return fs.realPath(source)\n case \"remove\": {\n const path = at(\"remove.txt\")\n return fs.writeFile(path, bytes).pipe(Effect.andThen(fs.remove(path)))\n }\n case \"rename\": {\n const from = at(\"rename-from.txt\")\n return fs.writeFile(from, bytes).pipe(Effect.andThen(fs.rename(from, at(\"rename-to.txt\"))))\n }\n case \"sink\":\n return Stream.run(Stream.succeed(bytes), fs.sink(at(\"sink.txt\")))\n case \"stat\":\n return Effect.tap(fs.stat(source), (info) =>\n Effect.sync(() => {\n expect(typeof info.size).toBe(\"bigint\")\n expect(typeof info.type).toBe(\"string\")\n }))\n case \"stream\":\n return Stream.runForEach(\n fs.stream(source),\n (chunk) => Effect.sync(() => expect(chunk).toBeInstanceOf(Uint8Array))\n )\n case \"symlink\":\n return fs.symlink(source, at(\"symlink.txt\"))\n case \"truncate\": {\n const path = at(\"truncate.txt\")\n return fs.writeFile(path, bytes).pipe(Effect.andThen(fs.truncate(path, 1)))\n }\n case \"utimes\":\n return fs.utimes(source, new Date(0), new Date(0))\n case \"watch\":\n return Effect.gen(function*() {\n const watched = yield* Stream.runHead(fs.watch(root)).pipe(\n Effect.timeout(\"5 seconds\"),\n Effect.forkChild({ startImmediately: true })\n )\n // Keep producing events until the watcher observes one: subscribing\n // can take longer than any fixed initial delay. The foreign Promise\n // also works when the supplied host provides a frozen TestClock.\n yield* Effect.raceFirst(\n Fiber.join(watched),\n fs.writeFile(at(\"watched.txt\"), bytes).pipe(\n Effect.andThen(Effect.promise(() => new Promise((resolve) => setTimeout(resolve, 10)))),\n Effect.forever\n )\n )\n })\n case \"writeFile\":\n return Effect.gen(function*() {\n const input = bytes.slice()\n const expected = input[0]\n expect(expected).toBeDefined()\n yield* fs.writeFile(at(\"write.txt\"), input)\n input[0] = expected! ^ 0xff\n expect((yield* fs.readFile(at(\"write.txt\")))[0]).toBe(expected)\n })\n case \"writeFileString\":\n return fs.writeFileString(at(\"write-string.txt\"), \"host-contract\")\n }\n}\n\n/** The default stdin probe: echo back one line read from the child's stdin. */\nconst defaultStdinCommand = ChildProcess.make(\n process.execPath,\n [\n \"-e\",\n `let input = \"\"; process.stdin.setEncoding(\"utf8\"); process.stdin.on(\"data\", chunk => input += chunk); process.stdin.on(\"end\", () => process.stdout.write(input.replace(/\\\\r?\\\\n$/, \"\")));`\n ],\n { stdin: Stream.fromArray([new TextEncoder().encode(\"stdin\\n\")]) }\n)\n\n/** The default two-leg process probe. */\nconst defaultPipelineCommand = ChildProcess.pipeTo(\n ChildProcess.make(process.execPath, [\"-e\", `process.stdout.write(\"host-contract-pipeline\")`]),\n ChildProcess.make(process.execPath, [\"-e\", \"process.stdin.pipe(process.stdout)\"])\n)\n\n/** The default multi-leg cancellation probe. */\nconst defaultInterruptCommand = ChildProcess.pipeTo(\n ChildProcess.make(process.execPath, [\"-e\", \"setTimeout(() => {}, 10_000)\"]),\n ChildProcess.make(process.execPath, [\"-e\", \"process.stdin.pipe(process.stdout)\"])\n)\n\n/**\n * Registers the shared Host contract with Vitest.\n *\n * Every invocation creates ten cases: complete service presence,\n * FileSystem, Path, five child-process lifecycle cases, Jj, and\n * HttpClient.\n *\n * @category testing\n * @since 1.0.0-rc.0\n */\nexport const runHostContract = (\n name: string,\n layer: HostContractLayer,\n caps: HostContractCapabilities\n): void => {\n const fileSystemCap = caps.fileSystem\n const pathCap = caps.path\n const childProcessCap = caps.childProcess\n const jjCap = caps.jj\n const httpClientCap = caps.httpClient\n const scratchPath = fileSystemCap.expected === \"success\"\n ? fileSystemCap.scratchPath ?? defaultScratchPath(name)\n : \"\"\n\n describe(`${name} Host contract`, () => {\n it.live(\"provides every tag in the closed Host service list\", () =>\n run(\n Effect.gen(function*() {\n yield* FileSystem.FileSystem\n yield* Path.Path\n yield* ChildProcessSpawner\n yield* JjService.Jj\n yield* HttpClient\n }),\n layer\n ))\n\n it.live(\"declares every FileSystem operation\", () =>\n run(\n fileSystemCap.expected === \"failure\"\n ? Effect.gen(function*() {\n const fs = yield* FileSystem.FileSystem\n yield* Effect.forEach(\n FileSystemOperations,\n (operation) =>\n assertFailure(\n fileSystemProbe(fs, operation, \"/host-contract/unsupported\"),\n fileSystemCap.code\n ),\n { discard: true }\n )\n })\n : Effect.gen(function*() {\n const fs = yield* FileSystem.FileSystem\n const root = scratchPath\n const bytes = new TextEncoder().encode(\"host-contract\")\n yield* Effect.gen(function*() {\n yield* fs.makeDirectory(root, { recursive: true })\n yield* fs.writeFile(`${root}/source.txt`, bytes)\n yield* FileSystemBatchContract.check(fs, root)\n yield* Effect.forEach(\n FileSystemOperations,\n (operation) => {\n const probe = fileSystemProbe(fs, operation, root)\n const code = fileSystemCap.unsupported?.[operation]\n return code === undefined ? probe : assertFailure(probe, code)\n },\n { discard: true }\n )\n }).pipe(\n Effect.ensuring(fs.remove(root, { recursive: true, force: true }).pipe(Effect.ignore))\n )\n }),\n layer\n ))\n\n it.live(\"declares Path behavior\", () =>\n run(\n pathCap.expected === \"failure\"\n ? Effect.gen(function*() {\n const path = yield* Path.Path\n yield* assertFailure(\n Effect.try({\n try: () => path.fromFileUrl(new URL(\"file:///host-contract/value\")),\n catch: (error) => error\n }),\n pathCap.code\n )\n })\n : Effect.gen(function*() {\n const path = yield* Path.Path\n expect(path.normalize(\"/host-contract/./nested/../value\")).toBe(`${path.sep}host-contract${path.sep}value`)\n }),\n layer\n ))\n\n it.live(\"declares buffered child-process behavior\", () =>\n run(\n childProcessCap.expected === \"failure\"\n ? unsupportedChildProcess(\"string\", childProcessCap.code)\n : Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n const command = childProcessCap.execCommand ??\n ChildProcess.make(process.execPath, [\"-e\", `process.stdout.write(\"host-contract\")`])\n expect(yield* spawner.string(command)).toBe(childProcessCap.expectedStdout ?? \"host-contract\")\n expect(yield* spawner.exitCode(command)).toBe(0)\n }),\n layer\n ))\n\n it.live(\"declares child-process streaming behavior\", () =>\n run(\n childProcessCap.expected === \"failure\"\n ? unsupportedChildProcess(\"stream\", childProcessCap.code)\n : Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n const output = yield* Stream.mkString(\n spawner.streamString(\n childProcessCap.streamCommand ??\n ChildProcess.make(process.execPath, [\"-e\", `process.stdout.write(\"host-contract-stream\")`])\n )\n )\n expect(output).toContain(childProcessCap.expectedStreamText ?? \"host-contract-stream\")\n }),\n layer\n ))\n\n it.live(\"declares child-process cwd and env option behavior\", () =>\n run(\n childProcessCap.expected === \"failure\"\n ? unsupportedChildProcess(\"string\", childProcessCap.code)\n : Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n const output = yield* spawner.string(\n childProcessCap.optionsCommand ??\n ChildProcess.make(process.execPath, [\"-e\", \"process.stdout.write(process.env.HOST_CONTRACT_ENV)\"], {\n cwd: tmpdir(),\n env: { HOST_CONTRACT_ENV: \"env\" },\n extendEnv: true\n })\n )\n expect(output).toBe(childProcessCap.expectedOptionsStdout ?? \"env\")\n }),\n layer\n ))\n\n it.live(\"declares child-process stdin behavior\", () =>\n run(\n Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n const stdinCap = childProcessCap.expected === \"failure\"\n ? childProcessCap\n : childProcessCap.stdin ?? { command: defaultStdinCommand, expectedStdout: \"stdin\" }\n if (\"expected\" in stdinCap) {\n yield* assertFailure(spawner.string(defaultStdinCommand), stdinCap.code)\n return\n }\n expect(yield* spawner.string(stdinCap.command)).toBe(stdinCap.expectedStdout)\n }),\n layer\n ))\n\n it.live(\"declares child-process pipeline behavior\", () =>\n run(\n Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n const pipelineCap = childProcessCap.expected === \"failure\"\n ? childProcessCap\n : childProcessCap.pipeline ?? {\n command: defaultPipelineCommand,\n expectedStdout: \"host-contract-pipeline\"\n }\n if (\"expected\" in pipelineCap) {\n yield* assertFailure(spawner.string(defaultPipelineCommand), pipelineCap.code)\n return\n }\n expect(yield* spawner.string(pipelineCap.command)).toBe(pipelineCap.expectedStdout)\n }),\n layer\n ))\n\n it.live(\"declares child-process interruption behavior\", () =>\n run(\n childProcessCap.expected === \"failure\"\n ? unsupportedChildProcess(\"stream\", childProcessCap.code)\n : Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n const ready = yield* Deferred.make<ChildProcessHandle>()\n const fiber = yield* Effect.scoped(\n Effect.gen(function*() {\n const handle = yield* spawner.spawn(\n childProcessCap.interruptCommand ?? defaultInterruptCommand\n )\n yield* Deferred.succeed(ready, handle)\n yield* handle.exitCode\n })\n ).pipe(\n Effect.forkChild({ startImmediately: true })\n )\n const handle = yield* Deferred.await(ready)\n expect(yield* handle.isRunning).toBe(true)\n yield* Fiber.interrupt(fiber)\n expect(yield* handle.isRunning).toBe(false)\n }),\n layer\n ))\n\n it.live(\"declares every Jj operation\", () =>\n run(\n jjCap.expected === \"failure\"\n ? Effect.gen(function*() {\n const jj = yield* JjService.Jj\n const root = jj.root\n const revert = jj.revert\n expect(root).toBeTypeOf(\"function\")\n expect(revert).toBeTypeOf(\"function\")\n yield* Effect.forEach([\n jj.snapshot(\"host contract\"),\n jj.restore(\"host-contract-change\"),\n jj.diff(\"host-contract-from\", \"host-contract-to\"),\n jj.workspaceAdd(\"host-contract\", \"/host-contract/workspace\", \"host-contract-change\"),\n jj.workspaceForget(\"host-contract\"),\n jj.status(),\n root!(\"/host-contract\"),\n revert!(\"host-contract-change\")\n ], (probe) =>\n assertFailure(probe, jjCap.code), { discard: true })\n })\n : Effect.gen(function*() {\n const jj = yield* JjService.Jj\n const root = jj.root\n const revert = jj.revert\n expect(root).toBeTypeOf(\"function\")\n expect(revert).toBeTypeOf(\"function\")\n yield* (jjCap.prepareChange?.(\"first\") ?? Effect.void)\n const first = yield* jj.snapshot(\"host contract first\")\n expect(first.changeId.length).toBeGreaterThan(0)\n yield* (jjCap.prepareChange?.(\"second\") ?? Effect.void)\n const second = yield* jj.snapshot(\"host contract second\")\n expect(second.changeId.length).toBeGreaterThan(0)\n expect(typeof (yield* jj.diff(first.changeId, second.changeId))).toBe(\"string\")\n const revertCode = jjCap.unsupported?.revert\n if (revertCode === undefined) {\n const reverted = yield* revert!(second.changeId)\n expect(Array.isArray(reverted.reverted)).toBe(true)\n expect(reverted.reverted.every((path) =>\n typeof path === \"string\"\n )).toBe(true)\n } else {\n yield* assertFailure(revert!(second.changeId), revertCode)\n }\n yield* jj.restore(first.changeId)\n const workspaceName = `host-contract-${process.pid}-${++scratchSeq}`\n yield* jj.workspaceAdd(\n workspaceName,\n jjCap.workspacePath ?? defaultScratchPath(`${name}-jj-workspace`),\n first.changeId\n )\n yield* jj.workspaceForget(workspaceName)\n expect(typeof (yield* jj.status())).toBe(\"string\")\n const rootCode = jjCap.unsupported?.root\n const rootFrom = jjCap.rootFrom ?? \".\"\n if (rootCode === undefined) {\n expect((yield* root!(rootFrom)).length).toBeGreaterThan(0)\n } else {\n yield* assertFailure(root!(rootFrom), rootCode)\n }\n }),\n layer\n ))\n\n it.live(\"declares HTTP read, write, and redirect behavior\", () =>\n run(\n httpClientCap.expected === \"failure\"\n ? Effect.gen(function*() {\n const client = yield* HttpClient\n yield* Effect.forEach([\n HttpClientRequest.get(\"http://127.0.0.1:1/host-contract/read\"),\n HttpClientRequest.post(\"http://127.0.0.1:1/host-contract/write\").pipe(\n HttpClientRequest.bodyText(\"host-contract\")\n ),\n HttpClientRequest.get(\"http://127.0.0.1:1/host-contract/redirect\")\n ], (request) =>\n assertFailure(client.execute(request), httpClientCap.code), { discard: true })\n })\n : Effect.gen(function*() {\n const client = yield* HttpClient\n for (const probe of [httpClientCap.read, httpClientCap.write, httpClientCap.redirect]) {\n const response = yield* client.execute(probe.request)\n expect(typeof response.status).toBe(\"number\")\n expect(typeof response.headers).toBe(\"object\")\n probe.assertResponse(response)\n }\n }),\n layer\n ))\n })\n}\n"],
|
|
4
|
+
"sourcesContent": ["/**\n * Shared contract suite for complete Host bundles.\n *\n * This module is emitted as ESM, CJS, and declarations and exported from\n * `@smthrs/kernel/test/contract`. It intentionally has a Vitest peer because\n * it registers a reusable behavioral contract for third-party Host bundles.\n *\n * @since 1.0.0-rc.0\n */\n\n// Every case here runs on real elapsed time \u2014 subprocess spawns, file locks,\n// mtimes, and poll loops \u2014 so the suite uses `it.live`; `it.effect`'s\n// TestClock never advances for them.\n\nimport { describe, expect, it } from \"@effect/vitest\"\nimport * as JjService from \"@smthrs/jj\"\nimport type { Jj, JjErrorCode } from \"@smthrs/jj\"\nimport { Deferred, Effect, Fiber, FileSystem, type Layer, Option, Path, Stream } from \"effect\"\nimport { HttpClient } from \"effect/http/HttpClient\"\nimport * as HttpClientRequest from \"effect/http/HttpClientRequest\"\nimport type * as HttpClientResponse from \"effect/http/HttpClientResponse\"\nimport * as ChildProcess from \"effect/process/ChildProcess\"\nimport { type ChildProcessHandle, ChildProcessSpawner } from \"effect/process/ChildProcessSpawner\"\nimport { tmpdir } from \"node:os\"\nimport { join } from \"node:path\"\nimport * as FileSystemBatchContract from \"./FileSystemBatchContract.ts\"\n\nexport { check as checkFileSystemBatch } from \"./FileSystemBatchContract.ts\"\n\n/**\n * A capability that must fail with a stable typed code.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface FailureCapability<Code extends string> {\n readonly expected: \"failure\"\n readonly code: Code\n}\n\n/**\n * Successful filesystem contract options.\n *\n * `scratchPath` is required only for hosts whose filesystem does not accept an\n * OS temp path \u2014 an in-memory double, say. Omitting it takes\n * {@link defaultScratchPath}, which is process-scoped and outside the working\n * tree.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface FileSystemSuccess {\n readonly expected: \"success\"\n readonly scratchPath?: string | undefined\n /** Operations this otherwise available host deliberately refuses. */\n readonly unsupported?: Partial<Record<FileSystemOperation, string>> | undefined\n}\n\n/**\n * Every method on Effect's closed filesystem service.\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const FileSystemOperations = [\n \"access\",\n \"copy\",\n \"copyFile\",\n \"chmod\",\n \"chown\",\n \"glob\",\n \"exists\",\n \"link\",\n \"makeDirectory\",\n \"makeTempDirectory\",\n \"makeTempDirectoryScoped\",\n \"makeTempFile\",\n \"makeTempFileScoped\",\n \"open\",\n \"readDirectory\",\n \"readFile\",\n \"readFileString\",\n \"readLink\",\n \"realPath\",\n \"remove\",\n \"rename\",\n \"sink\",\n \"stat\",\n \"stream\",\n \"symlink\",\n \"truncate\",\n \"utimes\",\n \"watch\",\n \"writeFile\",\n \"writeFileString\"\n] as const\n\n/**\n * One method of Effect's closed filesystem service.\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type FileSystemOperation = typeof FileSystemOperations[number]\n\n/**\n * Successful Path contract expectation.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface PathSuccess {\n readonly expected: \"success\"\n}\n\n/**\n * Successful `ChildProcessSpawner` contract options.\n *\n * Defaults use the current Node or Bun executable. In-process browser\n * doubles can provide their own scripted commands and expected output.\n *\n * There is no timeout case: a wall-clock budget is `Effect.timeout` around any\n * effect, not a per-spawn option, so there is nothing host-specific left to\n * assert.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface ChildProcessSuccess {\n readonly expected: \"success\"\n readonly execCommand?: ChildProcess.Command | undefined\n readonly expectedStdout?: string | undefined\n readonly streamCommand?: ChildProcess.Command | undefined\n readonly expectedStreamText?: string | undefined\n /** A command carrying `cwd` and `env` options through to the child. */\n readonly optionsCommand?: ChildProcess.Command | undefined\n readonly expectedOptionsStdout?: string | undefined\n /**\n * How the host handles a command fed from a `stdin` stream: the expected\n * stdout when it supports one, or the typed code when it does not.\n */\n readonly stdin?:\n | { readonly command: ChildProcess.Command; readonly expectedStdout: string }\n | FailureCapability<string>\n | undefined\n /** A two-leg pipeline, or the exact typed refusal for hosts without one. */\n readonly pipeline?:\n | { readonly command: ChildProcess.Command; readonly expectedStdout: string }\n | FailureCapability<string>\n | undefined\n readonly interruptCommand?: ChildProcess.Command | undefined\n}\n\n/**\n * Successful Jj contract expectation.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface JjSuccess {\n readonly expected: \"success\"\n /** Makes the first and second snapshots observably different. */\n readonly prepareChange?: ((phase: \"first\" | \"second\") => Effect.Effect<void, unknown>) | undefined\n /** Nonexistent path at which the contract may add one temporary workspace. */\n readonly workspacePath?: string | undefined\n /** Path whose repository root the host must resolve. */\n readonly rootFrom?: string | undefined\n /** Optional methods this otherwise available backend explicitly refuses. */\n readonly unsupported?: Partial<Record<\"root\" | \"revert\", JjErrorCode>> | undefined\n}\n\n/**\n * Every method on the closed Jj host service.\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const JjOperations = [\n \"snapshot\",\n \"restore\",\n \"diff\",\n \"workspaceAdd\",\n \"workspaceForget\",\n \"status\",\n \"root\",\n \"revert\"\n] as const\n\n/**\n * One method of the closed Jj host service.\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type JjOperation = typeof JjOperations[number]\n\n/**\n * Successful HTTP contract probe.\n *\n * A request is explicit so the shared suite never invents a live network call.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface HttpClientProbe {\n /** Exact request the host must execute. */\n readonly request: HttpClientRequest.HttpClientRequest\n /** Adapter-specific response proof. */\n readonly assertResponse: (response: HttpClientResponse.HttpClientResponse) => void\n}\n\n/**\n * Successful read/write/redirect HTTP contract probes.\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface HttpClientSuccess {\n readonly expected: \"success\"\n /** Safe read method. */\n readonly read: HttpClientProbe\n /** Body-carrying write method. */\n readonly write: HttpClientProbe\n /** Manual redirect response; the host must not follow it below the guard. */\n readonly redirect: HttpClientProbe\n}\n\n/**\n * Complete capability expectations for the closed five-tag Host surface.\n * Unsupported capabilities are asserted with their code and are never skipped.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport interface HostContractCapabilities {\n readonly fileSystem: FileSystemSuccess | FailureCapability<string>\n readonly path: PathSuccess | FailureCapability<string>\n readonly childProcess: ChildProcessSuccess | FailureCapability<string>\n readonly jj: JjSuccess | FailureCapability<JjErrorCode>\n readonly httpClient: HttpClientSuccess | FailureCapability<string>\n}\n\n/**\n * The full layer output required by the contract.\n *\n * @category models\n * @since 1.0.0-rc.0\n */\nexport type HostContractLayer = Layer.Layer<\n FileSystem.FileSystem | Path.Path | ChildProcessSpawner | Jj | HttpClient,\n unknown\n>\n\n/**\n * Normalizes the code a Host failure is identified by, across the three shapes\n * the closed surface produces: a `code` field (`JjError`), a nested\n * `reason._tag` (`PlatformError`, `HttpClientError`), and a bare `_tag`.\n * Anything else is uncoded.\n *\n * @category testing\n * @since 1.0.0-rc.0\n */\nexport const errorCode = (error: unknown): string | undefined => {\n if (typeof error !== \"object\" || error === null) return undefined\n if (\"code\" in error && typeof error.code === \"string\") return error.code\n if (\n \"reason\" in error &&\n typeof error.reason === \"object\" &&\n error.reason !== null &&\n \"_tag\" in error.reason &&\n typeof error.reason._tag === \"string\"\n ) {\n return error.reason._tag\n }\n if (\"_tag\" in error && typeof error._tag === \"string\") return error._tag\n return undefined\n}\n\n/**\n * Asserts that `effect` fails with `code`. Succeeding is itself a contract\n * violation: a capability declared unsupported must never quietly work.\n *\n * @category testing\n * @since 1.0.0-rc.0\n */\nexport const assertFailure = <A, E, R>(\n effect: Effect.Effect<A, E, R>,\n code: string\n): Effect.Effect<void, never, R> =>\n effect.pipe(\n Effect.match({\n onFailure: (error) => {\n expect(errorCode(error)).toBe(code)\n },\n onSuccess: () => {\n throw new Error(`expected typed failure ${code}`)\n }\n })\n )\n\nlet scratchSeq = 0\n\n/**\n * Allocates the scratch file path a contract bundle writes through when the\n * caller declares no `scratchPath` of its own.\n *\n * The path is absolute under the OS temp directory and scoped by both process\n * id and an allocation counter. A repo-relative default would put the file in\n * the working tree, where it is neither ignored by git nor private to one test\n * process: concurrent `vitest run` invocations would then race the same path,\n * and the suite's `ensuring` remove could truncate a sibling's file between its\n * write and its read.\n *\n * @category testing\n * @since 1.0.0-rc.0\n */\nexport const defaultScratchPath = (suite: string): string => {\n const slug = suite.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-+|-+$/g, \"\") || \"host\"\n // Ambient `process.pid` on purpose: this is a Node-only test harness that\n // must name a path unique to THIS operating-system process before any layer\n // exists to ask. Injecting it would only move the same read one frame out.\n return join(tmpdir(), `flows-host-contract-${process.pid}-${++scratchSeq}-${slug}`)\n}\n\n/** Everything a {@link HostContractLayer} provides; a case may use nothing else. */\ntype HostServices = Layer.Success<HostContractLayer>\n\nconst provide = <A, E>(\n effect: Effect.Effect<A, E, HostServices>,\n layer: HostContractLayer\n): Effect.Effect<A, E | unknown> => effect.pipe(Effect.provide(layer))\n\nconst run = <A, E>(effect: Effect.Effect<A, E, HostServices>, layer: HostContractLayer) => provide(effect, layer)\n\nconst unsupported = ChildProcess.make(\"host-contract-unsupported\")\n\nconst unsupportedChildProcess = (operation: \"string\" | \"stream\", code: string) =>\n Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n yield* operation === \"stream\"\n ? assertFailure(Stream.runDrain(spawner.streamString(unsupported)), code)\n : assertFailure(spawner.string(unsupported), code)\n })\n\nconst fileSystemProbe = (\n fs: FileSystem.FileSystem,\n operation: FileSystemOperation,\n root: string\n): Effect.Effect<unknown, unknown> => {\n const at = (name: string): string => `${root}/${operation}-${name}`\n const source = `${root}/source.txt`\n const bytes = new TextEncoder().encode(\"host-contract\")\n switch (operation) {\n case \"access\":\n return fs.access(source)\n case \"copy\":\n return fs.copy(source, at(\"copy.txt\"))\n case \"copyFile\":\n return fs.copyFile(source, at(\"copy-file.txt\"))\n case \"chmod\":\n return fs.chmod(source, 0o600)\n case \"chown\":\n return Effect.flatMap(fs.stat(source), (info) =>\n fs.chown(\n source,\n Option.getOrElse(info.uid, () => 0),\n Option.getOrElse(info.gid, () => 0)\n ))\n case \"glob\":\n return fs.glob(\"**/*.txt\", { root })\n case \"exists\":\n return Effect.tap(fs.exists(source), (exists) => Effect.sync(() => expect(exists).toBe(true)))\n case \"link\":\n return fs.link(source, at(\"hard-link.txt\"))\n case \"makeDirectory\":\n return fs.makeDirectory(at(\"directory\"), { recursive: true })\n case \"makeTempDirectory\":\n return fs.makeTempDirectory({ directory: root })\n case \"makeTempDirectoryScoped\":\n return Effect.scoped(fs.makeTempDirectoryScoped({ directory: root }))\n case \"makeTempFile\":\n return fs.makeTempFile({ directory: root })\n case \"makeTempFileScoped\":\n return Effect.scoped(fs.makeTempFileScoped({ directory: root }))\n case \"open\":\n return Effect.scoped(fs.open(source, { flag: \"r\" }))\n case \"readDirectory\":\n return Effect.tap(fs.readDirectory(root), (entries) =>\n Effect.sync(() => {\n expect(Array.isArray(entries)).toBe(true)\n expect(entries.every((entry) => typeof entry === \"string\")).toBe(true)\n }))\n case \"readFile\":\n return Effect.gen(function*() {\n const first = yield* fs.readFile(source)\n expect(first).toBeInstanceOf(Uint8Array)\n const original = first[0]\n expect(original).toBeDefined()\n first[0] = original! ^ 0xff\n const second = yield* fs.readFile(source)\n expect(second[0]).toBe(original)\n })\n case \"readFileString\":\n return fs.readFileString(source)\n case \"readLink\": {\n const link = at(\"symbolic-link.txt\")\n return fs.symlink(source, link).pipe(Effect.andThen(fs.readLink(link)))\n }\n case \"realPath\":\n return fs.realPath(source)\n case \"remove\": {\n const path = at(\"remove.txt\")\n return fs.writeFile(path, bytes).pipe(Effect.andThen(fs.remove(path)))\n }\n case \"rename\": {\n const from = at(\"rename-from.txt\")\n return fs.writeFile(from, bytes).pipe(Effect.andThen(fs.rename(from, at(\"rename-to.txt\"))))\n }\n case \"sink\":\n return Stream.run(Stream.succeed(bytes), fs.sink(at(\"sink.txt\")))\n case \"stat\":\n return Effect.tap(fs.stat(source), (info) =>\n Effect.sync(() => {\n expect(typeof info.size).toBe(\"bigint\")\n expect(typeof info.type).toBe(\"string\")\n }))\n case \"stream\":\n return Stream.runForEach(\n fs.stream(source),\n (chunk) => Effect.sync(() => expect(chunk).toBeInstanceOf(Uint8Array))\n )\n case \"symlink\":\n return fs.symlink(source, at(\"symlink.txt\"))\n case \"truncate\": {\n const path = at(\"truncate.txt\")\n return fs.writeFile(path, bytes).pipe(Effect.andThen(fs.truncate(path, 1)))\n }\n case \"utimes\":\n return fs.utimes(source, new Date(0), new Date(0))\n case \"watch\":\n return Effect.gen(function*() {\n const watched = yield* Stream.runHead(fs.watch(root)).pipe(\n Effect.timeout(\"5 seconds\"),\n Effect.forkChild({ startImmediately: true })\n )\n // Keep producing events until the watcher observes one: subscribing\n // can take longer than any fixed initial delay. The foreign Promise\n // also works when the supplied host provides a frozen TestClock.\n yield* Effect.raceFirst(\n Fiber.join(watched),\n fs.writeFile(at(\"watched.txt\"), bytes).pipe(\n Effect.andThen(Effect.promise(() => new Promise((resolve) => setTimeout(resolve, 10)))),\n Effect.forever\n )\n )\n })\n case \"writeFile\":\n return Effect.gen(function*() {\n const input = bytes.slice()\n const expected = input[0]\n expect(expected).toBeDefined()\n yield* fs.writeFile(at(\"write.txt\"), input)\n input[0] = expected! ^ 0xff\n expect((yield* fs.readFile(at(\"write.txt\")))[0]).toBe(expected)\n })\n case \"writeFileString\":\n return fs.writeFileString(at(\"write-string.txt\"), \"host-contract\")\n }\n}\n\n/** The default stdin probe: echo back one line read from the child's stdin. */\nconst defaultStdinCommand = ChildProcess.make(\n process.execPath,\n [\n \"-e\",\n `let input = \"\"; process.stdin.setEncoding(\"utf8\"); process.stdin.on(\"data\", chunk => input += chunk); process.stdin.on(\"end\", () => process.stdout.write(input.replace(/\\\\r?\\\\n$/, \"\")));`\n ],\n { stdin: Stream.fromArray([new TextEncoder().encode(\"stdin\\n\")]) }\n)\n\n/** The default two-leg process probe. */\nconst defaultPipelineCommand = ChildProcess.pipeTo(\n ChildProcess.make(process.execPath, [\"-e\", `process.stdout.write(\"host-contract-pipeline\")`]),\n ChildProcess.make(process.execPath, [\"-e\", \"process.stdin.pipe(process.stdout)\"])\n)\n\n/** The default multi-leg cancellation probe. */\nconst defaultInterruptCommand = ChildProcess.pipeTo(\n ChildProcess.make(process.execPath, [\"-e\", \"setTimeout(() => {}, 10_000)\"]),\n ChildProcess.make(process.execPath, [\"-e\", \"process.stdin.pipe(process.stdout)\"])\n)\n\n/**\n * Registers the shared Host contract with Vitest.\n *\n * Every invocation creates ten cases: complete service presence,\n * FileSystem, Path, five child-process lifecycle cases, Jj, and\n * HttpClient.\n *\n * @category testing\n * @since 1.0.0-rc.0\n */\nexport const runHostContract = (\n name: string,\n layer: HostContractLayer,\n caps: HostContractCapabilities\n): void => {\n const fileSystemCap = caps.fileSystem\n const pathCap = caps.path\n const childProcessCap = caps.childProcess\n const jjCap = caps.jj\n const httpClientCap = caps.httpClient\n const scratchPath = fileSystemCap.expected === \"success\"\n ? fileSystemCap.scratchPath ?? defaultScratchPath(name)\n : \"\"\n\n describe(`${name} Host contract`, () => {\n it.live(\"provides every tag in the closed Host service list\", () =>\n run(\n Effect.gen(function*() {\n yield* FileSystem.FileSystem\n yield* Path.Path\n yield* ChildProcessSpawner\n yield* JjService.Jj\n yield* HttpClient\n }),\n layer\n ))\n\n it.live(\"declares every FileSystem operation\", () =>\n run(\n fileSystemCap.expected === \"failure\"\n ? Effect.gen(function*() {\n const fs = yield* FileSystem.FileSystem\n yield* Effect.forEach(\n FileSystemOperations,\n (operation) =>\n assertFailure(\n fileSystemProbe(fs, operation, \"/host-contract/unsupported\"),\n fileSystemCap.code\n ),\n { discard: true }\n )\n })\n : Effect.gen(function*() {\n const fs = yield* FileSystem.FileSystem\n const root = scratchPath\n const bytes = new TextEncoder().encode(\"host-contract\")\n yield* Effect.gen(function*() {\n yield* fs.makeDirectory(root, { recursive: true })\n yield* fs.writeFile(`${root}/source.txt`, bytes)\n yield* FileSystemBatchContract.check(fs, root)\n yield* Effect.forEach(\n FileSystemOperations,\n (operation) => {\n const probe = fileSystemProbe(fs, operation, root)\n const code = fileSystemCap.unsupported?.[operation]\n return code === undefined ? probe : assertFailure(probe, code)\n },\n { discard: true }\n )\n }).pipe(\n Effect.ensuring(fs.remove(root, { recursive: true, force: true }).pipe(Effect.ignore))\n )\n }),\n layer\n ))\n\n it.live(\"declares Path behavior\", () =>\n run(\n pathCap.expected === \"failure\"\n ? Effect.gen(function*() {\n const path = yield* Path.Path\n yield* assertFailure(\n Effect.try({\n try: () => path.fromFileUrl(new URL(\"file:///host-contract/value\")),\n catch: (error) => error\n }),\n pathCap.code\n )\n })\n : Effect.gen(function*() {\n const path = yield* Path.Path\n expect(path.normalize(\"/host-contract/./nested/../value\")).toBe(`${path.sep}host-contract${path.sep}value`)\n }),\n layer\n ))\n\n it.live(\"declares buffered child-process behavior\", () =>\n run(\n childProcessCap.expected === \"failure\"\n ? unsupportedChildProcess(\"string\", childProcessCap.code)\n : Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n const command = childProcessCap.execCommand ??\n ChildProcess.make(process.execPath, [\"-e\", `process.stdout.write(\"host-contract\")`])\n expect(yield* spawner.string(command)).toBe(childProcessCap.expectedStdout ?? \"host-contract\")\n expect(yield* spawner.exitCode(command)).toBe(0)\n }),\n layer\n ))\n\n it.live(\"declares child-process streaming behavior\", () =>\n run(\n childProcessCap.expected === \"failure\"\n ? unsupportedChildProcess(\"stream\", childProcessCap.code)\n : Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n const output = yield* Stream.mkString(\n spawner.streamString(\n childProcessCap.streamCommand ??\n ChildProcess.make(process.execPath, [\"-e\", `process.stdout.write(\"host-contract-stream\")`])\n )\n )\n expect(output).toContain(childProcessCap.expectedStreamText ?? \"host-contract-stream\")\n }),\n layer\n ))\n\n it.live(\"declares child-process cwd and env option behavior\", () =>\n run(\n childProcessCap.expected === \"failure\"\n ? unsupportedChildProcess(\"string\", childProcessCap.code)\n : Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n const output = yield* spawner.string(\n childProcessCap.optionsCommand ??\n ChildProcess.make(process.execPath, [\"-e\", \"process.stdout.write(process.env.HOST_CONTRACT_ENV)\"], {\n cwd: tmpdir(),\n env: { HOST_CONTRACT_ENV: \"env\" },\n extendEnv: true\n })\n )\n expect(output).toBe(childProcessCap.expectedOptionsStdout ?? \"env\")\n }),\n layer\n ))\n\n it.live(\"declares child-process stdin behavior\", () =>\n run(\n Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n const stdinCap = childProcessCap.expected === \"failure\"\n ? childProcessCap\n : childProcessCap.stdin ?? { command: defaultStdinCommand, expectedStdout: \"stdin\" }\n if (\"expected\" in stdinCap) {\n yield* assertFailure(spawner.string(defaultStdinCommand), stdinCap.code)\n return\n }\n expect(yield* spawner.string(stdinCap.command)).toBe(stdinCap.expectedStdout)\n }),\n layer\n ))\n\n it.live(\"declares child-process pipeline behavior\", () =>\n run(\n Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n const pipelineCap = childProcessCap.expected === \"failure\"\n ? childProcessCap\n : childProcessCap.pipeline ?? {\n command: defaultPipelineCommand,\n expectedStdout: \"host-contract-pipeline\"\n }\n if (\"expected\" in pipelineCap) {\n yield* assertFailure(spawner.string(defaultPipelineCommand), pipelineCap.code)\n return\n }\n expect(yield* spawner.string(pipelineCap.command)).toBe(pipelineCap.expectedStdout)\n }),\n layer\n ))\n\n it.live(\"declares child-process interruption behavior\", () =>\n run(\n childProcessCap.expected === \"failure\"\n ? unsupportedChildProcess(\"stream\", childProcessCap.code)\n : Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n const ready = yield* Deferred.make<ChildProcessHandle>()\n const fiber = yield* Effect.scoped(\n Effect.gen(function*() {\n const handle = yield* spawner.spawn(\n childProcessCap.interruptCommand ?? defaultInterruptCommand\n )\n yield* Deferred.succeed(ready, handle)\n yield* handle.exitCode\n })\n ).pipe(\n Effect.forkChild({ startImmediately: true })\n )\n const handle = yield* Deferred.await(ready)\n expect(yield* handle.isRunning).toBe(true)\n yield* Fiber.interrupt(fiber)\n expect(yield* handle.isRunning).toBe(false)\n }),\n layer\n ))\n\n it.live(\"declares every Jj operation\", () =>\n run(\n jjCap.expected === \"failure\"\n ? Effect.gen(function*() {\n const jj = yield* JjService.Jj\n const root = jj.root\n const revert = jj.revert\n expect(root).toBeTypeOf(\"function\")\n expect(revert).toBeTypeOf(\"function\")\n yield* Effect.forEach([\n jj.snapshot(\"host contract\"),\n jj.restore(\"host-contract-change\"),\n jj.diff(\"host-contract-from\", \"host-contract-to\"),\n jj.workspaceAdd(\"host-contract\", \"/host-contract/workspace\", \"host-contract-change\"),\n jj.workspaceForget(\"host-contract\"),\n jj.status(),\n root!(\"/host-contract\"),\n revert!(\"host-contract-change\")\n ], (probe) =>\n assertFailure(probe, jjCap.code), { discard: true })\n })\n : Effect.gen(function*() {\n const jj = yield* JjService.Jj\n const root = jj.root\n const revert = jj.revert\n expect(root).toBeTypeOf(\"function\")\n expect(revert).toBeTypeOf(\"function\")\n yield* (jjCap.prepareChange?.(\"first\") ?? Effect.void)\n const first = yield* jj.snapshot(\"host contract first\")\n expect(first.changeId.length).toBeGreaterThan(0)\n yield* (jjCap.prepareChange?.(\"second\") ?? Effect.void)\n const second = yield* jj.snapshot(\"host contract second\")\n expect(second.changeId.length).toBeGreaterThan(0)\n expect(typeof (yield* jj.diff(first.changeId, second.changeId))).toBe(\"string\")\n const revertCode = jjCap.unsupported?.revert\n if (revertCode === undefined) {\n const reverted = yield* revert!(second.changeId)\n expect(Array.isArray(reverted.reverted)).toBe(true)\n expect(reverted.reverted.every((path) =>\n typeof path === \"string\"\n )).toBe(true)\n } else {\n yield* assertFailure(revert!(second.changeId), revertCode)\n }\n yield* jj.restore(first.changeId)\n const workspaceName = `host-contract-${process.pid}-${++scratchSeq}`\n yield* jj.workspaceAdd(\n workspaceName,\n jjCap.workspacePath ?? defaultScratchPath(`${name}-jj-workspace`),\n first.changeId\n )\n yield* jj.workspaceForget(workspaceName)\n expect(typeof (yield* jj.status())).toBe(\"string\")\n const rootCode = jjCap.unsupported?.root\n const rootFrom = jjCap.rootFrom ?? \".\"\n if (rootCode === undefined) {\n expect((yield* root!(rootFrom)).length).toBeGreaterThan(0)\n } else {\n yield* assertFailure(root!(rootFrom), rootCode)\n }\n }),\n layer\n ))\n\n it.live(\"declares HTTP read, write, and redirect behavior\", () =>\n run(\n httpClientCap.expected === \"failure\"\n ? Effect.gen(function*() {\n const client = yield* HttpClient\n yield* Effect.forEach([\n HttpClientRequest.get(\"http://127.0.0.1:1/host-contract/read\"),\n HttpClientRequest.post(\"http://127.0.0.1:1/host-contract/write\").pipe(\n HttpClientRequest.bodyText(\"host-contract\")\n ),\n HttpClientRequest.get(\"http://127.0.0.1:1/host-contract/redirect\")\n ], (request) =>\n assertFailure(client.execute(request), httpClientCap.code), { discard: true })\n })\n : Effect.gen(function*() {\n const client = yield* HttpClient\n for (const probe of [httpClientCap.read, httpClientCap.write, httpClientCap.redirect]) {\n const response = yield* client.execute(probe.request)\n expect(typeof response.status).toBe(\"number\")\n expect(typeof response.headers).toBe(\"object\")\n probe.assertResponse(response)\n }\n }),\n layer\n ))\n })\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": ["handle"]
|
|
7
7
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Permission-aware process spawning.
|
|
3
3
|
*
|
|
4
4
|
* Smithers has no shell service of its own — process execution is Effect's
|
|
5
|
-
* `effect/
|
|
5
|
+
* `effect/process`. The only thing the kernel adds is the
|
|
6
6
|
* `proc:spawn` capability check and the host confinement wrapper, applied to
|
|
7
7
|
* the one primitive every other helper is derived from.
|
|
8
8
|
*
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* @since 1.0.0-rc.0
|
|
23
23
|
*/
|
|
24
24
|
import { Layer, Path } from "effect";
|
|
25
|
-
import { ChildProcessSpawner } from "effect/
|
|
25
|
+
import { ChildProcessSpawner } from "effect/process/ChildProcessSpawner";
|
|
26
26
|
import { GrantStore } from "./GrantStore.ts";
|
|
27
27
|
import { Workspace } from "./Workspace.ts";
|
|
28
28
|
/**
|
|
@@ -32,7 +32,7 @@ import { Workspace } from "./Workspace.ts";
|
|
|
32
32
|
* @category services
|
|
33
33
|
* @since 1.0.0-rc.0
|
|
34
34
|
*/
|
|
35
|
-
export { ChildProcessSpawner } from "effect/
|
|
35
|
+
export { ChildProcessSpawner } from "effect/process/ChildProcessSpawner";
|
|
36
36
|
/**
|
|
37
37
|
* Derives the full six-method surface from one `spawn`, so `exitCode`,
|
|
38
38
|
* `string`, `lines`, and both `stream*` helpers can never bypass whatever
|
|
@@ -41,7 +41,7 @@ export { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner
|
|
|
41
41
|
* @category constructors
|
|
42
42
|
* @since 1.0.0-rc.0
|
|
43
43
|
*/
|
|
44
|
-
export { make } from "effect/
|
|
44
|
+
export { make } from "effect/process/ChildProcessSpawner";
|
|
45
45
|
/**
|
|
46
46
|
* Constructs an unavailable spawner stub.
|
|
47
47
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChildProcessSpawner.d.ts","sourceRoot":"","sources":["../../src/ChildProcessSpawner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAsB,KAAK,EAAU,IAAI,EAAc,MAAM,QAAQ,CAAA;AAG5E,OAAO,EAAE,mBAAmB,EAAuB,MAAM,
|
|
1
|
+
{"version":3,"file":"ChildProcessSpawner.d.ts","sourceRoot":"","sources":["../../src/ChildProcessSpawner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAsB,KAAK,EAAU,IAAI,EAAc,MAAM,QAAQ,CAAA;AAG5E,OAAO,EAAE,mBAAmB,EAAuB,MAAM,oCAAoC,CAAA;AAE7F,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAK5C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAgK1C;;;;;;GAMG;AACH,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAA;AAExE;;;;;;;GAOG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAA;AAEzD;;;;;;;;;GASG;AACH,eAAO,MAAM,QAAQ,GACnB,YAAW,OAAO,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAM,KACtD,mBAAmB,CAAC,SAAS,CAY9B,CAAA;AAEF;;;;;GAKG;AACH,eAAO,MAAM,SAAS,GACpB,YAAW,OAAO,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAM,KACtD,KAAK,CAAC,KAAK,CAAC,mBAAmB,CAA4D,CAAA;AAE9F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,KAAK,CAC7B,mBAAmB,EACnB,KAAK,EACL,mBAAmB,GAAG,UAAU,GAAG,IAAI,CAAC,IAAI,GAAG,SAAS,CA4HzD,CAAA"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Permission-aware process spawning.
|
|
3
3
|
*
|
|
4
4
|
* Smithers has no shell service of its own — process execution is Effect's
|
|
5
|
-
* `effect/
|
|
5
|
+
* `effect/process`. The only thing the kernel adds is the
|
|
6
6
|
* `proc:spawn` capability check and the host confinement wrapper, applied to
|
|
7
7
|
* the one primitive every other helper is derived from.
|
|
8
8
|
*
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
import { toPlatformError } from "@smthrs/capability/Permission";
|
|
25
25
|
import { Effect, FileSystem, Layer, Option, Path } from "effect";
|
|
26
26
|
import { systemError } from "effect/PlatformError";
|
|
27
|
-
import * as ChildProcess from "effect/
|
|
28
|
-
import { ChildProcessSpawner, make as makeSpawner } from "effect/
|
|
27
|
+
import * as ChildProcess from "effect/process/ChildProcess";
|
|
28
|
+
import { ChildProcessSpawner, make as makeSpawner } from "effect/process/ChildProcessSpawner";
|
|
29
29
|
import * as CommandLine from "./CommandLine.js";
|
|
30
30
|
import { GrantStore } from "./GrantStore.js";
|
|
31
31
|
import * as Containment from "./internal/Containment.js";
|
|
@@ -191,7 +191,7 @@ const environmentNames = (command) => {
|
|
|
191
191
|
* @category services
|
|
192
192
|
* @since 1.0.0-rc.0
|
|
193
193
|
*/
|
|
194
|
-
export { ChildProcessSpawner } from "effect/
|
|
194
|
+
export { ChildProcessSpawner } from "effect/process/ChildProcessSpawner";
|
|
195
195
|
/**
|
|
196
196
|
* Derives the full six-method surface from one `spawn`, so `exitCode`,
|
|
197
197
|
* `string`, `lines`, and both `stream*` helpers can never bypass whatever
|
|
@@ -200,7 +200,7 @@ export { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner
|
|
|
200
200
|
* @category constructors
|
|
201
201
|
* @since 1.0.0-rc.0
|
|
202
202
|
*/
|
|
203
|
-
export { make } from "effect/
|
|
203
|
+
export { make } from "effect/process/ChildProcessSpawner";
|
|
204
204
|
/**
|
|
205
205
|
* Constructs an unavailable spawner stub.
|
|
206
206
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChildProcessSpawner.js","sourceRoot":"","sources":["../../src/ChildProcessSpawner.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Permission-aware process spawning.\n *\n * Smithers has no shell service of its own — process execution is Effect's\n * `effect/unstable/process`. The only thing the kernel adds is the\n * `proc:spawn` capability check and the host confinement wrapper, applied to\n * the one primitive every other helper is derived from.\n *\n * There is no kernel spawner interface and no kernel spawner tag. Effect owns\n * both, and its tag fixes the error channel to `PlatformError`, so this module\n * is only the middleware: a `Layer` over Effect's own tag. A denied command is\n * projected into `PlatformError` by `Permission.toPlatformError`, which keeps\n * the structured kernel failure on the error's `cause`. Because the channel\n * stays `PlatformError`, the full six-method surface is derived from the one\n * guarded `spawn` through Effect's own `ChildProcessSpawner.make` without a\n * cast.\n *\n * Governing design:\n * `docs/specs/Concepts/Permission Kernel.md` and\n * `docs/specs/Concepts/Host Adapters.md`.\n *\n * @since 1.0.0-rc.0\n */\n\nimport { toPlatformError } from \"@smthrs/capability/Permission\"\nimport { Effect, FileSystem, Layer, Option, Path, type Scope } from \"effect\"\nimport { type PlatformError, systemError } from \"effect/PlatformError\"\nimport * as ChildProcess from \"effect/unstable/process/ChildProcess\"\nimport { ChildProcessSpawner, make as makeSpawner } from \"effect/unstable/process/ChildProcessSpawner\"\nimport * as CommandLine from \"./CommandLine.ts\"\nimport { GrantStore } from \"./GrantStore.ts\"\nimport * as Containment from \"./internal/Containment.ts\"\nimport { makeCapability } from \"./internal/makeCapability.ts\"\nimport * as ProcessConfinement from \"./ProcessConfinement.ts\"\nimport * as Rooted from \"./Rooted.ts\"\nimport { Workspace } from \"./Workspace.ts\"\n\nconst data = (value: object, name: PropertyKey): unknown => {\n const descriptor = Object.getOwnPropertyDescriptor(value, name)\n if (descriptor === undefined || !(\"value\" in descriptor)) {\n throw new TypeError(`command ${String(name)} must be a data property`)\n }\n return descriptor.value\n}\n\nconst optionalData = (value: object, name: PropertyKey): unknown => {\n const descriptor = Object.getOwnPropertyDescriptor(value, name)\n if (descriptor === undefined) return undefined\n if (!(\"value\" in descriptor)) throw new TypeError(`command ${String(name)} must be a data property`)\n return descriptor.value\n}\n\nconst snapshotRecord = (\n value: Readonly<Record<string, string | undefined>> | undefined\n): Record<string, string | undefined> | undefined => {\n if (value === undefined) return undefined\n const snapshot: Record<string, string | undefined> = Object.create(null)\n for (const [name, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {\n if (!descriptor.enumerable) continue\n if (!(\"value\" in descriptor) || (descriptor.value !== undefined && typeof descriptor.value !== \"string\")) {\n throw new TypeError(\"command environment must contain string data properties\")\n }\n snapshot[name] = descriptor.value\n }\n return Object.freeze(snapshot)\n}\n\nconst snapshotStreamConfig = <A>(value: A): A => {\n if (typeof value !== \"object\" || value === null) return value\n const descriptors = Object.getOwnPropertyDescriptors(value)\n if (descriptors.stream === undefined) return value\n if (!(\"value\" in descriptors.stream)) throw new TypeError(\"command stream must be a data property\")\n const snapshot: Record<string, unknown> = { stream: descriptors.stream.value }\n for (const name of [\"endOnDone\", \"encoding\"] as const) {\n const descriptor = descriptors[name]\n if (descriptor !== undefined) {\n if (!(\"value\" in descriptor)) throw new TypeError(`command ${name} must be a data property`)\n snapshot[name] = descriptor.value\n }\n }\n return Object.freeze(snapshot) as A\n}\n\nconst snapshotAdditionalFds = (\n value: ChildProcess.CommandOptions[\"additionalFds\"]\n): ChildProcess.CommandOptions[\"additionalFds\"] => {\n if (value === undefined) return undefined\n const snapshot: Record<`fd${number}`, ChildProcess.AdditionalFdConfig> = {}\n for (const [name, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {\n if (!descriptor.enumerable) continue\n if (!(\"value\" in descriptor) || typeof descriptor.value !== \"object\" || descriptor.value === null) {\n throw new TypeError(\"additional file descriptors must be data records\")\n }\n const config = descriptor.value as ChildProcess.AdditionalFdConfig\n const type = data(config, \"type\")\n if (type === \"input\") {\n const stream = Object.getOwnPropertyDescriptor(config, \"stream\")\n snapshot[name as `fd${number}`] = Object.freeze({\n type,\n ...(stream === undefined ? {} : \"value\" in stream ? { stream: stream.value } : (() => {\n throw new TypeError(\"additional fd stream must be a data property\")\n })())\n })\n } else if (type === \"output\") {\n const sink = Object.getOwnPropertyDescriptor(config, \"sink\")\n snapshot[name as `fd${number}`] = Object.freeze({\n type,\n ...(sink === undefined ? {} : \"value\" in sink ? { sink: sink.value } : (() => {\n throw new TypeError(\"additional fd sink must be a data property\")\n })())\n })\n } else {\n throw new TypeError(\"additional file descriptor type is invalid\")\n }\n }\n return Object.freeze(snapshot)\n}\n\nconst snapshotOptions = (options: ChildProcess.CommandOptions): ChildProcess.CommandOptions =>\n Object.freeze({\n killSignal: options.killSignal,\n forceKillAfter: options.forceKillAfter,\n cwd: options.cwd,\n env: snapshotRecord(options.env),\n extendEnv: options.extendEnv,\n shell: options.shell,\n detached: options.detached,\n windowsHide: options.windowsHide,\n stdin: snapshotStreamConfig(options.stdin),\n stdout: snapshotStreamConfig(options.stdout),\n stderr: snapshotStreamConfig(options.stderr),\n additionalFds: snapshotAdditionalFds(options.additionalFds)\n })\n\nconst snapshotCommand = (command: ChildProcess.Command): ChildProcess.Command => {\n const tag = data(command, \"_tag\")\n if (tag === \"StandardCommand\") {\n const executable = data(command, \"command\")\n const args = data(command, \"args\")\n const options = data(command, \"options\")\n if (typeof executable !== \"string\" || !Array.isArray(args) || !args.every((arg) => typeof arg === \"string\")) {\n throw new TypeError(\"standard command executable and arguments are invalid\")\n }\n if (typeof options !== \"object\" || options === null) throw new TypeError(\"command options are invalid\")\n return ChildProcess.make(\n executable,\n Object.freeze([...args]),\n snapshotOptions(options)\n )\n }\n if (tag === \"PipedCommand\") {\n const left = data(command, \"left\")\n const right = data(command, \"right\")\n const options = data(command, \"options\")\n if (typeof left !== \"object\" || left === null || typeof right !== \"object\" || right === null) {\n throw new TypeError(\"pipeline commands are invalid\")\n }\n if (typeof options !== \"object\" || options === null) throw new TypeError(\"pipeline options are invalid\")\n const from = optionalData(options, \"from\")\n const to = optionalData(options, \"to\")\n return ChildProcess.pipeTo(\n snapshotCommand(right as ChildProcess.Command),\n Object.freeze({\n ...(from === undefined ? {} : { from: from as ChildProcess.PipeFromOption }),\n ...(to === undefined ? {} : { to: to as ChildProcess.PipeToOption })\n })\n )(snapshotCommand(left as ChildProcess.Command))\n }\n throw new TypeError(\"unsupported command shape\")\n}\n\nconst maximumEnvironmentNames = 64\n\n/**\n * The environment this process holds, which a child built with\n * `ChildProcessEnvironment.make` inherits its bootstrap names from. Empty on a\n * host with no process environment, so every declared bootstrap name counts\n * as an override there.\n */\nconst ambientEnvironment = (): Readonly<Record<string, string | undefined>> =>\n (globalThis as { readonly process?: { readonly env?: Record<string, string | undefined> } }).process?.env ?? {}\n\nconst environmentNames = (command: ChildProcess.StandardCommand): ReadonlyArray<string> | undefined => {\n const environment = command.options.env\n if (environment === undefined) return undefined\n const names = Object.entries(environment)\n .filter(([, value]) => value !== undefined)\n .map(([name]) => name)\n .sort()\n if (names.length === 0) return undefined\n return names.length <= maximumEnvironmentNames\n ? names\n : [...names.slice(0, maximumEnvironmentNames), `+${names.length - maximumEnvironmentNames} more`]\n}\n\n/**\n * Effect's own process spawner tag, unchanged. Re-exported so the kernel\n * namespace stays one-stop; it is the *same* tag, never a second one.\n *\n * @category services\n * @since 1.0.0-rc.0\n */\nexport { ChildProcessSpawner } from \"effect/unstable/process/ChildProcessSpawner\"\n\n/**\n * Derives the full six-method surface from one `spawn`, so `exitCode`,\n * `string`, `lines`, and both `stream*` helpers can never bypass whatever\n * `spawn` was given.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport { make } from \"effect/unstable/process/ChildProcessSpawner\"\n\n/**\n * Constructs an unavailable spawner stub.\n *\n * Every derived helper reports the missing host as a `NotFound`\n * `PlatformError` naming the command line, so an unconfigured capability\n * answers rather than vanishing.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport const makeNoop = (\n overrides: Partial<ChildProcessSpawner[\"Service\"]> = {}\n): ChildProcessSpawner[\"Service\"] => ({\n ...makeSpawner((command) =>\n Effect.fail(\n systemError({\n _tag: \"NotFound\",\n module: \"ChildProcessSpawner\",\n method: \"spawn\",\n description: `no process host for \\`${CommandLine.render(command)}\\``\n })\n )\n ),\n ...overrides\n})\n\n/**\n * Provides an unavailable spawner stub.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerNoop = (\n overrides: Partial<ChildProcessSpawner[\"Service\"]> = {}\n): Layer.Layer<ChildProcessSpawner> => Layer.succeed(ChildProcessSpawner)(makeNoop(overrides))\n\n/**\n * Decorates the process spawner in place with a `proc:spawn` capability check.\n *\n * The check is suspended into the spawn itself, so building a `Command` or a\n * stream neither requests permission nor starts a process; only running one\n * does. Every pipeline stage is checked as its own capability, left to right,\n * and no stage starts until all pass. A stage's resource is\n * `CommandLine.resource`: the command line `CommandLine.render` produces, with\n * a `shell: true` line that holds shell control syntax marked as\n * `sh -c '<line>'`, any environment variable the child would not inherit from\n * this process named in an `env <NAME>… -- ` prefix, and a working directory\n * outside `Workspace.root` named in a `cwd <path> -- ` prefix. Custom shell\n * paths are included explicitly in the resource; browser and remote adapters\n * reject them rather than silently substituting a different shell.\n *\n * Every approved stage requires `ProcessConfinement` in the host context.\n * A missing service refuses spawning; its `layerNoop` is the explicit choice\n * for an unconfined host.\n *\n * Pipeline `from`/`to` routing remains outside the grant. The working\n * directory and overridden environment names also reach attended surfaces as\n * display metadata; environment values never leave the command.\n *\n * The layer provides the tag it also requires: compose it over a host spawner\n * layer with `Layer.provide` and a `ChildProcess.Command` run as a plain\n * `Effect` is checked too.\n *\n * A command with no `cwd`, or a relative one, runs in `Workspace.root`, never\n * the process's own directory; the check sees that resolved directory. When\n * the host filesystem is in the layer's context, the working directory and\n * `Workspace.root` are compared by real path, so a symlink inside the\n * workspace that leads out of it is named, as its target, in the `cwd` prefix.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layer: Layer.Layer<\n ChildProcessSpawner,\n never,\n ChildProcessSpawner | GrantStore | Path.Path | Workspace\n> = Layer.effect(\n ChildProcessSpawner,\n Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n const grants = yield* GrantStore\n const confinement = yield* Effect.serviceOption(ProcessConfinement.ProcessConfinement)\n const path = yield* Path.Path\n const rooted = Rooted.path(path, path.resolve((yield* Workspace).root))\n const root = rooted.resolve(\".\")\n // The raw host filesystem this layer is composed over, when there is one.\n // A symlink inside the workspace can lead out of it, so containment is\n // judged on real paths; a directory that cannot be resolved (it does not\n // exist, or the host has no filesystem) is judged lexically.\n const fileSystem = yield* Effect.serviceOption(FileSystem.FileSystem)\n const real = (directory: string) =>\n Option.match(fileSystem, {\n onNone: () => Effect.succeed(directory),\n onSome: (fs) => fs.realPath(directory).pipe(Effect.orElseSucceed(() => directory))\n })\n const within = (base: string) => (directory: string) => {\n const relative = path.relative(base, directory)\n return relative.split(path.sep)[0] !== \"..\" && !path.isAbsolute(relative)\n }\n const checkStage = (stage: ChildProcess.StandardCommand) =>\n Effect.gen(function*() {\n // Rooted.command gives every stage an absolute cwd.\n const cwd = stage.options.cwd!\n const realRoot = yield* real(root)\n const resolvedCwd = yield* real(cwd)\n const contains = resolvedCwd === cwd\n ? (directory: string) => within(root)(directory) || within(realRoot)(directory)\n : within(realRoot)\n return yield* checkResolved(\n stage,\n CommandLine.resource(stage, {\n ambient: ambientEnvironment(),\n contains,\n resolvedCwd\n })\n )\n })\n const checkResolved = (stage: ChildProcess.StandardCommand, rendered: string) => {\n const environment = environmentNames(stage)\n return makeCapability(\"proc:spawn\", rendered).pipe(\n Effect.flatMap((capability) =>\n grants.check(capability, {\n cwd: stage.options.cwd,\n ...(environment === undefined ? {} : { env: environment })\n })\n ),\n Effect.mapError((error) =>\n toPlatformError({\n module: \"ChildProcessSpawner\",\n method: \"spawn\",\n pathOrDescriptor: rendered,\n error\n })\n )\n )\n }\n const check = (command: ChildProcess.Command) =>\n Effect.forEach(CommandLine.stages(command), checkStage, { discard: true })\n const confine = (\n command: ChildProcess.Command\n ): Effect.Effect<\n ChildProcess.Command,\n PlatformError,\n Scope.Scope\n > =>\n Option.match(confinement, {\n onNone: () =>\n Effect.fail(systemError({\n _tag: \"NotFound\",\n module: \"ProcessConfinement\",\n method: \"confine\",\n description:\n \"process confinement is required; use ProcessConfinement.layerNoop to explicitly run unconfined\"\n })),\n onSome: (service) =>\n Effect.gen(function*() {\n const profile = yield* ProcessConfinement.profile(grants, root, path).pipe(\n Effect.mapError((error) => toPlatformError({ module: \"ChildProcessSpawner\", method: \"spawn\", error }))\n )\n const wrap = (\n command: ChildProcess.Command\n ): Effect.Effect<\n ChildProcess.Command,\n PlatformError,\n Scope.Scope\n > =>\n command._tag === \"StandardCommand\"\n ? service.confine(command, profile)\n : Effect.gen(function*() {\n const left = yield* wrap(command.left)\n const right = yield* wrap(command.right)\n return ChildProcess.pipeTo(right, command.options)(left)\n })\n return yield* wrap(command)\n })\n })\n return Containment.inherit(\n spawner,\n makeSpawner(\n Effect.fn(\"ChildProcessSpawner.spawn\")((command: ChildProcess.Command) =>\n Effect.try({\n try: () => snapshotCommand(command),\n catch: () =>\n systemError({\n _tag: \"InvalidData\",\n module: \"ChildProcessSpawner\",\n method: \"spawn\",\n description: \"command must be an immutable supported process description\"\n })\n }).pipe(\n Effect.map((snapshot) => Rooted.command(snapshot, rooted)),\n Effect.flatMap((rooted) =>\n check(rooted).pipe(Effect.andThen(confine(rooted)), Effect.flatMap(spawner.spawn))\n )\n )\n )\n )\n )\n })\n)\n"]}
|
|
1
|
+
{"version":3,"file":"ChildProcessSpawner.js","sourceRoot":"","sources":["../../src/ChildProcessSpawner.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Permission-aware process spawning.\n *\n * Smithers has no shell service of its own — process execution is Effect's\n * `effect/process`. The only thing the kernel adds is the\n * `proc:spawn` capability check and the host confinement wrapper, applied to\n * the one primitive every other helper is derived from.\n *\n * There is no kernel spawner interface and no kernel spawner tag. Effect owns\n * both, and its tag fixes the error channel to `PlatformError`, so this module\n * is only the middleware: a `Layer` over Effect's own tag. A denied command is\n * projected into `PlatformError` by `Permission.toPlatformError`, which keeps\n * the structured kernel failure on the error's `cause`. Because the channel\n * stays `PlatformError`, the full six-method surface is derived from the one\n * guarded `spawn` through Effect's own `ChildProcessSpawner.make` without a\n * cast.\n *\n * Governing design:\n * `docs/specs/Concepts/Permission Kernel.md` and\n * `docs/specs/Concepts/Host Adapters.md`.\n *\n * @since 1.0.0-rc.0\n */\n\nimport { toPlatformError } from \"@smthrs/capability/Permission\"\nimport { Effect, FileSystem, Layer, Option, Path, type Scope } from \"effect\"\nimport { type PlatformError, systemError } from \"effect/PlatformError\"\nimport * as ChildProcess from \"effect/process/ChildProcess\"\nimport { ChildProcessSpawner, make as makeSpawner } from \"effect/process/ChildProcessSpawner\"\nimport * as CommandLine from \"./CommandLine.ts\"\nimport { GrantStore } from \"./GrantStore.ts\"\nimport * as Containment from \"./internal/Containment.ts\"\nimport { makeCapability } from \"./internal/makeCapability.ts\"\nimport * as ProcessConfinement from \"./ProcessConfinement.ts\"\nimport * as Rooted from \"./Rooted.ts\"\nimport { Workspace } from \"./Workspace.ts\"\n\nconst data = (value: object, name: PropertyKey): unknown => {\n const descriptor = Object.getOwnPropertyDescriptor(value, name)\n if (descriptor === undefined || !(\"value\" in descriptor)) {\n throw new TypeError(`command ${String(name)} must be a data property`)\n }\n return descriptor.value\n}\n\nconst optionalData = (value: object, name: PropertyKey): unknown => {\n const descriptor = Object.getOwnPropertyDescriptor(value, name)\n if (descriptor === undefined) return undefined\n if (!(\"value\" in descriptor)) throw new TypeError(`command ${String(name)} must be a data property`)\n return descriptor.value\n}\n\nconst snapshotRecord = (\n value: Readonly<Record<string, string | undefined>> | undefined\n): Record<string, string | undefined> | undefined => {\n if (value === undefined) return undefined\n const snapshot: Record<string, string | undefined> = Object.create(null)\n for (const [name, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {\n if (!descriptor.enumerable) continue\n if (!(\"value\" in descriptor) || (descriptor.value !== undefined && typeof descriptor.value !== \"string\")) {\n throw new TypeError(\"command environment must contain string data properties\")\n }\n snapshot[name] = descriptor.value\n }\n return Object.freeze(snapshot)\n}\n\nconst snapshotStreamConfig = <A>(value: A): A => {\n if (typeof value !== \"object\" || value === null) return value\n const descriptors = Object.getOwnPropertyDescriptors(value)\n if (descriptors.stream === undefined) return value\n if (!(\"value\" in descriptors.stream)) throw new TypeError(\"command stream must be a data property\")\n const snapshot: Record<string, unknown> = { stream: descriptors.stream.value }\n for (const name of [\"endOnDone\", \"encoding\"] as const) {\n const descriptor = descriptors[name]\n if (descriptor !== undefined) {\n if (!(\"value\" in descriptor)) throw new TypeError(`command ${name} must be a data property`)\n snapshot[name] = descriptor.value\n }\n }\n return Object.freeze(snapshot) as A\n}\n\nconst snapshotAdditionalFds = (\n value: ChildProcess.CommandOptions[\"additionalFds\"]\n): ChildProcess.CommandOptions[\"additionalFds\"] => {\n if (value === undefined) return undefined\n const snapshot: Record<`fd${number}`, ChildProcess.AdditionalFdConfig> = {}\n for (const [name, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {\n if (!descriptor.enumerable) continue\n if (!(\"value\" in descriptor) || typeof descriptor.value !== \"object\" || descriptor.value === null) {\n throw new TypeError(\"additional file descriptors must be data records\")\n }\n const config = descriptor.value as ChildProcess.AdditionalFdConfig\n const type = data(config, \"type\")\n if (type === \"input\") {\n const stream = Object.getOwnPropertyDescriptor(config, \"stream\")\n snapshot[name as `fd${number}`] = Object.freeze({\n type,\n ...(stream === undefined ? {} : \"value\" in stream ? { stream: stream.value } : (() => {\n throw new TypeError(\"additional fd stream must be a data property\")\n })())\n })\n } else if (type === \"output\") {\n const sink = Object.getOwnPropertyDescriptor(config, \"sink\")\n snapshot[name as `fd${number}`] = Object.freeze({\n type,\n ...(sink === undefined ? {} : \"value\" in sink ? { sink: sink.value } : (() => {\n throw new TypeError(\"additional fd sink must be a data property\")\n })())\n })\n } else {\n throw new TypeError(\"additional file descriptor type is invalid\")\n }\n }\n return Object.freeze(snapshot)\n}\n\nconst snapshotOptions = (options: ChildProcess.CommandOptions): ChildProcess.CommandOptions =>\n Object.freeze({\n killSignal: options.killSignal,\n forceKillAfter: options.forceKillAfter,\n cwd: options.cwd,\n env: snapshotRecord(options.env),\n extendEnv: options.extendEnv,\n shell: options.shell,\n detached: options.detached,\n windowsHide: options.windowsHide,\n stdin: snapshotStreamConfig(options.stdin),\n stdout: snapshotStreamConfig(options.stdout),\n stderr: snapshotStreamConfig(options.stderr),\n additionalFds: snapshotAdditionalFds(options.additionalFds)\n })\n\nconst snapshotCommand = (command: ChildProcess.Command): ChildProcess.Command => {\n const tag = data(command, \"_tag\")\n if (tag === \"StandardCommand\") {\n const executable = data(command, \"command\")\n const args = data(command, \"args\")\n const options = data(command, \"options\")\n if (typeof executable !== \"string\" || !Array.isArray(args) || !args.every((arg) => typeof arg === \"string\")) {\n throw new TypeError(\"standard command executable and arguments are invalid\")\n }\n if (typeof options !== \"object\" || options === null) throw new TypeError(\"command options are invalid\")\n return ChildProcess.make(\n executable,\n Object.freeze([...args]),\n snapshotOptions(options)\n )\n }\n if (tag === \"PipedCommand\") {\n const left = data(command, \"left\")\n const right = data(command, \"right\")\n const options = data(command, \"options\")\n if (typeof left !== \"object\" || left === null || typeof right !== \"object\" || right === null) {\n throw new TypeError(\"pipeline commands are invalid\")\n }\n if (typeof options !== \"object\" || options === null) throw new TypeError(\"pipeline options are invalid\")\n const from = optionalData(options, \"from\")\n const to = optionalData(options, \"to\")\n return ChildProcess.pipeTo(\n snapshotCommand(right as ChildProcess.Command),\n Object.freeze({\n ...(from === undefined ? {} : { from: from as ChildProcess.PipeFromOption }),\n ...(to === undefined ? {} : { to: to as ChildProcess.PipeToOption })\n })\n )(snapshotCommand(left as ChildProcess.Command))\n }\n throw new TypeError(\"unsupported command shape\")\n}\n\nconst maximumEnvironmentNames = 64\n\n/**\n * The environment this process holds, which a child built with\n * `ChildProcessEnvironment.make` inherits its bootstrap names from. Empty on a\n * host with no process environment, so every declared bootstrap name counts\n * as an override there.\n */\nconst ambientEnvironment = (): Readonly<Record<string, string | undefined>> =>\n (globalThis as { readonly process?: { readonly env?: Record<string, string | undefined> } }).process?.env ?? {}\n\nconst environmentNames = (command: ChildProcess.StandardCommand): ReadonlyArray<string> | undefined => {\n const environment = command.options.env\n if (environment === undefined) return undefined\n const names = Object.entries(environment)\n .filter(([, value]) => value !== undefined)\n .map(([name]) => name)\n .sort()\n if (names.length === 0) return undefined\n return names.length <= maximumEnvironmentNames\n ? names\n : [...names.slice(0, maximumEnvironmentNames), `+${names.length - maximumEnvironmentNames} more`]\n}\n\n/**\n * Effect's own process spawner tag, unchanged. Re-exported so the kernel\n * namespace stays one-stop; it is the *same* tag, never a second one.\n *\n * @category services\n * @since 1.0.0-rc.0\n */\nexport { ChildProcessSpawner } from \"effect/process/ChildProcessSpawner\"\n\n/**\n * Derives the full six-method surface from one `spawn`, so `exitCode`,\n * `string`, `lines`, and both `stream*` helpers can never bypass whatever\n * `spawn` was given.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport { make } from \"effect/process/ChildProcessSpawner\"\n\n/**\n * Constructs an unavailable spawner stub.\n *\n * Every derived helper reports the missing host as a `NotFound`\n * `PlatformError` naming the command line, so an unconfigured capability\n * answers rather than vanishing.\n *\n * @category constructors\n * @since 1.0.0-rc.0\n */\nexport const makeNoop = (\n overrides: Partial<ChildProcessSpawner[\"Service\"]> = {}\n): ChildProcessSpawner[\"Service\"] => ({\n ...makeSpawner((command) =>\n Effect.fail(\n systemError({\n _tag: \"NotFound\",\n module: \"ChildProcessSpawner\",\n method: \"spawn\",\n description: `no process host for \\`${CommandLine.render(command)}\\``\n })\n )\n ),\n ...overrides\n})\n\n/**\n * Provides an unavailable spawner stub.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerNoop = (\n overrides: Partial<ChildProcessSpawner[\"Service\"]> = {}\n): Layer.Layer<ChildProcessSpawner> => Layer.succeed(ChildProcessSpawner)(makeNoop(overrides))\n\n/**\n * Decorates the process spawner in place with a `proc:spawn` capability check.\n *\n * The check is suspended into the spawn itself, so building a `Command` or a\n * stream neither requests permission nor starts a process; only running one\n * does. Every pipeline stage is checked as its own capability, left to right,\n * and no stage starts until all pass. A stage's resource is\n * `CommandLine.resource`: the command line `CommandLine.render` produces, with\n * a `shell: true` line that holds shell control syntax marked as\n * `sh -c '<line>'`, any environment variable the child would not inherit from\n * this process named in an `env <NAME>… -- ` prefix, and a working directory\n * outside `Workspace.root` named in a `cwd <path> -- ` prefix. Custom shell\n * paths are included explicitly in the resource; browser and remote adapters\n * reject them rather than silently substituting a different shell.\n *\n * Every approved stage requires `ProcessConfinement` in the host context.\n * A missing service refuses spawning; its `layerNoop` is the explicit choice\n * for an unconfined host.\n *\n * Pipeline `from`/`to` routing remains outside the grant. The working\n * directory and overridden environment names also reach attended surfaces as\n * display metadata; environment values never leave the command.\n *\n * The layer provides the tag it also requires: compose it over a host spawner\n * layer with `Layer.provide` and a `ChildProcess.Command` run as a plain\n * `Effect` is checked too.\n *\n * A command with no `cwd`, or a relative one, runs in `Workspace.root`, never\n * the process's own directory; the check sees that resolved directory. When\n * the host filesystem is in the layer's context, the working directory and\n * `Workspace.root` are compared by real path, so a symlink inside the\n * workspace that leads out of it is named, as its target, in the `cwd` prefix.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layer: Layer.Layer<\n ChildProcessSpawner,\n never,\n ChildProcessSpawner | GrantStore | Path.Path | Workspace\n> = Layer.effect(\n ChildProcessSpawner,\n Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n const grants = yield* GrantStore\n const confinement = yield* Effect.serviceOption(ProcessConfinement.ProcessConfinement)\n const path = yield* Path.Path\n const rooted = Rooted.path(path, path.resolve((yield* Workspace).root))\n const root = rooted.resolve(\".\")\n // The raw host filesystem this layer is composed over, when there is one.\n // A symlink inside the workspace can lead out of it, so containment is\n // judged on real paths; a directory that cannot be resolved (it does not\n // exist, or the host has no filesystem) is judged lexically.\n const fileSystem = yield* Effect.serviceOption(FileSystem.FileSystem)\n const real = (directory: string) =>\n Option.match(fileSystem, {\n onNone: () => Effect.succeed(directory),\n onSome: (fs) => fs.realPath(directory).pipe(Effect.orElseSucceed(() => directory))\n })\n const within = (base: string) => (directory: string) => {\n const relative = path.relative(base, directory)\n return relative.split(path.sep)[0] !== \"..\" && !path.isAbsolute(relative)\n }\n const checkStage = (stage: ChildProcess.StandardCommand) =>\n Effect.gen(function*() {\n // Rooted.command gives every stage an absolute cwd.\n const cwd = stage.options.cwd!\n const realRoot = yield* real(root)\n const resolvedCwd = yield* real(cwd)\n const contains = resolvedCwd === cwd\n ? (directory: string) => within(root)(directory) || within(realRoot)(directory)\n : within(realRoot)\n return yield* checkResolved(\n stage,\n CommandLine.resource(stage, {\n ambient: ambientEnvironment(),\n contains,\n resolvedCwd\n })\n )\n })\n const checkResolved = (stage: ChildProcess.StandardCommand, rendered: string) => {\n const environment = environmentNames(stage)\n return makeCapability(\"proc:spawn\", rendered).pipe(\n Effect.flatMap((capability) =>\n grants.check(capability, {\n cwd: stage.options.cwd,\n ...(environment === undefined ? {} : { env: environment })\n })\n ),\n Effect.mapError((error) =>\n toPlatformError({\n module: \"ChildProcessSpawner\",\n method: \"spawn\",\n pathOrDescriptor: rendered,\n error\n })\n )\n )\n }\n const check = (command: ChildProcess.Command) =>\n Effect.forEach(CommandLine.stages(command), checkStage, { discard: true })\n const confine = (\n command: ChildProcess.Command\n ): Effect.Effect<\n ChildProcess.Command,\n PlatformError,\n Scope.Scope\n > =>\n Option.match(confinement, {\n onNone: () =>\n Effect.fail(systemError({\n _tag: \"NotFound\",\n module: \"ProcessConfinement\",\n method: \"confine\",\n description:\n \"process confinement is required; use ProcessConfinement.layerNoop to explicitly run unconfined\"\n })),\n onSome: (service) =>\n Effect.gen(function*() {\n const profile = yield* ProcessConfinement.profile(grants, root, path).pipe(\n Effect.mapError((error) => toPlatformError({ module: \"ChildProcessSpawner\", method: \"spawn\", error }))\n )\n const wrap = (\n command: ChildProcess.Command\n ): Effect.Effect<\n ChildProcess.Command,\n PlatformError,\n Scope.Scope\n > =>\n command._tag === \"StandardCommand\"\n ? service.confine(command, profile)\n : Effect.gen(function*() {\n const left = yield* wrap(command.left)\n const right = yield* wrap(command.right)\n return ChildProcess.pipeTo(right, command.options)(left)\n })\n return yield* wrap(command)\n })\n })\n return Containment.inherit(\n spawner,\n makeSpawner(\n Effect.fn(\"ChildProcessSpawner.spawn\")((command: ChildProcess.Command) =>\n Effect.try({\n try: () => snapshotCommand(command),\n catch: () =>\n systemError({\n _tag: \"InvalidData\",\n module: \"ChildProcessSpawner\",\n method: \"spawn\",\n description: \"command must be an immutable supported process description\"\n })\n }).pipe(\n Effect.map((snapshot) => Rooted.command(snapshot, rooted)),\n Effect.flatMap((rooted) =>\n check(rooted).pipe(Effect.andThen(confine(rooted)), Effect.flatMap(spawner.spawn))\n )\n )\n )\n )\n )\n })\n)\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @since 1.0.0-rc.0
|
|
3
3
|
*
|
|
4
|
-
* Rendering an `effect/
|
|
4
|
+
* Rendering an `effect/process` `Command` back to a shell command
|
|
5
5
|
* line.
|
|
6
6
|
*
|
|
7
7
|
* Two callers need the same string and must agree on it exactly:
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* The module is pure string handling — no host access — so it stays on the
|
|
19
19
|
* browser-safe side of the package.
|
|
20
20
|
*/
|
|
21
|
-
import type * as ChildProcess from "effect/
|
|
21
|
+
import type * as ChildProcess from "effect/process/ChildProcess";
|
|
22
22
|
/**
|
|
23
23
|
* Quotes one token for a POSIX shell, leaving obviously safe tokens alone.
|
|
24
24
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandLine.d.ts","sourceRoot":"","sources":["../../src/CommandLine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,KAAK,YAAY,MAAM,
|
|
1
|
+
{"version":3,"file":"CommandLine.d.ts","sourceRoot":"","sources":["../../src/CommandLine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,KAAK,YAAY,MAAM,6BAA6B,CAAA;AAMhE;;;;;GAKG;AACH,eAAO,MAAM,KAAK,GAAI,OAAO,MAAM,KAAG,MAC4C,CAAA;AAElF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,MAAM,GAAI,SAAS,YAAY,CAAC,OAAO,KAAG,MAOG,CAAA;AAiB1D;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GAAG,SAAS,CAAA;IAC3E;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,SAAS,CAAA;IAChE;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC1C;AAgBD;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,GAAI,SAAS,YAAY,CAAC,OAAO,KAAG,aAAa,CAAC,YAAY,CAAC,eAAe,CACK,CAAA;AAEtG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,eAAO,MAAM,QAAQ,GAAI,SAAS,YAAY,CAAC,eAAe,EAAE,UAAS,eAAoB,KAAG,MAW/F,CAAA;AASD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,UAAU,GAAI,SAAS,YAAY,CAAC,OAAO,KAAG,MAKO,CAAA;AAElE;;;;;;;GAOG;AACH,eAAO,MAAM,GAAG,GAAI,SAAS,YAAY,CAAC,OAAO,KAAG,MAAM,GAAG,SACiB,CAAA;AAE9E;;;;;;GAMG;AACH,eAAO,MAAM,GAAG,GACd,SAAS,YAAY,CAAC,OAAO,KAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,SACsC,CAAA"}
|
package/dist/esm/CommandLine.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandLine.js","sourceRoot":"","sources":["../../src/CommandLine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @since 1.0.0-rc.0\n *\n * Rendering an `effect/unstable/process` `Command` back to a shell command\n * line.\n *\n * Two callers need the same string and must agree on it exactly:\n *\n * 1. `@smthrs/kernel/ChildProcessSpawner` uses it as the `proc:spawn`\n * capability resource, so a grant reads the way an operator wrote it; and\n * 2. `@smthrs/platform-browser/BrowserChildProcessSpawner` uses it as the line\n * it hands to the in-browser bash interpreter, which has no `argv` to spawn\n * with.\n *\n * A single renderer keeps a granted capability and the command a browser\n * actually runs from drifting apart.\n *\n * The module is pure string handling — no host access — so it stays on the\n * browser-safe side of the package.\n */\n\nimport type * as ChildProcess from \"effect/unstable/process/ChildProcess\"\nimport { isInheritedName } from \"./ChildProcessEnvironment.ts\"\n\n/** Tokens made only of these characters need no quoting in a POSIX shell. */\nconst SAFE = /^[A-Za-z0-9_@%+=:,./-]+$/\n\n/**\n * Quotes one token for a POSIX shell, leaving obviously safe tokens alone.\n *\n * @category rendering\n * @since 1.0.0-rc.0\n */\nexport const quote = (token: string): string =>\n token !== \"\" && SAFE.test(token) ? token : `'${token.replaceAll(\"'\", `'\\\\''`)}'`\n\n/**\n * Renders a `Command` as a single shell command line.\n *\n * A standard command with `shell: true` renders its tokens verbatim, matching\n * the line Node and the browser interpreter hand to the default shell. A\n * custom shell renders as an explicit `<shell> -c <line>` invocation so the\n * selected executable is part of the permission resource. Without `shell`,\n * every token is POSIX-quoted to preserve literal argv semantics. These\n * distinctions are security-sensitive: the rendered value is also the\n * `proc:spawn` capability resource, so it must describe what will execute.\n * The rendering is POSIX-only by contract: on Windows, Node invokes the shell\n * with `/d /s /c` rather than `-c`, so the rendered line describes the POSIX\n * invocation, not a cmd.exe one.\n *\n * The `proc:spawn` capability resource is {@link resource}, which starts from\n * this line and adds what the line alone cannot show.\n *\n * A `PipedCommand` renders with `|` between its sides. That is a faithful\n * rendering of what the pipeline does, and it is the only form an interpreter\n * that takes a command line rather than an `argv` can be given. `from`/`to`\n * pipe options are *not* expressible this way; rendering ignores them, so a\n * pipeline that redirects `stderr` renders like one that pipes `stdout`.\n * Capability checks therefore see the commands, never the plumbing.\n *\n * @category rendering\n * @since 1.0.0-rc.0\n */\nexport const render = (command: ChildProcess.Command): string =>\n command._tag === \"StandardCommand\"\n ? command.options.shell === undefined || command.options.shell === false\n ? [command.command, ...command.args].map(quote).join(\" \")\n : command.options.shell === true\n ? [command.command, ...command.args].join(\" \")\n : `${quote(command.options.shell)} -c ${quote([command.command, ...command.args].join(\" \"))}`\n : `${render(command.left)} | ${render(command.right)}`\n\n/**\n * Shell syntax that chains, substitutes, groups, or redirects: everything that\n * lets one shell line run or write more than a single simple command.\n */\nconst shellControl = /[;&|`$<>()\\n\\r]/\n\n/**\n * Redirections that neither run nor write anything a grant should see: an fd\n * duplication such as `2>&1` and a discard to `/dev/null`. They are removed\n * before {@link shellControl} is tested, so `git status 2>&1` keeps its\n * verbatim resource. Only a space or tab may sit between `>` and `/dev/null`:\n * `>` followed by a line break is not a discard, so it stays control syntax.\n */\nconst harmlessRedirect = /(^|\\s)(?:\\d*>&\\d+|\\d*>[ \\t]*\\/dev\\/null)(?=\\s|$)/g\n\n/**\n * What {@link resource} compares a stage against, supplied by the spawner that\n * checks it.\n *\n * @category models\n * @since 1.0.0-rc.1\n */\nexport interface ResourceContext {\n /**\n * The environment the spawning process itself holds. A bootstrap name\n * (`PATH`, `HOME`, ...) whose declared value equals the ambient value is the\n * inherited default and stays out of the resource; one set to anything else\n * is named. Omitted, every declared bootstrap name is named.\n */\n readonly ambient?: Readonly<Record<string, string | undefined>> | undefined\n /**\n * Whether a stage's working directory is inside the directory the flow\n * already governs. A `cwd` it rejects is named in the resource. Omitted,\n * every `cwd` a stage sets is named.\n */\n readonly contains?: ((directory: string) => boolean) | undefined\n /**\n * The stage `cwd` with every symlink resolved, when the host could resolve\n * it. It replaces the lexical `cwd` both for `contains` and in the\n * `cwd <path> -- ` prefix, so a symlink inside the governed directory that\n * leads out of it is judged, and shown, by where the command really runs.\n */\n readonly resolvedCwd?: string | undefined\n}\n\n/**\n * Names of the environment variables a stage sets that the child would not\n * otherwise inherit: every name outside the bootstrap set, and a bootstrap\n * name whose value differs from the ambient one.\n */\nconst overriddenNames = (\n command: ChildProcess.StandardCommand,\n ambient: Readonly<Record<string, string | undefined>>\n): ReadonlyArray<string> =>\n Object.entries(command.options.env ?? {})\n .filter(([name, value]) => value !== undefined && (!isInheritedName(name) || ambient[name] !== value))\n .map(([name]) => name)\n .sort()\n\n/**\n * The stages of a command in execution order: the command itself, or every\n * `StandardCommand` of a pipeline from left to right. The spawner checks each\n * stage as its own `proc:spawn` capability.\n *\n * @category rendering\n * @since 1.0.0-rc.1\n */\nexport const stages = (command: ChildProcess.Command): ReadonlyArray<ChildProcess.StandardCommand> =>\n command._tag === \"StandardCommand\" ? [command] : [...stages(command.left), ...stages(command.right)]\n\n/**\n * The `proc:spawn` capability resource for one stage: {@link render}, with\n * three additions that keep a prefix grant from authorizing code the stage\n * never named.\n *\n * - A `shell: true` stage whose line holds shell control syntax (`;`, `&`,\n * `|`, `` ` ``, `$`, `<`, `>`, `(`, `)`, or a line break) is marked as the\n * explicit `sh -c '<line>'` it is. A grant such as `git status *` then\n * cannot match `git status; curl x | sh`, while a simple shell line such as\n * `git status --short` keeps its verbatim resource. An fd duplication such\n * as `2>&1` and a discard to `/dev/null` do not count as control syntax.\n * - A stage that sets an environment variable the child would not otherwise\n * inherit is prefixed with `env <NAME>… -- `, sorted names only. That is\n * every name outside the bootstrap set `ChildProcessEnvironment` inherits\n * (`PATH`, `HOME`, `USER`, `LANG`, `TERM`, `TMPDIR`, `SHELL`, `LC_*`), and a\n * bootstrap name whose value differs from `context.ambient`. A grant for\n * `git status` covers neither `env GIT_SSH_COMMAND -- git status` nor\n * `env PATH -- git status`. Values never enter the resource because they\n * often carry credentials.\n * - A stage whose `cwd` `context.contains` rejects is prefixed with\n * `cwd <path> -- `, so a grant does not follow the command into a\n * directory whose configuration the flow does not govern. When\n * `context.resolvedCwd` is given, that real path is judged and named\n * instead of the lexical `cwd`.\n *\n * A pipeline has no single resource: the spawner checks every one of its\n * {@link stages}, so `git status | sh` needs a grant for `sh` too.\n *\n * The `env` and `cwd` prefixes share their spelling with a literal command:\n * `make(\"env\", [\"PATH\", \"--\", \"git\", \"status\"])` renders as\n * `env PATH -- git status`, the resource of `git status` with `PATH`\n * overridden. The alias grants nothing extra. The literal command runs a\n * program named `PATH` (or `cwd`) from the ambient `PATH`, not `git`. A grant\n * with `*` inside the prefix, such as `env * -- git status`, also matches\n * `env -C /x -- git status`, but it already admits any variable name,\n * including `GIT_SSH_COMMAND` and `LD_PRELOAD`, which run arbitrary code.\n * Grant a prefixed resource exactly as the request shows it.\n *\n * @category rendering\n * @since 1.0.0-rc.1\n */\nexport const resource = (command: ChildProcess.StandardCommand, context: ResourceContext = {}): string => {\n const line = render(command)\n const shown = command.options.shell === true && shellControl.test(line.replace(harmlessRedirect, \"$1\"))\n ? `sh -c ${quote(line)}`\n : line\n const names = overriddenNames(command, context.ambient ?? {})\n const withEnv = names.length === 0 ? shown : `env ${names.map(quote).join(\" \")} -- ${shown}`\n const directory = command.options.cwd === undefined ? undefined : context.resolvedCwd ?? command.options.cwd\n return directory === undefined || context.contains?.(directory) === true\n ? withEnv\n : `cwd ${quote(directory)} -- ${withEnv}`\n}\n\n/** The leading token of a shell line, which is the program the line runs. */\nconst program = (line: string): string => {\n const trimmed = line.trimStart()\n const end = trimmed.search(/\\s/)\n return end === -1 ? trimmed : trimmed.slice(0, end)\n}\n\n/**\n * The executable a command runs, without its arguments.\n *\n * A durable record of a spawned process names its program with this rather\n * than with {@link render}: arguments carry credentials (`curl -u user:pass`,\n * `mysql -phunter2`), and a journal that keeps them keeps them permanently.\n * Nothing that reads such a record needs more than the program name; a reaper\n * matches processes by pid and process group.\n *\n * Without `shell`, `command` is the executable itself, spaces and all. With a\n * shell, the line is what runs, so the program is its first token: both\n * `make(\"mysql -phunter2\", [], { shell: true })` and\n * `make(\"mysql\", [\"-phunter2\"])` yield `mysql`. A pipeline names one\n * executable per stage, joined the way {@link render} joins the stages.\n *\n * @category rendering\n * @since 1.0.0-rc.0\n */\nexport const executable = (command: ChildProcess.Command): string =>\n command._tag === \"StandardCommand\"\n ? command.options.shell === undefined || command.options.shell === false\n ? command.command\n : program(command.command)\n : `${executable(command.left)} | ${executable(command.right)}`\n\n/**\n * The working directory a command runs in, taking the leftmost stage of a\n * pipeline, which is the stage `setCwd` and the spawners agree to treat as the\n * pipeline's own directory.\n *\n * @category rendering\n * @since 1.0.0-rc.0\n */\nexport const cwd = (command: ChildProcess.Command): string | undefined =>\n command._tag === \"StandardCommand\" ? command.options.cwd : cwd(command.left)\n\n/**\n * The environment overrides a command runs with, taking the leftmost stage of\n * a pipeline for the same reason {@link cwd} does.\n *\n * @category rendering\n * @since 1.0.0-rc.0\n */\nexport const env = (\n command: ChildProcess.Command\n): Record<string, string | undefined> | undefined =>\n command._tag === \"StandardCommand\" ? command.options.env : env(command.left)\n"]}
|
|
1
|
+
{"version":3,"file":"CommandLine.js","sourceRoot":"","sources":["../../src/CommandLine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @since 1.0.0-rc.0\n *\n * Rendering an `effect/process` `Command` back to a shell command\n * line.\n *\n * Two callers need the same string and must agree on it exactly:\n *\n * 1. `@smthrs/kernel/ChildProcessSpawner` uses it as the `proc:spawn`\n * capability resource, so a grant reads the way an operator wrote it; and\n * 2. `@smthrs/platform-browser/BrowserChildProcessSpawner` uses it as the line\n * it hands to the in-browser bash interpreter, which has no `argv` to spawn\n * with.\n *\n * A single renderer keeps a granted capability and the command a browser\n * actually runs from drifting apart.\n *\n * The module is pure string handling — no host access — so it stays on the\n * browser-safe side of the package.\n */\n\nimport type * as ChildProcess from \"effect/process/ChildProcess\"\nimport { isInheritedName } from \"./ChildProcessEnvironment.ts\"\n\n/** Tokens made only of these characters need no quoting in a POSIX shell. */\nconst SAFE = /^[A-Za-z0-9_@%+=:,./-]+$/\n\n/**\n * Quotes one token for a POSIX shell, leaving obviously safe tokens alone.\n *\n * @category rendering\n * @since 1.0.0-rc.0\n */\nexport const quote = (token: string): string =>\n token !== \"\" && SAFE.test(token) ? token : `'${token.replaceAll(\"'\", `'\\\\''`)}'`\n\n/**\n * Renders a `Command` as a single shell command line.\n *\n * A standard command with `shell: true` renders its tokens verbatim, matching\n * the line Node and the browser interpreter hand to the default shell. A\n * custom shell renders as an explicit `<shell> -c <line>` invocation so the\n * selected executable is part of the permission resource. Without `shell`,\n * every token is POSIX-quoted to preserve literal argv semantics. These\n * distinctions are security-sensitive: the rendered value is also the\n * `proc:spawn` capability resource, so it must describe what will execute.\n * The rendering is POSIX-only by contract: on Windows, Node invokes the shell\n * with `/d /s /c` rather than `-c`, so the rendered line describes the POSIX\n * invocation, not a cmd.exe one.\n *\n * The `proc:spawn` capability resource is {@link resource}, which starts from\n * this line and adds what the line alone cannot show.\n *\n * A `PipedCommand` renders with `|` between its sides. That is a faithful\n * rendering of what the pipeline does, and it is the only form an interpreter\n * that takes a command line rather than an `argv` can be given. `from`/`to`\n * pipe options are *not* expressible this way; rendering ignores them, so a\n * pipeline that redirects `stderr` renders like one that pipes `stdout`.\n * Capability checks therefore see the commands, never the plumbing.\n *\n * @category rendering\n * @since 1.0.0-rc.0\n */\nexport const render = (command: ChildProcess.Command): string =>\n command._tag === \"StandardCommand\"\n ? command.options.shell === undefined || command.options.shell === false\n ? [command.command, ...command.args].map(quote).join(\" \")\n : command.options.shell === true\n ? [command.command, ...command.args].join(\" \")\n : `${quote(command.options.shell)} -c ${quote([command.command, ...command.args].join(\" \"))}`\n : `${render(command.left)} | ${render(command.right)}`\n\n/**\n * Shell syntax that chains, substitutes, groups, or redirects: everything that\n * lets one shell line run or write more than a single simple command.\n */\nconst shellControl = /[;&|`$<>()\\n\\r]/\n\n/**\n * Redirections that neither run nor write anything a grant should see: an fd\n * duplication such as `2>&1` and a discard to `/dev/null`. They are removed\n * before {@link shellControl} is tested, so `git status 2>&1` keeps its\n * verbatim resource. Only a space or tab may sit between `>` and `/dev/null`:\n * `>` followed by a line break is not a discard, so it stays control syntax.\n */\nconst harmlessRedirect = /(^|\\s)(?:\\d*>&\\d+|\\d*>[ \\t]*\\/dev\\/null)(?=\\s|$)/g\n\n/**\n * What {@link resource} compares a stage against, supplied by the spawner that\n * checks it.\n *\n * @category models\n * @since 1.0.0-rc.1\n */\nexport interface ResourceContext {\n /**\n * The environment the spawning process itself holds. A bootstrap name\n * (`PATH`, `HOME`, ...) whose declared value equals the ambient value is the\n * inherited default and stays out of the resource; one set to anything else\n * is named. Omitted, every declared bootstrap name is named.\n */\n readonly ambient?: Readonly<Record<string, string | undefined>> | undefined\n /**\n * Whether a stage's working directory is inside the directory the flow\n * already governs. A `cwd` it rejects is named in the resource. Omitted,\n * every `cwd` a stage sets is named.\n */\n readonly contains?: ((directory: string) => boolean) | undefined\n /**\n * The stage `cwd` with every symlink resolved, when the host could resolve\n * it. It replaces the lexical `cwd` both for `contains` and in the\n * `cwd <path> -- ` prefix, so a symlink inside the governed directory that\n * leads out of it is judged, and shown, by where the command really runs.\n */\n readonly resolvedCwd?: string | undefined\n}\n\n/**\n * Names of the environment variables a stage sets that the child would not\n * otherwise inherit: every name outside the bootstrap set, and a bootstrap\n * name whose value differs from the ambient one.\n */\nconst overriddenNames = (\n command: ChildProcess.StandardCommand,\n ambient: Readonly<Record<string, string | undefined>>\n): ReadonlyArray<string> =>\n Object.entries(command.options.env ?? {})\n .filter(([name, value]) => value !== undefined && (!isInheritedName(name) || ambient[name] !== value))\n .map(([name]) => name)\n .sort()\n\n/**\n * The stages of a command in execution order: the command itself, or every\n * `StandardCommand` of a pipeline from left to right. The spawner checks each\n * stage as its own `proc:spawn` capability.\n *\n * @category rendering\n * @since 1.0.0-rc.1\n */\nexport const stages = (command: ChildProcess.Command): ReadonlyArray<ChildProcess.StandardCommand> =>\n command._tag === \"StandardCommand\" ? [command] : [...stages(command.left), ...stages(command.right)]\n\n/**\n * The `proc:spawn` capability resource for one stage: {@link render}, with\n * three additions that keep a prefix grant from authorizing code the stage\n * never named.\n *\n * - A `shell: true` stage whose line holds shell control syntax (`;`, `&`,\n * `|`, `` ` ``, `$`, `<`, `>`, `(`, `)`, or a line break) is marked as the\n * explicit `sh -c '<line>'` it is. A grant such as `git status *` then\n * cannot match `git status; curl x | sh`, while a simple shell line such as\n * `git status --short` keeps its verbatim resource. An fd duplication such\n * as `2>&1` and a discard to `/dev/null` do not count as control syntax.\n * - A stage that sets an environment variable the child would not otherwise\n * inherit is prefixed with `env <NAME>… -- `, sorted names only. That is\n * every name outside the bootstrap set `ChildProcessEnvironment` inherits\n * (`PATH`, `HOME`, `USER`, `LANG`, `TERM`, `TMPDIR`, `SHELL`, `LC_*`), and a\n * bootstrap name whose value differs from `context.ambient`. A grant for\n * `git status` covers neither `env GIT_SSH_COMMAND -- git status` nor\n * `env PATH -- git status`. Values never enter the resource because they\n * often carry credentials.\n * - A stage whose `cwd` `context.contains` rejects is prefixed with\n * `cwd <path> -- `, so a grant does not follow the command into a\n * directory whose configuration the flow does not govern. When\n * `context.resolvedCwd` is given, that real path is judged and named\n * instead of the lexical `cwd`.\n *\n * A pipeline has no single resource: the spawner checks every one of its\n * {@link stages}, so `git status | sh` needs a grant for `sh` too.\n *\n * The `env` and `cwd` prefixes share their spelling with a literal command:\n * `make(\"env\", [\"PATH\", \"--\", \"git\", \"status\"])` renders as\n * `env PATH -- git status`, the resource of `git status` with `PATH`\n * overridden. The alias grants nothing extra. The literal command runs a\n * program named `PATH` (or `cwd`) from the ambient `PATH`, not `git`. A grant\n * with `*` inside the prefix, such as `env * -- git status`, also matches\n * `env -C /x -- git status`, but it already admits any variable name,\n * including `GIT_SSH_COMMAND` and `LD_PRELOAD`, which run arbitrary code.\n * Grant a prefixed resource exactly as the request shows it.\n *\n * @category rendering\n * @since 1.0.0-rc.1\n */\nexport const resource = (command: ChildProcess.StandardCommand, context: ResourceContext = {}): string => {\n const line = render(command)\n const shown = command.options.shell === true && shellControl.test(line.replace(harmlessRedirect, \"$1\"))\n ? `sh -c ${quote(line)}`\n : line\n const names = overriddenNames(command, context.ambient ?? {})\n const withEnv = names.length === 0 ? shown : `env ${names.map(quote).join(\" \")} -- ${shown}`\n const directory = command.options.cwd === undefined ? undefined : context.resolvedCwd ?? command.options.cwd\n return directory === undefined || context.contains?.(directory) === true\n ? withEnv\n : `cwd ${quote(directory)} -- ${withEnv}`\n}\n\n/** The leading token of a shell line, which is the program the line runs. */\nconst program = (line: string): string => {\n const trimmed = line.trimStart()\n const end = trimmed.search(/\\s/)\n return end === -1 ? trimmed : trimmed.slice(0, end)\n}\n\n/**\n * The executable a command runs, without its arguments.\n *\n * A durable record of a spawned process names its program with this rather\n * than with {@link render}: arguments carry credentials (`curl -u user:pass`,\n * `mysql -phunter2`), and a journal that keeps them keeps them permanently.\n * Nothing that reads such a record needs more than the program name; a reaper\n * matches processes by pid and process group.\n *\n * Without `shell`, `command` is the executable itself, spaces and all. With a\n * shell, the line is what runs, so the program is its first token: both\n * `make(\"mysql -phunter2\", [], { shell: true })` and\n * `make(\"mysql\", [\"-phunter2\"])` yield `mysql`. A pipeline names one\n * executable per stage, joined the way {@link render} joins the stages.\n *\n * @category rendering\n * @since 1.0.0-rc.0\n */\nexport const executable = (command: ChildProcess.Command): string =>\n command._tag === \"StandardCommand\"\n ? command.options.shell === undefined || command.options.shell === false\n ? command.command\n : program(command.command)\n : `${executable(command.left)} | ${executable(command.right)}`\n\n/**\n * The working directory a command runs in, taking the leftmost stage of a\n * pipeline, which is the stage `setCwd` and the spawners agree to treat as the\n * pipeline's own directory.\n *\n * @category rendering\n * @since 1.0.0-rc.0\n */\nexport const cwd = (command: ChildProcess.Command): string | undefined =>\n command._tag === \"StandardCommand\" ? command.options.cwd : cwd(command.left)\n\n/**\n * The environment overrides a command runs with, taking the leftmost stage of\n * a pipeline for the same reason {@link cwd} does.\n *\n * @category rendering\n * @since 1.0.0-rc.0\n */\nexport const env = (\n command: ChildProcess.Command\n): Record<string, string | undefined> | undefined =>\n command._tag === \"StandardCommand\" ? command.options.env : env(command.left)\n"]}
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
import * as Effect from "effect/Effect";
|
|
27
27
|
import * as Layer from "effect/Layer";
|
|
28
28
|
import * as PlatformError from "effect/PlatformError";
|
|
29
|
+
import * as ChildProcess from "effect/process/ChildProcess";
|
|
30
|
+
import { type ChildProcessHandle, ChildProcessSpawner } from "effect/process/ChildProcessSpawner";
|
|
29
31
|
import * as Scope from "effect/Scope";
|
|
30
|
-
import * as ChildProcess from "effect/unstable/process/ChildProcess";
|
|
31
|
-
import { type ChildProcessHandle, ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner";
|
|
32
32
|
import * as ProcessLedger from "./ProcessLedger.ts";
|
|
33
33
|
/**
|
|
34
34
|
* How long a contained child may take to honour `SIGTERM`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContainedSpawner.d.ts","sourceRoot":"","sources":["../../src/ContainedSpawner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,aAAa,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"ContainedSpawner.d.ts","sourceRoot":"","sources":["../../src/ContainedSpawner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,aAAa,MAAM,sBAAsB,CAAA;AACrD,OAAO,KAAK,YAAY,MAAM,6BAA6B,CAAA;AAC3D,OAAO,EAAE,KAAK,kBAAkB,EAAE,mBAAmB,EAAuB,MAAM,oCAAoC,CAAA;AAEtH,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAIrC,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AAEnD;;;;;GAKG;AACH,eAAO,MAAM,cAAc,OAAO,CAAA;AAElC;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACvC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,SAAS,GAAG,CACtB,OAAO,EAAE,YAAY,CAAC,eAAe,EACrC,KAAK,EAAE,CACL,OAAO,EAAE,YAAY,CAAC,eAAe,KAClC,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,aAAa,CAAC,aAAa,EAAE,KAAK,CAAC,KAAK,CAAC,KAC7E,MAAM,CAAC,MAAM,CAChB;IACE,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAA;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,aAAa,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IAChF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;CACzC,EACD,aAAa,CAAC,aAAa,EAC3B,KAAK,CAAC,KAAK,CACZ,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,WAAW,GAAI,SAAS,mBAAmB,CAAC,SAAS,CAAC,KAAG,OAA2C,CAAA;AAEjH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,GAC1B,SAAS,YAAY,CAAC,OAAO,EAC7B,UAAU,OAAO,KAChB,YAAY,CAAC,OAcf,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,OAAO,GAAI,SAAS,YAAY,CAAC,OAAO,EAAE,KAAK,MAAM,EAAE,WAAW,MAAM,KAAG,MAAM,GAAG,IAIhG,CAAA;AAKD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,KAAK,GAChB,UAAU,OAAO,EACjB,YAAY,SAAS,KACpB,KAAK,CAAC,KAAK,CAAC,mBAAmB,EAAE,KAAK,EAAE,mBAAmB,GAAG,aAAa,CAAC,aAAa,CAmEzF,CAAA"}
|
|
@@ -27,10 +27,10 @@ import * as Effect from "effect/Effect";
|
|
|
27
27
|
import * as Exit from "effect/Exit";
|
|
28
28
|
import * as Layer from "effect/Layer";
|
|
29
29
|
import * as PlatformError from "effect/PlatformError";
|
|
30
|
+
import * as ChildProcess from "effect/process/ChildProcess";
|
|
31
|
+
import { ChildProcessSpawner, make as makeSpawner } from "effect/process/ChildProcessSpawner";
|
|
30
32
|
import * as Schedule from "effect/Schedule";
|
|
31
33
|
import * as Scope from "effect/Scope";
|
|
32
|
-
import * as ChildProcess from "effect/unstable/process/ChildProcess";
|
|
33
|
-
import { ChildProcessSpawner, make as makeSpawner } from "effect/unstable/process/ChildProcessSpawner";
|
|
34
34
|
import * as CommandLine from "./CommandLine.js";
|
|
35
35
|
import * as Containment from "./internal/Containment.js";
|
|
36
36
|
import * as Pipeline from "./internal/Pipeline.js";
|