@smthrs/kernel 0.0.0-stage → 1.0.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +206 -0
- package/LICENSE +21 -0
- package/README.md +154 -2
- package/dist/cjs/CapabilitySet.d.ts +6 -0
- package/dist/cjs/CapabilitySet.d.ts.map +1 -0
- package/dist/cjs/CapabilitySet.js +19 -0
- package/dist/cjs/CapabilitySet.js.map +7 -0
- package/dist/cjs/ChildProcessEnvironment.d.ts +60 -0
- package/dist/cjs/ChildProcessEnvironment.d.ts.map +1 -0
- package/dist/cjs/ChildProcessEnvironment.js +61 -0
- package/dist/cjs/ChildProcessEnvironment.js.map +7 -0
- package/dist/cjs/ChildProcessSpawner.d.ts +100 -0
- package/dist/cjs/ChildProcessSpawner.d.ts.map +1 -0
- package/dist/cjs/ChildProcessSpawner.js +291 -0
- package/dist/cjs/ChildProcessSpawner.js.map +7 -0
- package/dist/cjs/CommandLine.d.ts +173 -0
- package/dist/cjs/CommandLine.d.ts.map +1 -0
- package/dist/cjs/CommandLine.js +54 -0
- package/dist/cjs/CommandLine.js.map +7 -0
- package/dist/cjs/ContainedSpawner.d.ts +135 -0
- package/dist/cjs/ContainedSpawner.d.ts.map +1 -0
- package/dist/cjs/ContainedSpawner.js +141 -0
- package/dist/cjs/ContainedSpawner.js.map +7 -0
- package/dist/cjs/FileSystem.d.ts +452 -0
- package/dist/cjs/FileSystem.d.ts.map +1 -0
- package/dist/cjs/FileSystem.js +890 -0
- package/dist/cjs/FileSystem.js.map +7 -0
- package/dist/cjs/FileSystemBatch.d.ts +94 -0
- package/dist/cjs/FileSystemBatch.d.ts.map +1 -0
- package/dist/cjs/FileSystemBatch.js +31 -0
- package/dist/cjs/FileSystemBatch.js.map +7 -0
- package/dist/cjs/GrantEvent.d.ts +240 -0
- package/dist/cjs/GrantEvent.d.ts.map +1 -0
- package/dist/cjs/GrantEvent.js +115 -0
- package/dist/cjs/GrantEvent.js.map +7 -0
- package/dist/cjs/GrantStore.d.ts +270 -0
- package/dist/cjs/GrantStore.d.ts.map +1 -0
- package/dist/cjs/GrantStore.js +857 -0
- package/dist/cjs/GrantStore.js.map +7 -0
- package/dist/cjs/HostError.d.ts +36 -0
- package/dist/cjs/HostError.d.ts.map +1 -0
- package/dist/cjs/HostError.js +17 -0
- package/dist/cjs/HostError.js.map +7 -0
- package/dist/cjs/HostServices.d.ts +101 -0
- package/dist/cjs/HostServices.d.ts.map +1 -0
- package/dist/cjs/HostServices.js +66 -0
- package/dist/cjs/HostServices.js.map +7 -0
- package/dist/cjs/HttpClient.d.ts +177 -0
- package/dist/cjs/HttpClient.d.ts.map +1 -0
- package/dist/cjs/HttpClient.js +217 -0
- package/dist/cjs/HttpClient.js.map +7 -0
- package/dist/cjs/Jj.d.ts +67 -0
- package/dist/cjs/Jj.d.ts.map +1 -0
- package/dist/cjs/Jj.js +118 -0
- package/dist/cjs/Jj.js.map +7 -0
- package/dist/cjs/JournalGrantStore.d.ts +65 -0
- package/dist/cjs/JournalGrantStore.d.ts.map +1 -0
- package/dist/cjs/JournalGrantStore.js +354 -0
- package/dist/cjs/JournalGrantStore.js.map +7 -0
- package/dist/cjs/Path.d.ts +37 -0
- package/dist/cjs/Path.d.ts.map +1 -0
- package/dist/cjs/Path.js +46 -0
- package/dist/cjs/Path.js.map +7 -0
- package/dist/cjs/ProcessConfinement.d.ts +55 -0
- package/dist/cjs/ProcessConfinement.d.ts.map +1 -0
- package/dist/cjs/ProcessConfinement.js +94 -0
- package/dist/cjs/ProcessConfinement.js.map +7 -0
- package/dist/cjs/ProcessLedger.d.ts +260 -0
- package/dist/cjs/ProcessLedger.d.ts.map +1 -0
- package/dist/cjs/ProcessLedger.js +190 -0
- package/dist/cjs/ProcessLedger.js.map +7 -0
- package/dist/cjs/Rooted.d.ts +53 -0
- package/dist/cjs/Rooted.d.ts.map +1 -0
- package/dist/cjs/Rooted.js +101 -0
- package/dist/cjs/Rooted.js.map +7 -0
- package/dist/cjs/Unreachable.d.ts +27 -0
- package/dist/cjs/Unreachable.d.ts.map +1 -0
- package/dist/cjs/Unreachable.js +43 -0
- package/dist/cjs/Unreachable.js.map +7 -0
- package/dist/cjs/Workspace.d.ts +62 -0
- package/dist/cjs/Workspace.d.ts.map +1 -0
- package/dist/cjs/Workspace.js +35 -0
- package/dist/cjs/Workspace.js.map +7 -0
- package/dist/cjs/index.d.ts +161 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +73 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/Containment.d.ts +24 -0
- package/dist/cjs/internal/Containment.d.ts.map +1 -0
- package/dist/cjs/internal/Containment.js +30 -0
- package/dist/cjs/internal/Containment.js.map +7 -0
- package/dist/cjs/internal/Pipeline.d.ts +18 -0
- package/dist/cjs/internal/Pipeline.d.ts.map +1 -0
- package/dist/cjs/internal/Pipeline.js +90 -0
- package/dist/cjs/internal/Pipeline.js.map +7 -0
- package/dist/cjs/internal/makeCapability.d.ts +16 -0
- package/dist/cjs/internal/makeCapability.d.ts.map +1 -0
- package/dist/cjs/internal/makeCapability.js +44 -0
- package/dist/cjs/internal/makeCapability.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/test/FileSystemBatchContract.d.ts +15 -0
- package/dist/cjs/test/FileSystemBatchContract.d.ts.map +1 -0
- package/dist/cjs/test/FileSystemBatchContract.js +76 -0
- package/dist/cjs/test/FileSystemBatchContract.js.map +7 -0
- package/dist/cjs/test/HostContract.d.ts +225 -0
- package/dist/cjs/test/HostContract.d.ts.map +1 -0
- package/dist/cjs/test/HostContract.js +489 -0
- package/dist/cjs/test/HostContract.js.map +7 -0
- package/dist/cjs/test/TestGrantStore.d.ts +31 -0
- package/dist/cjs/test/TestGrantStore.d.ts.map +1 -0
- package/dist/cjs/test/TestGrantStore.js +65 -0
- package/dist/cjs/test/TestGrantStore.js.map +7 -0
- package/dist/esm/CapabilitySet.d.ts +6 -0
- package/dist/esm/CapabilitySet.d.ts.map +1 -0
- package/dist/esm/CapabilitySet.js +6 -0
- package/dist/esm/CapabilitySet.js.map +1 -0
- package/dist/esm/ChildProcessEnvironment.d.ts +60 -0
- package/dist/esm/ChildProcessEnvironment.d.ts.map +1 -0
- package/dist/esm/ChildProcessEnvironment.js +91 -0
- package/dist/esm/ChildProcessEnvironment.js.map +1 -0
- package/dist/esm/ChildProcessSpawner.d.ts +100 -0
- package/dist/esm/ChildProcessSpawner.d.ts.map +1 -0
- package/dist/esm/ChildProcessSpawner.js +342 -0
- package/dist/esm/ChildProcessSpawner.js.map +1 -0
- package/dist/esm/CommandLine.d.ts +173 -0
- package/dist/esm/CommandLine.d.ts.map +1 -0
- package/dist/esm/CommandLine.js +195 -0
- package/dist/esm/CommandLine.js.map +1 -0
- package/dist/esm/ContainedSpawner.d.ts +135 -0
- package/dist/esm/ContainedSpawner.d.ts.map +1 -0
- package/dist/esm/ContainedSpawner.js +180 -0
- package/dist/esm/ContainedSpawner.js.map +1 -0
- package/dist/esm/FileSystem.d.ts +452 -0
- package/dist/esm/FileSystem.d.ts.map +1 -0
- package/dist/esm/FileSystem.js +989 -0
- package/dist/esm/FileSystem.js.map +1 -0
- package/dist/esm/FileSystemBatch.d.ts +94 -0
- package/dist/esm/FileSystemBatch.d.ts.map +1 -0
- package/dist/esm/FileSystemBatch.js +26 -0
- package/dist/esm/FileSystemBatch.js.map +1 -0
- package/dist/esm/GrantEvent.d.ts +240 -0
- package/dist/esm/GrantEvent.d.ts.map +1 -0
- package/dist/esm/GrantEvent.js +155 -0
- package/dist/esm/GrantEvent.js.map +1 -0
- package/dist/esm/GrantStore.d.ts +270 -0
- package/dist/esm/GrantStore.d.ts.map +1 -0
- package/dist/esm/GrantStore.js +951 -0
- package/dist/esm/GrantStore.js.map +1 -0
- package/dist/esm/HostError.d.ts +36 -0
- package/dist/esm/HostError.d.ts.map +1 -0
- package/dist/esm/HostError.js +20 -0
- package/dist/esm/HostError.js.map +1 -0
- package/dist/esm/HostServices.d.ts +101 -0
- package/dist/esm/HostServices.d.ts.map +1 -0
- package/dist/esm/HostServices.js +76 -0
- package/dist/esm/HostServices.js.map +1 -0
- package/dist/esm/HttpClient.d.ts +177 -0
- package/dist/esm/HttpClient.d.ts.map +1 -0
- package/dist/esm/HttpClient.js +299 -0
- package/dist/esm/HttpClient.js.map +1 -0
- package/dist/esm/Jj.d.ts +67 -0
- package/dist/esm/Jj.d.ts.map +1 -0
- package/dist/esm/Jj.js +111 -0
- package/dist/esm/Jj.js.map +1 -0
- package/dist/esm/JournalGrantStore.d.ts +65 -0
- package/dist/esm/JournalGrantStore.d.ts.map +1 -0
- package/dist/esm/JournalGrantStore.js +350 -0
- package/dist/esm/JournalGrantStore.js.map +1 -0
- package/dist/esm/Path.d.ts +37 -0
- package/dist/esm/Path.d.ts.map +1 -0
- package/dist/esm/Path.js +34 -0
- package/dist/esm/Path.js.map +1 -0
- package/dist/esm/ProcessConfinement.d.ts +55 -0
- package/dist/esm/ProcessConfinement.d.ts.map +1 -0
- package/dist/esm/ProcessConfinement.js +106 -0
- package/dist/esm/ProcessConfinement.js.map +1 -0
- package/dist/esm/ProcessLedger.d.ts +260 -0
- package/dist/esm/ProcessLedger.d.ts.map +1 -0
- package/dist/esm/ProcessLedger.js +283 -0
- package/dist/esm/ProcessLedger.js.map +1 -0
- package/dist/esm/Rooted.d.ts +53 -0
- package/dist/esm/Rooted.d.ts.map +1 -0
- package/dist/esm/Rooted.js +104 -0
- package/dist/esm/Rooted.js.map +1 -0
- package/dist/esm/Unreachable.d.ts +27 -0
- package/dist/esm/Unreachable.d.ts.map +1 -0
- package/dist/esm/Unreachable.js +26 -0
- package/dist/esm/Unreachable.js.map +1 -0
- package/dist/esm/Workspace.d.ts +62 -0
- package/dist/esm/Workspace.d.ts.map +1 -0
- package/dist/esm/Workspace.js +51 -0
- package/dist/esm/Workspace.js.map +1 -0
- package/dist/esm/index.d.ts +161 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +151 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/Containment.d.ts +24 -0
- package/dist/esm/internal/Containment.d.ts.map +1 -0
- package/dist/esm/internal/Containment.js +27 -0
- package/dist/esm/internal/Containment.js.map +1 -0
- package/dist/esm/internal/Pipeline.d.ts +18 -0
- package/dist/esm/internal/Pipeline.d.ts.map +1 -0
- package/dist/esm/internal/Pipeline.js +68 -0
- package/dist/esm/internal/Pipeline.js.map +1 -0
- package/dist/esm/internal/makeCapability.d.ts +16 -0
- package/dist/esm/internal/makeCapability.d.ts.map +1 -0
- package/dist/esm/internal/makeCapability.js +22 -0
- package/dist/esm/internal/makeCapability.js.map +1 -0
- package/dist/esm/test/FileSystemBatchContract.d.ts +15 -0
- package/dist/esm/test/FileSystemBatchContract.d.ts.map +1 -0
- package/dist/esm/test/FileSystemBatchContract.js +57 -0
- package/dist/esm/test/FileSystemBatchContract.js.map +1 -0
- package/dist/esm/test/HostContract.d.ts +225 -0
- package/dist/esm/test/HostContract.d.ts.map +1 -0
- package/dist/esm/test/HostContract.js +460 -0
- package/dist/esm/test/HostContract.js.map +1 -0
- package/dist/esm/test/TestGrantStore.d.ts +31 -0
- package/dist/esm/test/TestGrantStore.d.ts.map +1 -0
- package/dist/esm/test/TestGrantStore.js +57 -0
- package/dist/esm/test/TestGrantStore.js.map +1 -0
- package/docs/README.md +162 -0
- package/docs/api.md +1532 -0
- package/docs/concepts/decoration-in-place.md +117 -0
- package/docs/concepts/filesystem-batches.md +58 -0
- package/docs/concepts/filesystem-confinement.md +168 -0
- package/docs/concepts/grant-decisions.md +181 -0
- package/docs/concepts/process-containment.md +155 -0
- package/docs/contract.md +14 -0
- package/docs/guides/adapt-a-new-host-platform.md +148 -0
- package/docs/guides/answer-permission-requests.md +151 -0
- package/docs/guides/authorize-network-and-model-calls.md +118 -0
- package/docs/guides/contain-spawned-processes.md +118 -0
- package/docs/guides/guard-a-host-bundle.md +109 -0
- package/docs/guides/persist-grants-across-restarts.md +135 -0
- package/docs/guides/write-a-capability-policy.md +159 -0
- package/docs/installation.md +115 -0
- package/docs/quickstart.md +182 -0
- package/docs/testing.md +108 -0
- package/docs/troubleshooting.md +281 -0
- package/package.json +344 -3
- package/src/CapabilitySet.ts +6 -0
- package/src/ChildProcessEnvironment.ts +98 -0
- package/src/ChildProcessSpawner.ts +414 -0
- package/src/CommandLine.ts +250 -0
- package/src/ContainedSpawner.ts +284 -0
- package/src/FileSystem.ts +1655 -0
- package/src/FileSystemBatch.ts +90 -0
- package/src/GrantEvent.ts +174 -0
- package/src/GrantStore.ts +1345 -0
- package/src/HostError.ts +37 -0
- package/src/HostServices.ts +133 -0
- package/src/HttpClient.ts +406 -0
- package/src/Jj.ts +159 -0
- package/src/JournalGrantStore.ts +461 -0
- package/src/Path.ts +47 -0
- package/src/ProcessConfinement.ts +156 -0
- package/src/ProcessLedger.ts +423 -0
- package/src/Rooted.ts +128 -0
- package/src/Unreachable.ts +30 -0
- package/src/Workspace.ts +65 -0
- package/src/index.ts +181 -0
- package/src/internal/Containment.ts +37 -0
- package/src/internal/Pipeline.ts +85 -0
- package/src/internal/makeCapability.ts +26 -0
- package/src/test/FileSystemBatchContract.ts +59 -0
- package/src/test/HostContract.ts +785 -0
- package/src/test/TestGrantStore.ts +73 -0
package/src/Rooted.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem, path and process services bound to one root directory.
|
|
3
|
+
*
|
|
4
|
+
* Node resolves a relative path against `process.cwd()`. A host serves one
|
|
5
|
+
* directory whatever the process's own, so here every relative path, and every
|
|
6
|
+
* command spawned without a `cwd`, resolves against `root`. Absolute paths pass
|
|
7
|
+
* through unchanged, and so does a symlink's target, which is relative to the
|
|
8
|
+
* link rather than to any directory.
|
|
9
|
+
*
|
|
10
|
+
* @since 1.0.0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { Context, Effect, FileSystem, Layer, Path } from "effect"
|
|
14
|
+
import * as ChildProcess from "effect/unstable/process/ChildProcess"
|
|
15
|
+
import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner"
|
|
16
|
+
import * as Containment from "./internal/Containment.ts"
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* `base` with every relative path resolved against the absolute `root`.
|
|
20
|
+
*
|
|
21
|
+
* @category constructors
|
|
22
|
+
* @since 1.0.0
|
|
23
|
+
*/
|
|
24
|
+
export const path = (base: Path.Path, root: string): Path.Path => {
|
|
25
|
+
const resolve = (...segments: ReadonlyArray<string>) => base.resolve(root, ...segments)
|
|
26
|
+
return {
|
|
27
|
+
...base,
|
|
28
|
+
resolve,
|
|
29
|
+
relative: (from, to) => base.relative(resolve(from), resolve(to)),
|
|
30
|
+
toFileUrl: (value) => base.toFileUrl(resolve(value))
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* `base` with every relative path resolved by `rooted`, a {@link path}.
|
|
36
|
+
*
|
|
37
|
+
* @category constructors
|
|
38
|
+
* @since 1.0.0
|
|
39
|
+
*/
|
|
40
|
+
export const fileSystem = (base: FileSystem.FileSystem, rooted: Path.Path): FileSystem.FileSystem => {
|
|
41
|
+
const at = (value: string) => rooted.resolve(value)
|
|
42
|
+
const temp = <O extends { readonly directory?: string | undefined }>(options: O | undefined) =>
|
|
43
|
+
options?.directory === undefined ? options : { ...options, directory: at(options.directory) }
|
|
44
|
+
return {
|
|
45
|
+
...base,
|
|
46
|
+
access: (value, options) => base.access(at(value), options),
|
|
47
|
+
copy: (from, to, options) => base.copy(at(from), at(to), options),
|
|
48
|
+
copyFile: (from, to) => base.copyFile(at(from), at(to)),
|
|
49
|
+
chmod: (value, mode) => base.chmod(at(value), mode),
|
|
50
|
+
chown: (value, uid, gid) => base.chown(at(value), uid, gid),
|
|
51
|
+
glob: (pattern, options) => base.glob(pattern, { ...options, root: at(options?.root ?? ".") }),
|
|
52
|
+
exists: (value) => base.exists(at(value)),
|
|
53
|
+
link: (from, to) => base.link(at(from), at(to)),
|
|
54
|
+
makeDirectory: (value, options) => base.makeDirectory(at(value), options),
|
|
55
|
+
makeTempDirectory: (options) => base.makeTempDirectory(temp(options)),
|
|
56
|
+
makeTempDirectoryScoped: (options) => base.makeTempDirectoryScoped(temp(options)),
|
|
57
|
+
makeTempFile: (options) => base.makeTempFile(temp(options)),
|
|
58
|
+
makeTempFileScoped: (options) => base.makeTempFileScoped(temp(options)),
|
|
59
|
+
open: (value, options) => base.open(at(value), options),
|
|
60
|
+
readDirectory: (value, options) => base.readDirectory(at(value), options),
|
|
61
|
+
readFile: (value) => base.readFile(at(value)),
|
|
62
|
+
readFileString: (value, encoding) => base.readFileString(at(value), encoding),
|
|
63
|
+
readLink: (value) => base.readLink(at(value)),
|
|
64
|
+
realPath: (value) => base.realPath(at(value)),
|
|
65
|
+
remove: (value, options) => base.remove(at(value), options),
|
|
66
|
+
rename: (from, to) => base.rename(at(from), at(to)),
|
|
67
|
+
sink: (value, options) => base.sink(at(value), options),
|
|
68
|
+
stat: (value) => base.stat(at(value)),
|
|
69
|
+
stream: (value, options) => base.stream(at(value), options),
|
|
70
|
+
symlink: (target, value) => base.symlink(target, at(value)),
|
|
71
|
+
truncate: (value, length) => base.truncate(at(value), length),
|
|
72
|
+
utimes: (value, atime, mtime) => base.utimes(at(value), atime, mtime),
|
|
73
|
+
watch: (value, options) => base.watch(at(value), options),
|
|
74
|
+
writeFile: (value, data, options) => base.writeFile(at(value), data, options),
|
|
75
|
+
writeFileString: (value, data, options) => base.writeFileString(at(value), data, options)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* `value` with every stage's `cwd` resolved by `rooted`, a {@link path}; its
|
|
81
|
+
* root for a stage without one.
|
|
82
|
+
*
|
|
83
|
+
* @category constructors
|
|
84
|
+
* @since 1.0.0
|
|
85
|
+
*/
|
|
86
|
+
export const command = (value: ChildProcess.Command, rooted: Path.Path): ChildProcess.Command =>
|
|
87
|
+
ChildProcess.isStandardCommand(value)
|
|
88
|
+
? ChildProcess.setCwd(value, rooted.resolve(value.options.cwd ?? "."))
|
|
89
|
+
: ChildProcess.pipeTo(command(value.left, rooted), command(value.right, rooted), value.options)
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* `base` spawning each command as {@link command} roots it. A contained `base`
|
|
93
|
+
* stays contained.
|
|
94
|
+
*
|
|
95
|
+
* @category constructors
|
|
96
|
+
* @since 1.0.0
|
|
97
|
+
*/
|
|
98
|
+
export const spawner = (
|
|
99
|
+
base: ChildProcessSpawner.ChildProcessSpawner["Service"],
|
|
100
|
+
rooted: Path.Path
|
|
101
|
+
): ChildProcessSpawner.ChildProcessSpawner["Service"] =>
|
|
102
|
+
Containment.inherit(base, ChildProcessSpawner.make((value) => base.spawn(command(value, rooted))))
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Decorates the `FileSystem`, `Path` and `ChildProcessSpawner` it is provided
|
|
106
|
+
* in place, so everything downstream resolves against `root`.
|
|
107
|
+
*
|
|
108
|
+
* @category layers
|
|
109
|
+
* @since 1.0.0
|
|
110
|
+
*/
|
|
111
|
+
export const layer = (
|
|
112
|
+
root: string
|
|
113
|
+
): Layer.Layer<
|
|
114
|
+
FileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner,
|
|
115
|
+
never,
|
|
116
|
+
FileSystem.FileSystem | Path.Path | ChildProcessSpawner.ChildProcessSpawner
|
|
117
|
+
> =>
|
|
118
|
+
Layer.effectContext(Effect.gen(function*() {
|
|
119
|
+
const base = yield* Path.Path
|
|
120
|
+
const rooted = path(base, base.resolve(root))
|
|
121
|
+
return Context.make(Path.Path, rooted).pipe(
|
|
122
|
+
Context.add(FileSystem.FileSystem, fileSystem(yield* FileSystem.FileSystem, rooted)),
|
|
123
|
+
Context.add(
|
|
124
|
+
ChildProcessSpawner.ChildProcessSpawner,
|
|
125
|
+
spawner(yield* ChildProcessSpawner.ChildProcessSpawner, rooted)
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
}))
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed host connectivity failures, including failed command diagnostics.
|
|
3
|
+
*
|
|
4
|
+
* @since 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as Schema from "effect/Schema"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A temporarily unreachable resolver, connection, or HTTP service.
|
|
11
|
+
*
|
|
12
|
+
* @category errors
|
|
13
|
+
* @since 1.0.0
|
|
14
|
+
*/
|
|
15
|
+
export class Unreachable extends Schema.TaggedError<Unreachable>()("@smthrs/kernel/Unreachable", {
|
|
16
|
+
message: Schema.String,
|
|
17
|
+
cause: Schema.optional(Schema.Unknown)
|
|
18
|
+
}) {}
|
|
19
|
+
|
|
20
|
+
const outage =
|
|
21
|
+
/Could not resolve host|Failed to connect to|Connection timed out|Operation timed out|Connection reset|Recv failure|Network is unreachable|Temporary failure in name resolution|SSL_ERROR_SYSCALL|The remote end hung up unexpectedly|\bEAI_AGAIN\b|\bENOTFOUND\b|\bHTTP(?:\/\d(?:\.\d)?)?\s*(?:(?:error|status|code)\s*[:=]?\s*)?[:=]?\s*(?:429|5\d{2})\b/i
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Classifies stderr from a failed host command; other exits keep their domain error.
|
|
25
|
+
*
|
|
26
|
+
* @category constructors
|
|
27
|
+
* @since 1.0.0
|
|
28
|
+
*/
|
|
29
|
+
export const classifyExit = (stderr: string): Unreachable | undefined =>
|
|
30
|
+
outage.test(stderr) ? new Unreachable({ message: stderr }) : undefined
|
package/src/Workspace.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The workspace root used to make filesystem capability resources stable.
|
|
3
|
+
*
|
|
4
|
+
* It lives beside the closed host list rather than in a platform package: a
|
|
5
|
+
* workspace root is a policy decision the kernel needs before it can name a
|
|
6
|
+
* filesystem capability, not something a platform can answer.
|
|
7
|
+
*
|
|
8
|
+
* Governing design:
|
|
9
|
+
* `docs/specs/Concepts/Effect Taxonomy.md` and
|
|
10
|
+
* `docs/specs/Concepts/Permission Kernel.md`.
|
|
11
|
+
*
|
|
12
|
+
* @since 1.0.0-rc.0
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { Context, Layer } from "effect"
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Workspace-root configuration.
|
|
19
|
+
*
|
|
20
|
+
* @category models
|
|
21
|
+
* @since 1.0.0-rc.0
|
|
22
|
+
*/
|
|
23
|
+
export interface Service {
|
|
24
|
+
readonly root: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The workspace-root service tag.
|
|
29
|
+
*
|
|
30
|
+
* @category services
|
|
31
|
+
* @since 1.0.0-rc.0
|
|
32
|
+
*/
|
|
33
|
+
export class Workspace extends Context.Service<Workspace, Service>()("@smthrs/kernel/Workspace") {}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Constructs workspace-root configuration.
|
|
37
|
+
*
|
|
38
|
+
* @category constructors
|
|
39
|
+
* @since 1.0.0-rc.0
|
|
40
|
+
*/
|
|
41
|
+
export const make = (root: string): Service => Workspace.of({ root })
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Provides a workspace root.
|
|
45
|
+
*
|
|
46
|
+
* @category layers
|
|
47
|
+
* @since 1.0.0-rc.0
|
|
48
|
+
*/
|
|
49
|
+
export const layer = (root: string): Layer.Layer<Workspace> => Layer.succeed(Workspace, make(root))
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Constructs a relative workspace-root stub for tests.
|
|
53
|
+
*
|
|
54
|
+
* @category constructors
|
|
55
|
+
* @since 1.0.0-rc.0
|
|
56
|
+
*/
|
|
57
|
+
export const makeNoop: Service = make(".")
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Provides a relative workspace-root stub for tests.
|
|
61
|
+
*
|
|
62
|
+
* @category layers
|
|
63
|
+
* @since 1.0.0-rc.0
|
|
64
|
+
*/
|
|
65
|
+
export const layerNoop: Layer.Layer<Workspace> = Layer.succeed(Workspace, makeNoop)
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability enforcement at the Host boundary.
|
|
3
|
+
*
|
|
4
|
+
* Every slot of the closed Host surface is decorated in place: a middleware
|
|
5
|
+
* `Layer` over the very tag a platform adapter provides, checking a capability
|
|
6
|
+
* against a `GrantStore` before it delegates. There is no second, protected
|
|
7
|
+
* tag, so code that never heard of the kernel is guarded the moment the layer
|
|
8
|
+
* is composed over its host.
|
|
9
|
+
*
|
|
10
|
+
* @since 1.0.0-rc.0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Capability values, patterns, matching, and effect tiers.
|
|
15
|
+
*
|
|
16
|
+
* @category namespace exports
|
|
17
|
+
* @since 1.0.0-rc.0
|
|
18
|
+
*/
|
|
19
|
+
export * as Capability from "@smthrs/capability/Capability"
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Monotone ambient authority and intersection.
|
|
23
|
+
*
|
|
24
|
+
* @category namespace exports
|
|
25
|
+
* @since 1.0.0-rc.0
|
|
26
|
+
*/
|
|
27
|
+
export * as CapabilitySet from "./CapabilitySet.ts"
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The `proc:spawn` middleware over Effect's own `ChildProcessSpawner` tag.
|
|
31
|
+
*
|
|
32
|
+
* @category namespace exports
|
|
33
|
+
* @since 1.0.0-rc.0
|
|
34
|
+
*/
|
|
35
|
+
export * as ChildProcessSpawner from "./ChildProcessSpawner.ts"
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Least-authority child-process environment construction.
|
|
39
|
+
*
|
|
40
|
+
* @category namespace exports
|
|
41
|
+
* @since 1.0.0-rc.0
|
|
42
|
+
*/
|
|
43
|
+
export * as ChildProcessEnvironment from "./ChildProcessEnvironment.ts"
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Process containment middleware over Effect's own `ChildProcessSpawner` tag.
|
|
47
|
+
*
|
|
48
|
+
* @category namespace exports
|
|
49
|
+
* @since 1.0.0-rc.0
|
|
50
|
+
*/
|
|
51
|
+
export * as ContainedSpawner from "./ContainedSpawner.ts"
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Rendering `ChildProcess` commands back to command lines.
|
|
55
|
+
*
|
|
56
|
+
* @category namespace exports
|
|
57
|
+
* @since 1.0.0-rc.0
|
|
58
|
+
*/
|
|
59
|
+
export * as CommandLine from "./CommandLine.ts"
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The `fs:read`/`fs:write` middleware over Effect's own `FileSystem` tag.
|
|
63
|
+
*
|
|
64
|
+
* @category namespace exports
|
|
65
|
+
* @since 1.0.0-rc.0
|
|
66
|
+
*/
|
|
67
|
+
export * as FileSystem from "./FileSystem.ts"
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Durable grant decision schemas.
|
|
71
|
+
*
|
|
72
|
+
* @category namespace exports
|
|
73
|
+
* @since 1.0.0-rc.0
|
|
74
|
+
*/
|
|
75
|
+
export * as GrantEvent from "./GrantEvent.ts"
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Attended and unattended grant-store services.
|
|
79
|
+
*
|
|
80
|
+
* @category namespace exports
|
|
81
|
+
* @since 1.0.0-rc.0
|
|
82
|
+
*/
|
|
83
|
+
export * as GrantStore from "./GrantStore.ts"
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The union of failures the closed Host surface is allowed to produce.
|
|
87
|
+
*
|
|
88
|
+
* A type, not a namespace: process execution now fails with Effect's own
|
|
89
|
+
* `PlatformError`, so nothing is left here to construct.
|
|
90
|
+
*
|
|
91
|
+
* @category models
|
|
92
|
+
* @since 1.0.0-rc.0
|
|
93
|
+
*/
|
|
94
|
+
export type { HostError } from "./HostError.ts"
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The closed protected Host service layer.
|
|
98
|
+
*
|
|
99
|
+
* @category namespace exports
|
|
100
|
+
* @since 1.0.0-rc.0
|
|
101
|
+
*/
|
|
102
|
+
export * as HostServices from "./HostServices.ts"
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The `net:get`/`net:post`/`model:call` middleware over Effect's own
|
|
106
|
+
* `HttpClient` tag.
|
|
107
|
+
*
|
|
108
|
+
* @category namespace exports
|
|
109
|
+
* @since 1.0.0-rc.0
|
|
110
|
+
*/
|
|
111
|
+
export * as HttpClient from "./HttpClient.ts"
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The jj capability middleware over `@smthrs/jj`'s own `Jj` tag.
|
|
115
|
+
*
|
|
116
|
+
* @category namespace exports
|
|
117
|
+
* @since 1.0.0-rc.0
|
|
118
|
+
*/
|
|
119
|
+
export * as Jj from "./Jj.ts"
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Journal-backed grant persistence.
|
|
123
|
+
*
|
|
124
|
+
* @category namespace exports
|
|
125
|
+
* @since 1.0.0-rc.0
|
|
126
|
+
*/
|
|
127
|
+
export * as JournalGrantStore from "./JournalGrantStore.ts"
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The explicit pure path-service decision.
|
|
131
|
+
*
|
|
132
|
+
* @category namespace exports
|
|
133
|
+
* @since 1.0.0-rc.0
|
|
134
|
+
*/
|
|
135
|
+
export * as Path from "./Path.ts"
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Typed permission errors and policy rules.
|
|
139
|
+
*
|
|
140
|
+
* @category namespace exports
|
|
141
|
+
* @since 1.0.0-rc.0
|
|
142
|
+
*/
|
|
143
|
+
export * as Permission from "@smthrs/capability/Permission"
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The host's durable record of the processes it started.
|
|
147
|
+
*
|
|
148
|
+
* @category namespace exports
|
|
149
|
+
* @since 1.0.0-rc.0
|
|
150
|
+
*/
|
|
151
|
+
export * as ProcessLedger from "./ProcessLedger.ts"
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Filesystem, path and process services bound to one root directory.
|
|
155
|
+
*
|
|
156
|
+
* @category namespace exports
|
|
157
|
+
* @since 1.0.0
|
|
158
|
+
*/
|
|
159
|
+
export * as Rooted from "./Rooted.ts"
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The shared workspace-root service.
|
|
163
|
+
*
|
|
164
|
+
* @category namespace exports
|
|
165
|
+
* @since 1.0.0-rc.0
|
|
166
|
+
*/
|
|
167
|
+
export * as Workspace from "./Workspace.ts"
|
|
168
|
+
|
|
169
|
+
/** Host process confinement.
|
|
170
|
+
* @category namespace exports
|
|
171
|
+
* @since 1.0.0-rc.1
|
|
172
|
+
*/
|
|
173
|
+
export * as ProcessConfinement from "./ProcessConfinement.ts"
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Typed connectivity failures and host command exit classification.
|
|
177
|
+
*
|
|
178
|
+
* @category namespace exports
|
|
179
|
+
* @since 1.0.0
|
|
180
|
+
*/
|
|
181
|
+
export * as Unreachable from "./Unreachable.ts"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trusted composition metadata shared by contained and permission-aware spawners.
|
|
3
|
+
* @since 1.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"
|
|
7
|
+
|
|
8
|
+
// The registry key also works when a service crosses the ESM/CJS projections.
|
|
9
|
+
// This declares a trusted lifecycle contract; it cannot prove a supplied
|
|
10
|
+
// lifecycle is honest or turn process containment into a security sandbox.
|
|
11
|
+
const key = Symbol.for("@smthrs/kernel/ContainedSpawner/Lifecycle/v1")
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Reads only an own data property, without invoking an accessor.
|
|
15
|
+
* @private
|
|
16
|
+
* @since 1.0.0
|
|
17
|
+
*/
|
|
18
|
+
export const isContained = (spawner: ChildProcessSpawner["Service"]): boolean =>
|
|
19
|
+
Object.getOwnPropertyDescriptor(spawner, key)?.value === true
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Marks a newly constructed service without copying through object spreads.
|
|
23
|
+
* @private
|
|
24
|
+
* @since 1.0.0
|
|
25
|
+
*/
|
|
26
|
+
export const mark = (spawner: ChildProcessSpawner["Service"]): ChildProcessSpawner["Service"] =>
|
|
27
|
+
Object.defineProperty(spawner, key, { value: true, enumerable: false, writable: false, configurable: false })
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Preserves the declaration on a wrapper that delegates its process lifetime.
|
|
31
|
+
* @private
|
|
32
|
+
* @since 1.0.0
|
|
33
|
+
*/
|
|
34
|
+
export const inherit = (
|
|
35
|
+
source: ChildProcessSpawner["Service"],
|
|
36
|
+
wrapper: ChildProcessSpawner["Service"]
|
|
37
|
+
): ChildProcessSpawner["Service"] => isContained(source) ? mark(wrapper) : wrapper
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline wiring around a per-process spawner, so every leg has its own lifecycle.
|
|
3
|
+
* @since 1.0.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as Effect from "effect/Effect"
|
|
7
|
+
import * as Exit from "effect/Exit"
|
|
8
|
+
import type * as PlatformError from "effect/PlatformError"
|
|
9
|
+
import type * as Scope from "effect/Scope"
|
|
10
|
+
import * as Stream from "effect/Stream"
|
|
11
|
+
import * as ChildProcess from "effect/unstable/process/ChildProcess"
|
|
12
|
+
import { type ChildProcessHandle, makeHandle } from "effect/unstable/process/ChildProcessSpawner"
|
|
13
|
+
|
|
14
|
+
type Spawn = (
|
|
15
|
+
command: ChildProcess.StandardCommand
|
|
16
|
+
) => Effect.Effect<ChildProcessHandle, PlatformError.PlatformError, Scope.Scope>
|
|
17
|
+
|
|
18
|
+
const source = (handle: ChildProcessHandle, from: ChildProcess.PipeFromOption | undefined) => {
|
|
19
|
+
if (from === "stderr") return handle.stderr
|
|
20
|
+
if (from === "all") return handle.all
|
|
21
|
+
const fd = from === undefined ? undefined : ChildProcess.parseFdName(from)
|
|
22
|
+
return fd === undefined ? handle.stdout : handle.getOutputFd(fd)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** An edge feeds the first process of its destination, including a nested pipeline. */
|
|
26
|
+
const withInput = (
|
|
27
|
+
command: ChildProcess.Command,
|
|
28
|
+
stream: Stream.Stream<Uint8Array, PlatformError.PlatformError>,
|
|
29
|
+
to: ChildProcess.PipeToOption | undefined
|
|
30
|
+
): ChildProcess.Command => {
|
|
31
|
+
if (command._tag === "PipedCommand") {
|
|
32
|
+
return ChildProcess.pipeTo(withInput(command.left, stream, to), command.right, command.options)
|
|
33
|
+
}
|
|
34
|
+
const fd = to === undefined ? undefined : ChildProcess.parseFdName(to)
|
|
35
|
+
if (fd !== undefined) {
|
|
36
|
+
return ChildProcess.make(command.command, command.args, {
|
|
37
|
+
...command.options,
|
|
38
|
+
additionalFds: {
|
|
39
|
+
...command.options.additionalFds,
|
|
40
|
+
[ChildProcess.fdName(fd)]: { type: "input", stream }
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
const stdin = command.options.stdin
|
|
45
|
+
return ChildProcess.make(command.command, command.args, {
|
|
46
|
+
...command.options,
|
|
47
|
+
stdin: {
|
|
48
|
+
...(typeof stdin === "object" && !Stream.isStream(stdin) ? stdin : {}),
|
|
49
|
+
stream
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Preserve Effect's rightmost output/status and reverse-order kill/reref semantics.
|
|
56
|
+
* @private
|
|
57
|
+
* @since 1.0.0
|
|
58
|
+
*/
|
|
59
|
+
export const spawn = (
|
|
60
|
+
command: ChildProcess.Command,
|
|
61
|
+
standard: Spawn
|
|
62
|
+
): Effect.Effect<ChildProcessHandle, PlatformError.PlatformError, Scope.Scope> =>
|
|
63
|
+
Effect.gen(function*() {
|
|
64
|
+
if (command._tag === "StandardCommand") return yield* standard(command)
|
|
65
|
+
const left = yield* spawn(command.left, standard)
|
|
66
|
+
const right = yield* spawn(
|
|
67
|
+
withInput(command.right, source(left, command.options.from), command.options.to),
|
|
68
|
+
standard
|
|
69
|
+
)
|
|
70
|
+
return makeHandle({
|
|
71
|
+
...right,
|
|
72
|
+
kill: (options) =>
|
|
73
|
+
Effect.gen(function*() {
|
|
74
|
+
const rightExit = yield* Effect.exit(right.kill(options))
|
|
75
|
+
const leftExit = yield* Effect.exit(left.kill(options))
|
|
76
|
+
if (Exit.isFailure(rightExit)) return yield* Effect.failCause(rightExit.cause)
|
|
77
|
+
return yield* leftExit
|
|
78
|
+
}),
|
|
79
|
+
unref: Effect.gen(function*() {
|
|
80
|
+
const leftReref = yield* left.unref
|
|
81
|
+
const rightReref = yield* right.unref
|
|
82
|
+
return Effect.andThen(rightReref, leftReref)
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed capability construction for host decorators.
|
|
3
|
+
*
|
|
4
|
+
* @since 1.0.0-rc.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as Capability from "@smthrs/capability/Capability"
|
|
8
|
+
import { GrantStoreError } from "@smthrs/capability/Permission"
|
|
9
|
+
import { Effect } from "effect"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Constructs decorator requests without leaking schema failures as defects.
|
|
13
|
+
*
|
|
14
|
+
* @category constructors
|
|
15
|
+
* @since 1.0.0-rc.0
|
|
16
|
+
*/
|
|
17
|
+
export const makeCapability = (action: Capability.Action, resource: string) =>
|
|
18
|
+
Effect.try({
|
|
19
|
+
try: () => Capability.make(action, resource),
|
|
20
|
+
catch: () =>
|
|
21
|
+
new GrantStoreError({
|
|
22
|
+
code: "invalid_resolution",
|
|
23
|
+
message:
|
|
24
|
+
`capability requires a valid action and a resource within maximumCapabilityResourceLength (${Capability.maxResourceLength} UTF-16 code units)`
|
|
25
|
+
})
|
|
26
|
+
})
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The optional atomic batch portion of the shared host contract.
|
|
3
|
+
*
|
|
4
|
+
* @since 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { expect } from "@effect/vitest"
|
|
8
|
+
import { Effect, type FileSystem, Option, Path, Result } from "effect"
|
|
9
|
+
import * as KernelFileSystem from "../FileSystem.ts"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Exercises every advertised batch implementation over the host contract's
|
|
13
|
+
* fresh `source.txt` fixture. Isolated volumes may omit this optimization.
|
|
14
|
+
*
|
|
15
|
+
* @since 1.0.0
|
|
16
|
+
* @category testing
|
|
17
|
+
*/
|
|
18
|
+
export const check = (fs: FileSystem.FileSystem, root: string) =>
|
|
19
|
+
Effect.gen(function*() {
|
|
20
|
+
const atomic = (fs as Partial<KernelFileSystem.AtomicHostFileSystem>)[KernelFileSystem.AtomicFileSystemTypeId]
|
|
21
|
+
if (atomic?.batchLimits === undefined) {
|
|
22
|
+
expect(KernelFileSystem.batch(fs)).toBeUndefined()
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
const path = yield* Path.Path
|
|
26
|
+
const boundaryRoot = yield* fs.realPath(root)
|
|
27
|
+
const rootIdentity = atomic.identifyRoot === undefined
|
|
28
|
+
? yield* Effect.map(fs.stat(root), (info) => `${info.dev}:${Option.getOrThrow(info.ino)}`)
|
|
29
|
+
: yield* atomic.identifyRoot(boundaryRoot)
|
|
30
|
+
const response = yield* atomic.execute({
|
|
31
|
+
operation: "batch",
|
|
32
|
+
boundaryRoot,
|
|
33
|
+
logicalRoot: root,
|
|
34
|
+
rootIdentity,
|
|
35
|
+
requests: [
|
|
36
|
+
{ operation: "digest", path: path.join(root, "source.txt"), content: true },
|
|
37
|
+
{ operation: "stat", path: path.join(root, "source.txt") },
|
|
38
|
+
{ operation: "readDirectory", path: root },
|
|
39
|
+
{ operation: "glob", path: path.join(root, "*.txt"), root },
|
|
40
|
+
{ operation: "digest", path: path.join(root, "absent") }
|
|
41
|
+
]
|
|
42
|
+
})
|
|
43
|
+
expect(response.rootIdentity).toBe(rootIdentity)
|
|
44
|
+
expect(response.entries.map((entry) => entry.index)).toEqual([2, 3, 4, 0, 1])
|
|
45
|
+
const entries = [...response.entries].sort((a, b) => a.index - b.index)
|
|
46
|
+
expect(Result.getOrThrow(entries[0]!.result)).toEqual({
|
|
47
|
+
operation: "digest",
|
|
48
|
+
digest: "0e1c618211c39c03726d50ee05e7e520ca6d8a2a3619b17983adc574e3ca34bf",
|
|
49
|
+
sizeBytes: 13,
|
|
50
|
+
bytes: new TextEncoder().encode("host-contract")
|
|
51
|
+
})
|
|
52
|
+
expect(Result.getOrThrow(entries[1]!.result)).toMatchObject({
|
|
53
|
+
operation: "stat",
|
|
54
|
+
info: { type: "File", size: 13n }
|
|
55
|
+
})
|
|
56
|
+
expect(Result.getOrThrow(entries[2]!.result)).toEqual({ operation: "readDirectory", paths: ["source.txt"] })
|
|
57
|
+
expect(Result.getOrThrow(entries[3]!.result)).toEqual({ operation: "glob", paths: [path.join(root, "source.txt")] })
|
|
58
|
+
expect(entries[4]!.result).toMatchObject({ _tag: "Failure", failure: { reason: { _tag: "NotFound" } } })
|
|
59
|
+
})
|