@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
|
@@ -0,0 +1,890 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var FileSystem_exports = {};
|
|
31
|
+
__export(FileSystem_exports, {
|
|
32
|
+
AtomicFileSystemTypeId: () => AtomicFileSystemTypeId,
|
|
33
|
+
FileSystemFault: () => FileSystemFault,
|
|
34
|
+
FileSystemFaultCode: () => FileSystemFaultCode,
|
|
35
|
+
canonicalResource: () => canonicalResource,
|
|
36
|
+
confined: () => confined,
|
|
37
|
+
confinedRoot: () => confinedRoot,
|
|
38
|
+
isConfinable: () => isConfinable,
|
|
39
|
+
layer: () => layer,
|
|
40
|
+
requireConfinable: () => requireConfinable,
|
|
41
|
+
systemTemporaryDirectoryName: () => systemTemporaryDirectoryName,
|
|
42
|
+
withAtomicFileSystem: () => withAtomicFileSystem,
|
|
43
|
+
withIsolatedFileSystem: () => withIsolatedFileSystem
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(FileSystem_exports);
|
|
46
|
+
var import_Permission = require("@smthrs/capability/Permission");
|
|
47
|
+
var import_effect = require("effect");
|
|
48
|
+
var Batch = __toESM(require("./FileSystemBatch.js"), 1);
|
|
49
|
+
var import_GrantStore = require("./GrantStore.js");
|
|
50
|
+
var import_makeCapability = require("./internal/makeCapability.js");
|
|
51
|
+
var import_Workspace = require("./Workspace.js");
|
|
52
|
+
__reExport(FileSystem_exports, require("./FileSystemBatch.js"), module.exports);
|
|
53
|
+
const systemTemporaryDirectoryName = "<system-temp>";
|
|
54
|
+
const AtomicFileSystemTypeId = /* @__PURE__ */ Symbol.for("@smthrs/kernel/AtomicFileSystem");
|
|
55
|
+
const withAtomicFileSystem = (fileSystem, atomic) => Object.assign(fileSystem, { [AtomicFileSystemTypeId]: atomic });
|
|
56
|
+
const FileSystemFaultCode = import_effect.Schema.Literals([
|
|
57
|
+
"executor_already_present",
|
|
58
|
+
"isolated_operation_unsupported",
|
|
59
|
+
"workspace_identity_changed"
|
|
60
|
+
]);
|
|
61
|
+
class FileSystemFault extends import_effect.Schema.TaggedError()("@smthrs/kernel/FileSystemFault", {
|
|
62
|
+
code: FileSystemFaultCode,
|
|
63
|
+
message: import_effect.Schema.String
|
|
64
|
+
}) {
|
|
65
|
+
name = "FileSystemFault";
|
|
66
|
+
}
|
|
67
|
+
const withIsolatedFileSystem = (fileSystem) => {
|
|
68
|
+
if (AtomicFileSystemTypeId in fileSystem) {
|
|
69
|
+
throw new FileSystemFault({
|
|
70
|
+
code: "executor_already_present",
|
|
71
|
+
message: "filesystem already carries a descriptor-relative executor; attesting whole-filesystem isolation would replace it"
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return withAtomicFileSystem(fileSystem, {
|
|
75
|
+
isolated: fileSystem,
|
|
76
|
+
execute: dispatch({
|
|
77
|
+
exists: (request) => fileSystem.exists(request.path),
|
|
78
|
+
chmod: (request) => fileSystem.chmod(request.path, request.options.mode),
|
|
79
|
+
chown: (request) => fileSystem.chown(request.path, request.options.uid, request.options.gid),
|
|
80
|
+
glob: (request) => fileSystem.glob(request.pattern, { ...request.options, root: request.root }),
|
|
81
|
+
makeDirectory: (request) => fileSystem.makeDirectory(request.path, request.options),
|
|
82
|
+
readDirectory: (request) => fileSystem.readDirectory(request.path, request.options),
|
|
83
|
+
readFile: (request) => fileSystem.readFile(request.path),
|
|
84
|
+
readFileString: (request) => fileSystem.readFileString(request.path, request.encoding),
|
|
85
|
+
readLink: (request) => fileSystem.readLink(request.path),
|
|
86
|
+
realPath: (request) => fileSystem.realPath(request.path),
|
|
87
|
+
// Authorization asks `resolve` only of an executor that claims
|
|
88
|
+
// `noFollowAuthorization`, which an attested volume never does.
|
|
89
|
+
resolve: (request) => unsupportedIsolated(request.operation),
|
|
90
|
+
remove: (request) => fileSystem.remove(request.path, request.options),
|
|
91
|
+
rename: (request) => fileSystem.rename(request.from, request.to),
|
|
92
|
+
stat: (request) => fileSystem.stat(request.path),
|
|
93
|
+
writeFile: (request) => import_effect.Encoding.decodeBase64(request.data).pipe(
|
|
94
|
+
import_effect.Effect.fromResult,
|
|
95
|
+
import_effect.Effect.orDie,
|
|
96
|
+
import_effect.Effect.flatMap((data) => fileSystem.writeFile(request.path, data, request.options))
|
|
97
|
+
),
|
|
98
|
+
writeFileString: (request) => fileSystem.writeFileString(request.path, request.data, request.options),
|
|
99
|
+
// An attested volume advertises no `batchLimits`, so the guarded layer
|
|
100
|
+
// never frames a batch for this executor.
|
|
101
|
+
batch: (request) => unsupportedIsolated(request.operation)
|
|
102
|
+
})
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
const unsupportedIsolated = (operation) => import_effect.Effect.die(
|
|
106
|
+
new FileSystemFault({
|
|
107
|
+
code: "isolated_operation_unsupported",
|
|
108
|
+
message: `unsupported isolated filesystem operation: ${operation}`
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
const dispatch = (handlers) => {
|
|
112
|
+
const table = handlers;
|
|
113
|
+
return (request) => {
|
|
114
|
+
const handler = table[request.operation];
|
|
115
|
+
return handler === void 0 ? unsupportedIsolated(request.operation) : handler(request);
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
const identifyRoot = (atomic, fileSystem, path) => atomic.identifyRoot === void 0 ? import_effect.Effect.map(fileSystem.stat(path), (info) => import_effect.Option.map(info.ino, (ino) => `${info.dev}:${ino}`)) : import_effect.Effect.map(atomic.identifyRoot(path), import_effect.Option.some);
|
|
119
|
+
const pinned = (atomic, root) => (request) => atomic.execute({ ...request, ...root });
|
|
120
|
+
const unconfinedDescription = "host does not provide descriptor-relative, no-follow filesystem isolation";
|
|
121
|
+
const unconfined = (method, pathOrDescriptor) => import_effect.PlatformError.systemError({
|
|
122
|
+
_tag: "PermissionDenied",
|
|
123
|
+
module: "FileSystem",
|
|
124
|
+
method,
|
|
125
|
+
pathOrDescriptor,
|
|
126
|
+
description: unconfinedDescription
|
|
127
|
+
});
|
|
128
|
+
const ConfinedFileSystemTypeId = /* @__PURE__ */ Symbol.for("@smthrs/kernel/ConfinedFileSystem");
|
|
129
|
+
const isConfinable = (fileSystem) => ConfinedFileSystemTypeId in fileSystem || AtomicFileSystemTypeId in fileSystem;
|
|
130
|
+
const confinedRoot = (fileSystem) => fileSystem[ConfinedFileSystemTypeId];
|
|
131
|
+
const requireConfinable = (fileSystem, root) => isConfinable(fileSystem) ? import_effect.Effect.void : import_effect.Effect.fail(unconfined("confined", root));
|
|
132
|
+
const confined = (fileSystem, root) => import_effect.Effect.gen(function* () {
|
|
133
|
+
const path = yield* import_effect.Path.Path;
|
|
134
|
+
const logicalRoot = path.resolve(root === "" ? "." : root);
|
|
135
|
+
if (confinedRoot(fileSystem) === logicalRoot) return fileSystem;
|
|
136
|
+
const atomic = fileSystem[AtomicFileSystemTypeId];
|
|
137
|
+
if (atomic === void 0) return yield* import_effect.Effect.fail(unconfined("confined", logicalRoot));
|
|
138
|
+
if (atomic.isolated !== void 0) return fileSystem;
|
|
139
|
+
const boundaryRoot = yield* fileSystem.realPath(logicalRoot);
|
|
140
|
+
const rootIdentity = yield* identifyRoot(atomic, fileSystem, boundaryRoot);
|
|
141
|
+
if (import_effect.Option.isNone(rootIdentity)) return yield* import_effect.Effect.fail(unconfined("confined", logicalRoot));
|
|
142
|
+
const run = pinned(atomic, { boundaryRoot, logicalRoot, rootIdentity: rootIdentity.value });
|
|
143
|
+
const normalize = (value) => path.resolve(logicalRoot, value);
|
|
144
|
+
const refuse = (method, value) => import_effect.Effect.fail(unconfined(method, normalize(value)));
|
|
145
|
+
const view = {
|
|
146
|
+
...import_effect.FileSystem.make({
|
|
147
|
+
access: (value) => refuse("access", value),
|
|
148
|
+
copy: (from) => refuse("copy", from),
|
|
149
|
+
copyFile: (from) => refuse("copyFile", from),
|
|
150
|
+
chmod: (value, mode) => run({ operation: "chmod", path: normalize(value), options: { mode } }),
|
|
151
|
+
chown: (value, uid, gid) => run({ operation: "chown", path: normalize(value), options: { uid, gid } }),
|
|
152
|
+
glob: (pattern, options) => {
|
|
153
|
+
const base = options?.root === void 0 ? logicalRoot : normalize(options.root);
|
|
154
|
+
return run({
|
|
155
|
+
operation: "glob",
|
|
156
|
+
pattern: path.resolve(base, pattern),
|
|
157
|
+
root: base,
|
|
158
|
+
options: options === void 0 ? void 0 : { exclude: options.exclude ?? [] }
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
link: (from) => refuse("link", from),
|
|
162
|
+
makeDirectory: (value, options) => run({
|
|
163
|
+
operation: "makeDirectory",
|
|
164
|
+
path: normalize(value),
|
|
165
|
+
options
|
|
166
|
+
}),
|
|
167
|
+
makeTempDirectory: () => refuse("makeTempDirectory", logicalRoot),
|
|
168
|
+
makeTempDirectoryScoped: () => refuse("makeTempDirectoryScoped", logicalRoot),
|
|
169
|
+
makeTempFile: () => refuse("makeTempFile", logicalRoot),
|
|
170
|
+
makeTempFileScoped: () => refuse("makeTempFileScoped", logicalRoot),
|
|
171
|
+
open: (value) => refuse("open", value),
|
|
172
|
+
readDirectory: (value, options) => run({
|
|
173
|
+
operation: "readDirectory",
|
|
174
|
+
path: normalize(value),
|
|
175
|
+
options
|
|
176
|
+
}),
|
|
177
|
+
readFile: (value) => run({ operation: "readFile", path: normalize(value) }),
|
|
178
|
+
readLink: (value) => run({ operation: "readLink", path: normalize(value) }),
|
|
179
|
+
realPath: (value) => run({ operation: "realPath", path: normalize(value) }),
|
|
180
|
+
remove: (value, options) => run({
|
|
181
|
+
operation: "remove",
|
|
182
|
+
path: normalize(value),
|
|
183
|
+
options
|
|
184
|
+
}),
|
|
185
|
+
rename: (from, to) => run({ operation: "rename", from: normalize(from), to: normalize(to) }),
|
|
186
|
+
stat: (value) => run({ operation: "stat", path: normalize(value) }),
|
|
187
|
+
symlink: (_from, to) => refuse("symlink", to),
|
|
188
|
+
truncate: (value) => refuse("truncate", value),
|
|
189
|
+
utimes: (value) => refuse("utimes", value),
|
|
190
|
+
watch: (value) => import_effect.Stream.fail(unconfined("watch", normalize(value))),
|
|
191
|
+
writeFile: (value, data, options) => {
|
|
192
|
+
const limit = atomic.contentLimit;
|
|
193
|
+
if (limit !== void 0 && data.byteLength > limit) {
|
|
194
|
+
return import_effect.Effect.fail(import_effect.PlatformError.badArgument({
|
|
195
|
+
module: "FileSystem",
|
|
196
|
+
method: "writeFile",
|
|
197
|
+
description: `writeFile payload of ${data.byteLength} bytes exceeds the ${limit} byte limit advertised by the host`
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
return run({
|
|
201
|
+
operation: "writeFile",
|
|
202
|
+
path: normalize(value),
|
|
203
|
+
data: import_effect.Encoding.encodeBase64(data),
|
|
204
|
+
options
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}),
|
|
208
|
+
exists: (value) => run({ operation: "exists", path: normalize(value) }),
|
|
209
|
+
readFileString: (value, encoding) => run({ operation: "readFileString", path: normalize(value), encoding }),
|
|
210
|
+
writeFileString: (value, data, options) => run({
|
|
211
|
+
operation: "writeFileString",
|
|
212
|
+
path: normalize(value),
|
|
213
|
+
data,
|
|
214
|
+
options
|
|
215
|
+
})
|
|
216
|
+
};
|
|
217
|
+
return Object.assign(view, { [ConfinedFileSystemTypeId]: logicalRoot });
|
|
218
|
+
});
|
|
219
|
+
const readableOpenFlags = /* @__PURE__ */ new Set([
|
|
220
|
+
"r",
|
|
221
|
+
"r+",
|
|
222
|
+
"w+",
|
|
223
|
+
"wx+",
|
|
224
|
+
"a+",
|
|
225
|
+
"ax+"
|
|
226
|
+
]);
|
|
227
|
+
const writableOpenFlags = /* @__PURE__ */ new Set([
|
|
228
|
+
"r+",
|
|
229
|
+
"w",
|
|
230
|
+
"wx",
|
|
231
|
+
"w+",
|
|
232
|
+
"wx+",
|
|
233
|
+
"a",
|
|
234
|
+
"ax",
|
|
235
|
+
"a+",
|
|
236
|
+
"ax+"
|
|
237
|
+
]);
|
|
238
|
+
const isInside = (path, root, candidate) => {
|
|
239
|
+
const relative = path.relative(root, candidate);
|
|
240
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
|
|
241
|
+
};
|
|
242
|
+
const identityOf = (info) => info.type === "File" ? import_effect.Option.map(info.ino, (ino) => `${info.dev}:${ino}`) : import_effect.Option.none();
|
|
243
|
+
const canonicalResource = (fileSystem, path, workspaceRoot, value) => {
|
|
244
|
+
const normalizedRoot = path.normalize(workspaceRoot);
|
|
245
|
+
const normalizedValue = path.normalize(
|
|
246
|
+
path.isAbsolute(value) ? value : path.resolve(normalizedRoot, value)
|
|
247
|
+
);
|
|
248
|
+
const resolveExistingAncestor = (candidate, symlinkDepth = 0) => fileSystem.realPath(candidate).pipe(
|
|
249
|
+
import_effect.Effect.catch((error) => {
|
|
250
|
+
const resolveParent = () => {
|
|
251
|
+
const parent = path.dirname(candidate);
|
|
252
|
+
if (parent === candidate) {
|
|
253
|
+
return import_effect.Effect.fail(error);
|
|
254
|
+
}
|
|
255
|
+
return resolveExistingAncestor(parent, symlinkDepth).pipe(
|
|
256
|
+
import_effect.Effect.map((resolvedParent) => path.join(resolvedParent, path.basename(candidate)))
|
|
257
|
+
);
|
|
258
|
+
};
|
|
259
|
+
return fileSystem.readLink(candidate).pipe(
|
|
260
|
+
import_effect.Effect.matchEffect({
|
|
261
|
+
onFailure: resolveParent,
|
|
262
|
+
onSuccess: (target) => symlinkDepth >= 40 ? import_effect.Effect.fail(error) : resolveExistingAncestor(
|
|
263
|
+
path.isAbsolute(target) ? target : path.resolve(path.dirname(candidate), target),
|
|
264
|
+
symlinkDepth + 1
|
|
265
|
+
)
|
|
266
|
+
})
|
|
267
|
+
);
|
|
268
|
+
})
|
|
269
|
+
);
|
|
270
|
+
return import_effect.Effect.all([
|
|
271
|
+
fileSystem.realPath(normalizedRoot),
|
|
272
|
+
resolveExistingAncestor(normalizedValue)
|
|
273
|
+
]).pipe(
|
|
274
|
+
import_effect.Effect.map(
|
|
275
|
+
([canonicalRoot, canonicalValue]) => isInside(path, canonicalRoot, canonicalValue) ? path.normalize(path.join(normalizedRoot, path.relative(canonicalRoot, canonicalValue))) : path.normalize(canonicalValue)
|
|
276
|
+
)
|
|
277
|
+
);
|
|
278
|
+
};
|
|
279
|
+
const uninspectable = "path component could not be inspected without following it";
|
|
280
|
+
const pinnedPath = (path, root, value) => {
|
|
281
|
+
const base = isInside(path, root.logicalRoot, value) ? root.logicalRoot : isInside(path, root.boundaryRoot, value) ? root.boundaryRoot : void 0;
|
|
282
|
+
return base === void 0 ? void 0 : path.join(root.boundaryRoot, path.relative(base, value));
|
|
283
|
+
};
|
|
284
|
+
const segmentsBelow = (path, root, value) => path.relative(root, value).split(path.sep).filter((segment) => segment !== "");
|
|
285
|
+
const globAuthorizationPath = (path, root, value, glob) => {
|
|
286
|
+
const requested = glob ? pinnedPath(path, root, value) : void 0;
|
|
287
|
+
if (requested === void 0) return { prefix: value, suffix: [] };
|
|
288
|
+
const parts = segmentsBelow(path, root.boundaryRoot, requested);
|
|
289
|
+
const wildcard = parts.findIndex((part) => /[*?[{]|[@+!]\(/.test(part));
|
|
290
|
+
return wildcard < 0 ? { prefix: value, suffix: [] } : { prefix: path.join(root.logicalRoot, ...parts.slice(0, wildcard)), suffix: parts.slice(wildcard) };
|
|
291
|
+
};
|
|
292
|
+
const confinedResource = (run, path, root, value, refuse, symlinkDepth = 0) => {
|
|
293
|
+
const requested = pinnedPath(path, root, value);
|
|
294
|
+
if (requested === void 0) {
|
|
295
|
+
return import_effect.Effect.succeed(value);
|
|
296
|
+
}
|
|
297
|
+
return run({ operation: "resolve", path: requested }).pipe(
|
|
298
|
+
import_effect.Effect.matchEffect({
|
|
299
|
+
onFailure: (cause) => refuse(logicalPath(path, root, requested), uninspectable, cause),
|
|
300
|
+
onSuccess: (resolution) => followResolution(run, path, root, requested, resolution, refuse, symlinkDepth)
|
|
301
|
+
})
|
|
302
|
+
);
|
|
303
|
+
};
|
|
304
|
+
const logicalPath = (path, root, canonical) => path.normalize(path.join(root.logicalRoot, path.relative(root.boundaryRoot, canonical)));
|
|
305
|
+
const followResolution = (run, path, root, requested, resolution, refuse, symlinkDepth) => {
|
|
306
|
+
const resolved = path.normalize(resolution.path);
|
|
307
|
+
if (!isInside(path, root.boundaryRoot, resolved)) {
|
|
308
|
+
return refuse(logicalPath(path, root, requested), "path resolves outside the pinned root");
|
|
309
|
+
}
|
|
310
|
+
if (resolution.target === null) {
|
|
311
|
+
return import_effect.Effect.succeed(logicalPath(path, root, resolved));
|
|
312
|
+
}
|
|
313
|
+
if (symlinkDepth >= 40) {
|
|
314
|
+
return refuse(logicalPath(path, root, resolved), "too many levels of symbolic links");
|
|
315
|
+
}
|
|
316
|
+
const rest = segmentsBelow(path, root.boundaryRoot, requested).slice(
|
|
317
|
+
segmentsBelow(path, root.boundaryRoot, resolved).length
|
|
318
|
+
);
|
|
319
|
+
return confinedResource(
|
|
320
|
+
run,
|
|
321
|
+
path,
|
|
322
|
+
root,
|
|
323
|
+
path.resolve(path.dirname(resolved), resolution.target, ...rest),
|
|
324
|
+
refuse,
|
|
325
|
+
symlinkDepth + 1
|
|
326
|
+
);
|
|
327
|
+
};
|
|
328
|
+
const publicValue = (resource, result) => import_effect.Result.isFailure(result) || result.success.operation !== "resolve" ? result : import_effect.Result.fail(import_effect.PlatformError.systemError({
|
|
329
|
+
_tag: "PermissionDenied",
|
|
330
|
+
module: "FileSystem",
|
|
331
|
+
method: "batch",
|
|
332
|
+
pathOrDescriptor: resource,
|
|
333
|
+
description: "executor answered a batch read with a resolution"
|
|
334
|
+
}));
|
|
335
|
+
const layer = import_effect.Layer.effect(
|
|
336
|
+
import_effect.FileSystem.FileSystem,
|
|
337
|
+
import_effect.Effect.gen(function* () {
|
|
338
|
+
const fileSystem = yield* import_effect.FileSystem.FileSystem;
|
|
339
|
+
const path = yield* import_effect.Path.Path;
|
|
340
|
+
const workspace = yield* import_Workspace.Workspace;
|
|
341
|
+
const grants = yield* import_GrantStore.GrantStore;
|
|
342
|
+
const atomic = fileSystem[AtomicFileSystemTypeId];
|
|
343
|
+
const normalizeFrom = (base, value) => path.normalize(path.isAbsolute(value) ? value : path.resolve(base, value));
|
|
344
|
+
const logicalRoot = path.normalize(path.resolve(workspace.root));
|
|
345
|
+
const normalize = (value) => normalizeFrom(logicalRoot, value);
|
|
346
|
+
const boundaryRoot = yield* fileSystem.realPath(logicalRoot);
|
|
347
|
+
const rootIdentity = atomic === void 0 || atomic.isolated !== void 0 && atomic.batchLimits === void 0 ? import_effect.Option.none() : yield* identifyRoot(atomic, fileSystem, boundaryRoot);
|
|
348
|
+
const root = { boundaryRoot, logicalRoot, rootIdentity: import_effect.Option.getOrUndefined(rootIdentity) };
|
|
349
|
+
const refuse = (method, resource) => (error) => (0, import_Permission.toPlatformError)({ module: "FileSystem", method, pathOrDescriptor: resource, error });
|
|
350
|
+
const deny = (action, method, resource, reason, cause) => (0, import_makeCapability.makeCapability)(action, resource).pipe(
|
|
351
|
+
import_effect.Effect.flatMap((capability) => {
|
|
352
|
+
const failure = (0, import_Permission.permissionDenied)(capability, reason);
|
|
353
|
+
if (cause !== void 0) Object.defineProperty(failure, "cause", { value: cause });
|
|
354
|
+
return import_effect.Effect.fail(failure);
|
|
355
|
+
}),
|
|
356
|
+
import_effect.Effect.mapError(refuse(method, resource)),
|
|
357
|
+
import_effect.Effect.mapError(
|
|
358
|
+
(failure) => cause?.reason.description === void 0 ? failure : import_effect.PlatformError.systemError({
|
|
359
|
+
...failure.reason,
|
|
360
|
+
_tag: "PermissionDenied",
|
|
361
|
+
description: cause.reason.description,
|
|
362
|
+
cause: failure.reason.cause
|
|
363
|
+
})
|
|
364
|
+
)
|
|
365
|
+
);
|
|
366
|
+
const insideWorkspace = (action, method) => (resource) => isInside(path, logicalRoot, resource) ? import_effect.Effect.succeed(resource) : deny(action, method, resource, "path is outside the workspace");
|
|
367
|
+
const resolvedResource = (action, method, value, glob = false) => {
|
|
368
|
+
const normalized = normalize(value);
|
|
369
|
+
if (atomic?.noFollowAuthorization === true) {
|
|
370
|
+
const { prefix, suffix } = globAuthorizationPath(path, root, normalized, glob);
|
|
371
|
+
return confinedResource(
|
|
372
|
+
pinned(atomic, root),
|
|
373
|
+
path,
|
|
374
|
+
root,
|
|
375
|
+
prefix,
|
|
376
|
+
(resource, reason, cause) => deny(action, method, resource, reason, cause)
|
|
377
|
+
).pipe(
|
|
378
|
+
import_effect.Effect.map((resource) => suffix.length === 0 ? resource : path.join(resource, ...suffix)),
|
|
379
|
+
import_effect.Effect.flatMap(insideWorkspace(action, method))
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
return canonicalResource(fileSystem, path, logicalRoot, normalized).pipe(
|
|
383
|
+
import_effect.Effect.flatMap(
|
|
384
|
+
(resource) => fileSystem.stat(normalized).pipe(
|
|
385
|
+
import_effect.Effect.matchEffect({
|
|
386
|
+
onFailure: () => import_effect.Effect.succeed(resource),
|
|
387
|
+
onSuccess: (info) => {
|
|
388
|
+
const hardLinked = info.type === "File" && import_effect.Option.isSome(info.nlink) && info.nlink.value > 1;
|
|
389
|
+
return hardLinked ? deny(action, method, resource, "hard-linked files cannot be confined to the workspace") : import_effect.Effect.succeed(resource);
|
|
390
|
+
}
|
|
391
|
+
})
|
|
392
|
+
)
|
|
393
|
+
)
|
|
394
|
+
);
|
|
395
|
+
};
|
|
396
|
+
const resolvedResources = (values) => {
|
|
397
|
+
const refuseRead = (resource, reason, cause) => deny("fs:read", "read", resource, reason, cause);
|
|
398
|
+
if (atomic?.noFollowAuthorization !== true || atomic.batchLimits === void 0) {
|
|
399
|
+
return import_effect.Effect.forEach(
|
|
400
|
+
values,
|
|
401
|
+
(value) => import_effect.Effect.result(resolvedResource("fs:read", "read", value.path, value.operation === "glob")),
|
|
402
|
+
{
|
|
403
|
+
concurrency: Batch.fallbackConcurrency
|
|
404
|
+
}
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
const run = pinned(atomic, root);
|
|
408
|
+
const parts = values.map(
|
|
409
|
+
(value) => globAuthorizationPath(path, root, normalize(value.path), value.operation === "glob")
|
|
410
|
+
);
|
|
411
|
+
const requested = parts.map(({ prefix }) => pinnedPath(path, root, prefix));
|
|
412
|
+
const members = [];
|
|
413
|
+
const memberOf = requested.map(
|
|
414
|
+
(value) => value === void 0 ? void 0 : members.push({ operation: "resolve", path: value }) - 1
|
|
415
|
+
);
|
|
416
|
+
const answered = members.length === 0 ? import_effect.Effect.succeed([]) : import_effect.Effect.map(run({ operation: "batch", requests: members }), (response) => response.entries);
|
|
417
|
+
return import_effect.Effect.flatMap(answered, (entries) => {
|
|
418
|
+
const results = new Map(entries.map((entry) => [entry.index, entry.result]));
|
|
419
|
+
return import_effect.Effect.forEach(values, (value, index) => {
|
|
420
|
+
const member = memberOf[index];
|
|
421
|
+
const resource = member === void 0 ? import_effect.Effect.succeed(normalize(value.path)) : import_effect.Effect.suspend(() => {
|
|
422
|
+
const pinnedValue = members[member].path;
|
|
423
|
+
const result = results.get(member);
|
|
424
|
+
return result === void 0 || import_effect.Result.isFailure(result) || result.success.operation !== "resolve" ? refuseRead(
|
|
425
|
+
logicalPath(path, root, pinnedValue),
|
|
426
|
+
uninspectable,
|
|
427
|
+
result !== void 0 && import_effect.Result.isFailure(result) ? result.failure : void 0
|
|
428
|
+
) : followResolution(run, path, root, pinnedValue, result.success.resolution, refuseRead, 0);
|
|
429
|
+
});
|
|
430
|
+
return import_effect.Effect.result(resource.pipe(
|
|
431
|
+
import_effect.Effect.map(
|
|
432
|
+
(resolved) => parts[index].suffix.length === 0 ? resolved : path.join(resolved, ...parts[index].suffix)
|
|
433
|
+
),
|
|
434
|
+
import_effect.Effect.flatMap(insideWorkspace("fs:read", "read"))
|
|
435
|
+
));
|
|
436
|
+
}, { concurrency: Batch.fallbackConcurrency });
|
|
437
|
+
});
|
|
438
|
+
};
|
|
439
|
+
const guard = (action, value, glob = false) => {
|
|
440
|
+
const method = action === "fs:read" ? "read" : "write";
|
|
441
|
+
return resolvedResource(action, method, value, glob).pipe(
|
|
442
|
+
import_effect.Effect.flatMap(
|
|
443
|
+
(resource) => (0, import_makeCapability.makeCapability)(action, resource).pipe(
|
|
444
|
+
import_effect.Effect.flatMap((capability) => grants.check(capability)),
|
|
445
|
+
import_effect.Effect.mapError(refuse(method, resource)),
|
|
446
|
+
// The grant decision can suspend — an attended request waiting for
|
|
447
|
+
// a human, a journal-backed store doing IO. What was authorized is
|
|
448
|
+
// the resource the path named at check time, so the path must
|
|
449
|
+
// still name it when the decision arrives: a symlink or rename
|
|
450
|
+
// swapped in during the wait is refused, never followed.
|
|
451
|
+
import_effect.Effect.andThen(resolvedResource(action, method, value, glob)),
|
|
452
|
+
import_effect.Effect.flatMap(
|
|
453
|
+
(settled) => settled === resource ? import_effect.Effect.void : deny(action, method, resource, "path no longer names the resource that was authorized")
|
|
454
|
+
)
|
|
455
|
+
)
|
|
456
|
+
)
|
|
457
|
+
);
|
|
458
|
+
};
|
|
459
|
+
const read = (value) => guard("fs:read", value);
|
|
460
|
+
const write = (value) => guard("fs:write", value);
|
|
461
|
+
const atomicUnavailable = (action, value, method) => deny(
|
|
462
|
+
action,
|
|
463
|
+
method,
|
|
464
|
+
normalize(value),
|
|
465
|
+
unconfinedDescription
|
|
466
|
+
);
|
|
467
|
+
const atomicOne = (action, value, method, request) => atomic === void 0 ? atomicUnavailable(action, value, method) : guard(action, value, request.operation === "glob").pipe(import_effect.Effect.andThen(pinned(atomic, root)(request)));
|
|
468
|
+
const atomicTwo = (first, second, method, request) => atomic === void 0 ? atomicUnavailable(first[0], first[1], method) : guard(first[0], first[1]).pipe(
|
|
469
|
+
import_effect.Effect.andThen(guard(second[0], second[1])),
|
|
470
|
+
import_effect.Effect.andThen(pinned(atomic, root)(request))
|
|
471
|
+
);
|
|
472
|
+
const isolatedOne = (action, value, method, use) => atomic?.isolated === void 0 ? atomicUnavailable(action, value, method) : guard(action, value).pipe(import_effect.Effect.andThen(use(atomic.isolated)));
|
|
473
|
+
const isolatedTwo = (first, second, method, use) => atomic?.isolated === void 0 ? atomicUnavailable(first[0], first[1], method) : guard(first[0], first[1]).pipe(
|
|
474
|
+
import_effect.Effect.andThen(guard(second[0], second[1])),
|
|
475
|
+
import_effect.Effect.andThen(use(atomic.isolated))
|
|
476
|
+
);
|
|
477
|
+
const temp = (directory) => directory === void 0 ? write(path.resolve(workspace.root, "..", systemTemporaryDirectoryName)) : write(directory);
|
|
478
|
+
const snapshotOptions = (value) => {
|
|
479
|
+
if (Array.isArray(value)) return Object.freeze(value.map(snapshotOptions));
|
|
480
|
+
if (typeof value !== "object" || value === null) return value;
|
|
481
|
+
const snapshot = {};
|
|
482
|
+
for (const [name, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
|
|
483
|
+
if (!descriptor.enumerable) continue;
|
|
484
|
+
if (!("value" in descriptor)) throw new TypeError("filesystem options must contain only data properties");
|
|
485
|
+
snapshot[name] = snapshotOptions(descriptor.value);
|
|
486
|
+
}
|
|
487
|
+
return Object.freeze(snapshot);
|
|
488
|
+
};
|
|
489
|
+
const normalizeTempOptions = (options) => options?.directory === void 0 ? options : { ...options, directory: normalize(options.directory) };
|
|
490
|
+
const openChecks = (value, flag) => {
|
|
491
|
+
const readable = readableOpenFlags.has(flag);
|
|
492
|
+
const writable = writableOpenFlags.has(flag);
|
|
493
|
+
return readable && writable ? read(value).pipe(import_effect.Effect.andThen(write(value))) : writable ? write(value) : read(value);
|
|
494
|
+
};
|
|
495
|
+
const descriptorRefusal = (action, value) => deny(
|
|
496
|
+
action,
|
|
497
|
+
action === "fs:read" ? "read" : "write",
|
|
498
|
+
normalize(value),
|
|
499
|
+
"descriptor no longer names the resource at its authorized path"
|
|
500
|
+
);
|
|
501
|
+
const verifyDescriptor = (host, action, value, identity) => import_effect.Option.match(identity, {
|
|
502
|
+
onNone: () => import_effect.Effect.void,
|
|
503
|
+
onSome: (bound) => host.stat(normalize(value)).pipe(
|
|
504
|
+
import_effect.Effect.matchEffect({
|
|
505
|
+
onFailure: () => descriptorRefusal(action, value),
|
|
506
|
+
onSuccess: (info) => import_effect.Option.match(identityOf(info), {
|
|
507
|
+
onNone: () => descriptorRefusal(action, value),
|
|
508
|
+
onSome: (current) => current === bound ? import_effect.Effect.void : descriptorRefusal(action, value)
|
|
509
|
+
})
|
|
510
|
+
})
|
|
511
|
+
)
|
|
512
|
+
});
|
|
513
|
+
const wrapFile = (host, file, value, identity) => {
|
|
514
|
+
const readable = import_effect.Effect.suspend(
|
|
515
|
+
() => read(value).pipe(import_effect.Effect.andThen(verifyDescriptor(host, "fs:read", value, identity)))
|
|
516
|
+
);
|
|
517
|
+
const writable = import_effect.Effect.suspend(
|
|
518
|
+
() => write(value).pipe(import_effect.Effect.andThen(verifyDescriptor(host, "fs:write", value, identity)))
|
|
519
|
+
);
|
|
520
|
+
return {
|
|
521
|
+
[import_effect.FileSystem.FileTypeId]: import_effect.FileSystem.FileTypeId,
|
|
522
|
+
stat: import_effect.Effect.fn("FileSystem.File.stat")(() => readable.pipe(import_effect.Effect.andThen(file.stat)))(),
|
|
523
|
+
seek: import_effect.Effect.fn("FileSystem.File.seek")(file.seek),
|
|
524
|
+
sync: import_effect.Effect.fn("FileSystem.File.sync")(() => writable.pipe(import_effect.Effect.andThen(file.sync)))(),
|
|
525
|
+
read: import_effect.Effect.fn("FileSystem.File.read")((buffer) => readable.pipe(import_effect.Effect.andThen(file.read(buffer)))),
|
|
526
|
+
readAlloc: import_effect.Effect.fn("FileSystem.File.readAlloc")(
|
|
527
|
+
(size) => readable.pipe(import_effect.Effect.andThen(file.readAlloc(size)))
|
|
528
|
+
),
|
|
529
|
+
truncate: import_effect.Effect.fn("FileSystem.File.truncate")(
|
|
530
|
+
(length) => writable.pipe(import_effect.Effect.andThen(file.truncate(length)))
|
|
531
|
+
),
|
|
532
|
+
write: import_effect.Effect.fn("FileSystem.File.write")((buffer) => writable.pipe(import_effect.Effect.andThen(file.write(buffer)))),
|
|
533
|
+
writeAll: import_effect.Effect.fn("FileSystem.File.writeAll")(
|
|
534
|
+
(buffer) => writable.pipe(import_effect.Effect.andThen(file.writeAll(buffer)))
|
|
535
|
+
)
|
|
536
|
+
};
|
|
537
|
+
};
|
|
538
|
+
const guarded = {
|
|
539
|
+
...import_effect.FileSystem.make({
|
|
540
|
+
access: import_effect.Effect.fn("FileSystem.access")((value, options) => {
|
|
541
|
+
const captured = snapshotOptions(options);
|
|
542
|
+
return isolatedOne("fs:read", value, "access", (host) => host.access(normalize(value), captured));
|
|
543
|
+
}),
|
|
544
|
+
copy: import_effect.Effect.fn("FileSystem.copy")((from, to, options) => {
|
|
545
|
+
const captured = snapshotOptions(options);
|
|
546
|
+
return isolatedTwo(
|
|
547
|
+
["fs:read", from],
|
|
548
|
+
["fs:write", to],
|
|
549
|
+
"copy",
|
|
550
|
+
(host) => host.copy(normalize(from), normalize(to), captured)
|
|
551
|
+
);
|
|
552
|
+
}),
|
|
553
|
+
copyFile: import_effect.Effect.fn("FileSystem.copyFile")(
|
|
554
|
+
(from, to) => isolatedTwo(
|
|
555
|
+
["fs:read", from],
|
|
556
|
+
["fs:write", to],
|
|
557
|
+
"copyFile",
|
|
558
|
+
(host) => host.copyFile(normalize(from), normalize(to))
|
|
559
|
+
)
|
|
560
|
+
),
|
|
561
|
+
chmod: import_effect.Effect.fn("FileSystem.chmod")(
|
|
562
|
+
(value, mode) => atomicOne("fs:write", value, "chmod", { operation: "chmod", path: normalize(value), options: { mode } })
|
|
563
|
+
),
|
|
564
|
+
chown: import_effect.Effect.fn("FileSystem.chown")(
|
|
565
|
+
(value, uid, gid) => atomicOne("fs:write", value, "chown", {
|
|
566
|
+
operation: "chown",
|
|
567
|
+
path: normalize(value),
|
|
568
|
+
options: { uid, gid }
|
|
569
|
+
})
|
|
570
|
+
),
|
|
571
|
+
glob: import_effect.Effect.fn("FileSystem.glob")((pattern, options) => {
|
|
572
|
+
const captured = snapshotOptions(options);
|
|
573
|
+
const root2 = captured?.root === void 0 ? workspace.root : normalize(captured.root);
|
|
574
|
+
const normalizedPattern = normalizeFrom(root2, pattern);
|
|
575
|
+
return atomicOne("fs:read", normalizedPattern, "glob", {
|
|
576
|
+
operation: "glob",
|
|
577
|
+
pattern: normalizedPattern,
|
|
578
|
+
root: root2,
|
|
579
|
+
options: captured === void 0 ? void 0 : { exclude: captured.exclude ?? [] }
|
|
580
|
+
});
|
|
581
|
+
}),
|
|
582
|
+
link: import_effect.Effect.fn("FileSystem.link")(
|
|
583
|
+
(from, to) => isolatedTwo(["fs:read", from], ["fs:write", to], "link", (host) => host.link(normalize(from), normalize(to)))
|
|
584
|
+
),
|
|
585
|
+
makeDirectory: import_effect.Effect.fn("FileSystem.makeDirectory")((value, options) => {
|
|
586
|
+
const captured = snapshotOptions(options);
|
|
587
|
+
return atomicOne("fs:write", value, "makeDirectory", {
|
|
588
|
+
operation: "makeDirectory",
|
|
589
|
+
path: normalize(value),
|
|
590
|
+
options: captured
|
|
591
|
+
});
|
|
592
|
+
}),
|
|
593
|
+
makeTempDirectory: import_effect.Effect.fn("FileSystem.makeTempDirectory")((options) => {
|
|
594
|
+
const captured = snapshotOptions(options);
|
|
595
|
+
return atomic?.isolated === void 0 ? atomicUnavailable(
|
|
596
|
+
"fs:write",
|
|
597
|
+
captured?.directory ?? `../${systemTemporaryDirectoryName}`,
|
|
598
|
+
"makeTempDirectory"
|
|
599
|
+
) : temp(captured?.directory).pipe(
|
|
600
|
+
import_effect.Effect.andThen(atomic.isolated.makeTempDirectory(normalizeTempOptions(captured)))
|
|
601
|
+
);
|
|
602
|
+
}),
|
|
603
|
+
makeTempDirectoryScoped: import_effect.Effect.fn("FileSystem.makeTempDirectoryScoped")((options) => {
|
|
604
|
+
const captured = snapshotOptions(options);
|
|
605
|
+
return atomic?.isolated === void 0 ? atomicUnavailable(
|
|
606
|
+
"fs:write",
|
|
607
|
+
captured?.directory ?? `../${systemTemporaryDirectoryName}`,
|
|
608
|
+
"makeTempDirectoryScoped"
|
|
609
|
+
) : temp(captured?.directory).pipe(
|
|
610
|
+
import_effect.Effect.andThen(atomic.isolated.makeTempDirectoryScoped(normalizeTempOptions(captured)))
|
|
611
|
+
);
|
|
612
|
+
}),
|
|
613
|
+
makeTempFile: import_effect.Effect.fn("FileSystem.makeTempFile")((options) => {
|
|
614
|
+
const captured = snapshotOptions(options);
|
|
615
|
+
return atomic?.isolated === void 0 ? atomicUnavailable(
|
|
616
|
+
"fs:write",
|
|
617
|
+
captured?.directory ?? `../${systemTemporaryDirectoryName}`,
|
|
618
|
+
"makeTempFile"
|
|
619
|
+
) : temp(captured?.directory).pipe(
|
|
620
|
+
import_effect.Effect.andThen(atomic.isolated.makeTempFile(normalizeTempOptions(captured)))
|
|
621
|
+
);
|
|
622
|
+
}),
|
|
623
|
+
makeTempFileScoped: import_effect.Effect.fn("FileSystem.makeTempFileScoped")((options) => {
|
|
624
|
+
const captured = snapshotOptions(options);
|
|
625
|
+
return atomic?.isolated === void 0 ? atomicUnavailable(
|
|
626
|
+
"fs:write",
|
|
627
|
+
captured?.directory ?? `../${systemTemporaryDirectoryName}`,
|
|
628
|
+
"makeTempFileScoped"
|
|
629
|
+
) : temp(captured?.directory).pipe(
|
|
630
|
+
import_effect.Effect.andThen(atomic.isolated.makeTempFileScoped(normalizeTempOptions(captured)))
|
|
631
|
+
);
|
|
632
|
+
}),
|
|
633
|
+
open: import_effect.Effect.fn("FileSystem.open")((value, options) => {
|
|
634
|
+
const captured = snapshotOptions(options);
|
|
635
|
+
const flag = captured?.flag ?? "r";
|
|
636
|
+
const isolated = atomic?.isolated;
|
|
637
|
+
return isolated === void 0 ? atomicUnavailable("fs:read", value, "open") : openChecks(value, flag).pipe(
|
|
638
|
+
import_effect.Effect.andThen(isolated.open(normalize(value), captured)),
|
|
639
|
+
// fstat the handle the moment it exists: the authorization the
|
|
640
|
+
// open checks granted binds to THIS resource identity, and every
|
|
641
|
+
// later handle operation verifies the authorized path still
|
|
642
|
+
// names it before delegating to the descriptor.
|
|
643
|
+
import_effect.Effect.flatMap(
|
|
644
|
+
(file) => file.stat.pipe(
|
|
645
|
+
import_effect.Effect.map((info) => wrapFile(isolated, file, value, identityOf(info)))
|
|
646
|
+
)
|
|
647
|
+
)
|
|
648
|
+
);
|
|
649
|
+
}),
|
|
650
|
+
readDirectory: import_effect.Effect.fn("FileSystem.readDirectory")((value, options) => {
|
|
651
|
+
const captured = snapshotOptions(options);
|
|
652
|
+
return atomicOne("fs:read", value, "readDirectory", {
|
|
653
|
+
operation: "readDirectory",
|
|
654
|
+
path: normalize(value),
|
|
655
|
+
options: captured
|
|
656
|
+
});
|
|
657
|
+
}),
|
|
658
|
+
readFile: import_effect.Effect.fn("FileSystem.readFile")(
|
|
659
|
+
(value) => atomicOne("fs:read", value, "readFile", {
|
|
660
|
+
operation: "readFile",
|
|
661
|
+
path: normalize(value)
|
|
662
|
+
})
|
|
663
|
+
),
|
|
664
|
+
readLink: import_effect.Effect.fn("FileSystem.readLink")(
|
|
665
|
+
(value) => atomicOne("fs:read", value, "readLink", {
|
|
666
|
+
operation: "readLink",
|
|
667
|
+
path: normalize(value)
|
|
668
|
+
})
|
|
669
|
+
),
|
|
670
|
+
realPath: import_effect.Effect.fn("FileSystem.realPath")(
|
|
671
|
+
(value) => atomicOne("fs:read", value, "realPath", {
|
|
672
|
+
operation: "realPath",
|
|
673
|
+
path: normalize(value)
|
|
674
|
+
})
|
|
675
|
+
),
|
|
676
|
+
remove: import_effect.Effect.fn("FileSystem.remove")((value, options) => {
|
|
677
|
+
const captured = snapshotOptions(options);
|
|
678
|
+
return atomicOne("fs:write", value, "remove", {
|
|
679
|
+
operation: "remove",
|
|
680
|
+
path: normalize(value),
|
|
681
|
+
options: captured
|
|
682
|
+
});
|
|
683
|
+
}),
|
|
684
|
+
rename: import_effect.Effect.fn("FileSystem.rename")(
|
|
685
|
+
(from, to) => atomicTwo(["fs:write", from], ["fs:write", to], "rename", {
|
|
686
|
+
operation: "rename",
|
|
687
|
+
from: normalize(from),
|
|
688
|
+
to: normalize(to)
|
|
689
|
+
})
|
|
690
|
+
),
|
|
691
|
+
stat: import_effect.Effect.fn("FileSystem.stat")(
|
|
692
|
+
(value) => atomicOne("fs:read", value, "stat", {
|
|
693
|
+
operation: "stat",
|
|
694
|
+
path: normalize(value)
|
|
695
|
+
})
|
|
696
|
+
),
|
|
697
|
+
symlink: import_effect.Effect.fn("FileSystem.symlink")(
|
|
698
|
+
(from, to) => isolatedOne("fs:write", to, "symlink", (host) => host.symlink(from, normalize(to)))
|
|
699
|
+
),
|
|
700
|
+
truncate: import_effect.Effect.fn("FileSystem.truncate")(
|
|
701
|
+
(value, length) => isolatedOne("fs:write", value, "truncate", (host) => host.truncate(normalize(value), length))
|
|
702
|
+
),
|
|
703
|
+
utimes: import_effect.Effect.fn("FileSystem.utimes")(
|
|
704
|
+
(value, atime, mtime) => isolatedOne("fs:write", value, "utimes", (host) => host.utimes(normalize(value), atime, mtime))
|
|
705
|
+
),
|
|
706
|
+
watch: (value) => import_effect.Stream.unwrap(
|
|
707
|
+
import_effect.Effect.fn("FileSystem.watch")(
|
|
708
|
+
() => import_effect.Effect.suspend(
|
|
709
|
+
() => atomic?.isolated === void 0 ? atomicUnavailable("fs:read", value, "watch") : read(value).pipe(import_effect.Effect.map(() => atomic.isolated.watch(normalize(value))))
|
|
710
|
+
)
|
|
711
|
+
)()
|
|
712
|
+
),
|
|
713
|
+
writeFile: import_effect.Effect.fn("FileSystem.writeFile")((value, data, options) => {
|
|
714
|
+
const captured = snapshotOptions(options);
|
|
715
|
+
const bytes = data.slice();
|
|
716
|
+
if (atomic?.isolated !== void 0) {
|
|
717
|
+
return isolatedOne(
|
|
718
|
+
"fs:write",
|
|
719
|
+
value,
|
|
720
|
+
"writeFile",
|
|
721
|
+
(host) => host.writeFile(normalize(value), bytes, captured)
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
const limit = atomic?.contentLimit;
|
|
725
|
+
if (limit !== void 0 && bytes.byteLength > limit) {
|
|
726
|
+
return import_effect.Effect.fail(import_effect.PlatformError.badArgument({
|
|
727
|
+
module: "FileSystem",
|
|
728
|
+
method: "writeFile",
|
|
729
|
+
description: `writeFile payload of ${bytes.byteLength} bytes exceeds the ${limit} byte limit advertised by the host`
|
|
730
|
+
}));
|
|
731
|
+
}
|
|
732
|
+
return atomicOne("fs:write", value, "writeFile", {
|
|
733
|
+
operation: "writeFile",
|
|
734
|
+
path: normalize(value),
|
|
735
|
+
data: import_effect.Encoding.encodeBase64(bytes),
|
|
736
|
+
options: captured
|
|
737
|
+
});
|
|
738
|
+
})
|
|
739
|
+
}),
|
|
740
|
+
exists: import_effect.Effect.fn("FileSystem.exists")(
|
|
741
|
+
(value) => atomicOne("fs:read", value, "exists", {
|
|
742
|
+
operation: "exists",
|
|
743
|
+
path: normalize(value)
|
|
744
|
+
})
|
|
745
|
+
),
|
|
746
|
+
readFileString: import_effect.Effect.fn("FileSystem.readFileString")(
|
|
747
|
+
(value, encoding) => atomicOne("fs:read", value, "readFileString", {
|
|
748
|
+
operation: "readFileString",
|
|
749
|
+
path: normalize(value),
|
|
750
|
+
encoding
|
|
751
|
+
})
|
|
752
|
+
),
|
|
753
|
+
sink: (value, options) => {
|
|
754
|
+
const captured = snapshotOptions(options);
|
|
755
|
+
return import_effect.Sink.unwrap(
|
|
756
|
+
import_effect.Effect.fn("FileSystem.sink")(
|
|
757
|
+
() => import_effect.Effect.suspend(
|
|
758
|
+
() => atomic?.isolated === void 0 ? atomicUnavailable("fs:write", value, "sink") : write(value).pipe(import_effect.Effect.map(() => atomic.isolated.sink(normalize(value), captured)))
|
|
759
|
+
)
|
|
760
|
+
)()
|
|
761
|
+
);
|
|
762
|
+
},
|
|
763
|
+
stream: (value, options) => {
|
|
764
|
+
const captured = snapshotOptions(options);
|
|
765
|
+
return import_effect.Stream.unwrap(
|
|
766
|
+
import_effect.Effect.fn("FileSystem.stream")(
|
|
767
|
+
() => import_effect.Effect.suspend(
|
|
768
|
+
() => atomic?.isolated === void 0 ? atomicUnavailable("fs:read", value, "stream") : read(value).pipe(import_effect.Effect.map(() => atomic.isolated.stream(normalize(value), captured)))
|
|
769
|
+
)
|
|
770
|
+
)()
|
|
771
|
+
);
|
|
772
|
+
},
|
|
773
|
+
writeFileString: import_effect.Effect.fn("FileSystem.writeFileString")((value, data, options) => {
|
|
774
|
+
const captured = snapshotOptions(options);
|
|
775
|
+
return atomicOne("fs:write", value, "writeFileString", {
|
|
776
|
+
operation: "writeFileString",
|
|
777
|
+
path: normalize(value),
|
|
778
|
+
data,
|
|
779
|
+
options: captured
|
|
780
|
+
});
|
|
781
|
+
})
|
|
782
|
+
};
|
|
783
|
+
Object.assign(guarded, { [ConfinedFileSystemTypeId]: logicalRoot });
|
|
784
|
+
if (atomic === void 0 || atomic.batchLimits === void 0) {
|
|
785
|
+
return guarded;
|
|
786
|
+
} else {
|
|
787
|
+
const limits = atomic.batchLimits;
|
|
788
|
+
const executeBatch = import_effect.Effect.fn("FileSystem.batch")(function* (requests) {
|
|
789
|
+
if (requests.length === 0 || requests.length > Math.min(limits.size, Batch.maxBatchSize)) {
|
|
790
|
+
return yield* import_effect.Effect.fail(
|
|
791
|
+
import_effect.PlatformError.badArgument({
|
|
792
|
+
module: "FileSystem",
|
|
793
|
+
method: "batch",
|
|
794
|
+
description: `batch must contain 1 to ${Math.min(limits.size, Batch.maxBatchSize)} operations`
|
|
795
|
+
})
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
if (import_effect.Option.isNone(rootIdentity)) {
|
|
799
|
+
return yield* atomicUnavailable("fs:read", logicalRoot, "batch");
|
|
800
|
+
}
|
|
801
|
+
const captured = snapshotOptions(requests).map((request) => ({
|
|
802
|
+
...request,
|
|
803
|
+
path: request.operation === "glob" ? normalizeFrom(normalize(request.root), request.path) : normalize(request.path),
|
|
804
|
+
...request.operation === "glob" ? { root: normalize(request.root) } : {}
|
|
805
|
+
}));
|
|
806
|
+
const rootChanged = (cause) => import_effect.PlatformError.systemError({
|
|
807
|
+
_tag: "Busy",
|
|
808
|
+
module: "FileSystem",
|
|
809
|
+
method: "batch",
|
|
810
|
+
pathOrDescriptor: logicalRoot,
|
|
811
|
+
description: "batch root no longer names the authorized descriptor",
|
|
812
|
+
cause
|
|
813
|
+
});
|
|
814
|
+
const expectedRoot = rootIdentity.value;
|
|
815
|
+
const verifyRoot = () => identifyRoot(atomic, fileSystem, logicalRoot).pipe(
|
|
816
|
+
import_effect.Effect.mapError(rootChanged),
|
|
817
|
+
import_effect.Effect.flatMap(
|
|
818
|
+
(current2) => import_effect.Option.isSome(current2) && current2.value === expectedRoot ? import_effect.Effect.void : import_effect.Effect.fail(rootChanged(
|
|
819
|
+
new FileSystemFault({
|
|
820
|
+
code: "workspace_identity_changed",
|
|
821
|
+
message: "workspace descriptor identity changed"
|
|
822
|
+
})
|
|
823
|
+
))
|
|
824
|
+
)
|
|
825
|
+
);
|
|
826
|
+
yield* verifyRoot();
|
|
827
|
+
const resources = yield* resolvedResources(captured);
|
|
828
|
+
const admitted = [];
|
|
829
|
+
const indexes = [];
|
|
830
|
+
const entries = [];
|
|
831
|
+
const granted = [];
|
|
832
|
+
for (const [index, request] of captured.entries()) {
|
|
833
|
+
const resource = resources[index];
|
|
834
|
+
const checked = import_effect.Result.isFailure(resource) ? import_effect.Result.fail(resource.failure) : yield* import_effect.Effect.result(
|
|
835
|
+
(0, import_makeCapability.makeCapability)("fs:read", resource.success).pipe(
|
|
836
|
+
import_effect.Effect.flatMap((capability) => grants.check(capability)),
|
|
837
|
+
import_effect.Effect.mapError(refuse("read", resource.success))
|
|
838
|
+
)
|
|
839
|
+
);
|
|
840
|
+
if (import_effect.Result.isFailure(checked)) {
|
|
841
|
+
entries.push({ index, path: request.path, result: import_effect.Result.fail(checked.failure) });
|
|
842
|
+
} else granted.push({ request, index, resource: import_effect.Result.getOrThrow(resource) });
|
|
843
|
+
}
|
|
844
|
+
const current = granted.length === 0 ? [] : yield* resolvedResources(granted.map(({ request }) => request));
|
|
845
|
+
const settled = yield* import_effect.Effect.forEach(granted, ({ resource }, position) => {
|
|
846
|
+
const now = current[position];
|
|
847
|
+
return import_effect.Effect.result(
|
|
848
|
+
import_effect.Result.isFailure(now) ? import_effect.Effect.fail(now.failure) : now.success === resource ? import_effect.Effect.void : deny("fs:read", "read", resource, "path no longer names the resource that was authorized")
|
|
849
|
+
);
|
|
850
|
+
});
|
|
851
|
+
for (const [position, member] of granted.entries()) {
|
|
852
|
+
const checked = settled[position];
|
|
853
|
+
if (import_effect.Result.isFailure(checked)) {
|
|
854
|
+
entries.push({ index: member.index, path: member.request.path, result: import_effect.Result.fail(checked.failure) });
|
|
855
|
+
} else {
|
|
856
|
+
admitted.push(member.request);
|
|
857
|
+
indexes.push(member.index);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
yield* verifyRoot();
|
|
861
|
+
if (admitted.length > 0) {
|
|
862
|
+
const measured = yield* atomic.execute({
|
|
863
|
+
operation: "batch",
|
|
864
|
+
boundaryRoot,
|
|
865
|
+
logicalRoot,
|
|
866
|
+
rootIdentity: rootIdentity.value,
|
|
867
|
+
requests: admitted
|
|
868
|
+
});
|
|
869
|
+
for (const entry of measured.entries) {
|
|
870
|
+
entries.push({
|
|
871
|
+
index: indexes[entry.index],
|
|
872
|
+
path: entry.path,
|
|
873
|
+
result: publicValue(entry.path, entry.result)
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
entries.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : a.index - b.index);
|
|
878
|
+
return { rootIdentity: rootIdentity.value, entries };
|
|
879
|
+
});
|
|
880
|
+
return Object.assign(guarded, {
|
|
881
|
+
[Batch.FileSystemBatchTypeId]: {
|
|
882
|
+
maxSize: Math.min(limits.size, Batch.maxBatchSize),
|
|
883
|
+
maxResponseBytes: limits.response,
|
|
884
|
+
execute: executeBatch
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
})
|
|
889
|
+
);
|
|
890
|
+
//# sourceMappingURL=FileSystem.js.map
|