@smthrs/kernel 0.0.0-stage → 1.0.0-rc.3

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.
Files changed (267) hide show
  1. package/CHANGELOG.md +206 -0
  2. package/LICENSE +21 -0
  3. package/README.md +154 -2
  4. package/dist/cjs/CapabilitySet.d.ts +6 -0
  5. package/dist/cjs/CapabilitySet.d.ts.map +1 -0
  6. package/dist/cjs/CapabilitySet.js +19 -0
  7. package/dist/cjs/CapabilitySet.js.map +7 -0
  8. package/dist/cjs/ChildProcessEnvironment.d.ts +60 -0
  9. package/dist/cjs/ChildProcessEnvironment.d.ts.map +1 -0
  10. package/dist/cjs/ChildProcessEnvironment.js +61 -0
  11. package/dist/cjs/ChildProcessEnvironment.js.map +7 -0
  12. package/dist/cjs/ChildProcessSpawner.d.ts +100 -0
  13. package/dist/cjs/ChildProcessSpawner.d.ts.map +1 -0
  14. package/dist/cjs/ChildProcessSpawner.js +291 -0
  15. package/dist/cjs/ChildProcessSpawner.js.map +7 -0
  16. package/dist/cjs/CommandLine.d.ts +173 -0
  17. package/dist/cjs/CommandLine.d.ts.map +1 -0
  18. package/dist/cjs/CommandLine.js +54 -0
  19. package/dist/cjs/CommandLine.js.map +7 -0
  20. package/dist/cjs/ContainedSpawner.d.ts +135 -0
  21. package/dist/cjs/ContainedSpawner.d.ts.map +1 -0
  22. package/dist/cjs/ContainedSpawner.js +141 -0
  23. package/dist/cjs/ContainedSpawner.js.map +7 -0
  24. package/dist/cjs/FileSystem.d.ts +452 -0
  25. package/dist/cjs/FileSystem.d.ts.map +1 -0
  26. package/dist/cjs/FileSystem.js +890 -0
  27. package/dist/cjs/FileSystem.js.map +7 -0
  28. package/dist/cjs/FileSystemBatch.d.ts +94 -0
  29. package/dist/cjs/FileSystemBatch.d.ts.map +1 -0
  30. package/dist/cjs/FileSystemBatch.js +31 -0
  31. package/dist/cjs/FileSystemBatch.js.map +7 -0
  32. package/dist/cjs/GrantEvent.d.ts +240 -0
  33. package/dist/cjs/GrantEvent.d.ts.map +1 -0
  34. package/dist/cjs/GrantEvent.js +115 -0
  35. package/dist/cjs/GrantEvent.js.map +7 -0
  36. package/dist/cjs/GrantStore.d.ts +270 -0
  37. package/dist/cjs/GrantStore.d.ts.map +1 -0
  38. package/dist/cjs/GrantStore.js +857 -0
  39. package/dist/cjs/GrantStore.js.map +7 -0
  40. package/dist/cjs/HostError.d.ts +36 -0
  41. package/dist/cjs/HostError.d.ts.map +1 -0
  42. package/dist/cjs/HostError.js +17 -0
  43. package/dist/cjs/HostError.js.map +7 -0
  44. package/dist/cjs/HostServices.d.ts +101 -0
  45. package/dist/cjs/HostServices.d.ts.map +1 -0
  46. package/dist/cjs/HostServices.js +66 -0
  47. package/dist/cjs/HostServices.js.map +7 -0
  48. package/dist/cjs/HttpClient.d.ts +177 -0
  49. package/dist/cjs/HttpClient.d.ts.map +1 -0
  50. package/dist/cjs/HttpClient.js +217 -0
  51. package/dist/cjs/HttpClient.js.map +7 -0
  52. package/dist/cjs/Jj.d.ts +67 -0
  53. package/dist/cjs/Jj.d.ts.map +1 -0
  54. package/dist/cjs/Jj.js +118 -0
  55. package/dist/cjs/Jj.js.map +7 -0
  56. package/dist/cjs/JournalGrantStore.d.ts +65 -0
  57. package/dist/cjs/JournalGrantStore.d.ts.map +1 -0
  58. package/dist/cjs/JournalGrantStore.js +354 -0
  59. package/dist/cjs/JournalGrantStore.js.map +7 -0
  60. package/dist/cjs/Path.d.ts +37 -0
  61. package/dist/cjs/Path.d.ts.map +1 -0
  62. package/dist/cjs/Path.js +46 -0
  63. package/dist/cjs/Path.js.map +7 -0
  64. package/dist/cjs/ProcessConfinement.d.ts +55 -0
  65. package/dist/cjs/ProcessConfinement.d.ts.map +1 -0
  66. package/dist/cjs/ProcessConfinement.js +94 -0
  67. package/dist/cjs/ProcessConfinement.js.map +7 -0
  68. package/dist/cjs/ProcessLedger.d.ts +260 -0
  69. package/dist/cjs/ProcessLedger.d.ts.map +1 -0
  70. package/dist/cjs/ProcessLedger.js +190 -0
  71. package/dist/cjs/ProcessLedger.js.map +7 -0
  72. package/dist/cjs/Rooted.d.ts +53 -0
  73. package/dist/cjs/Rooted.d.ts.map +1 -0
  74. package/dist/cjs/Rooted.js +101 -0
  75. package/dist/cjs/Rooted.js.map +7 -0
  76. package/dist/cjs/Unreachable.d.ts +27 -0
  77. package/dist/cjs/Unreachable.d.ts.map +1 -0
  78. package/dist/cjs/Unreachable.js +43 -0
  79. package/dist/cjs/Unreachable.js.map +7 -0
  80. package/dist/cjs/Workspace.d.ts +62 -0
  81. package/dist/cjs/Workspace.d.ts.map +1 -0
  82. package/dist/cjs/Workspace.js +35 -0
  83. package/dist/cjs/Workspace.js.map +7 -0
  84. package/dist/cjs/index.d.ts +161 -0
  85. package/dist/cjs/index.d.ts.map +1 -0
  86. package/dist/cjs/index.js +73 -0
  87. package/dist/cjs/index.js.map +7 -0
  88. package/dist/cjs/internal/Containment.d.ts +24 -0
  89. package/dist/cjs/internal/Containment.d.ts.map +1 -0
  90. package/dist/cjs/internal/Containment.js +30 -0
  91. package/dist/cjs/internal/Containment.js.map +7 -0
  92. package/dist/cjs/internal/Pipeline.d.ts +18 -0
  93. package/dist/cjs/internal/Pipeline.d.ts.map +1 -0
  94. package/dist/cjs/internal/Pipeline.js +90 -0
  95. package/dist/cjs/internal/Pipeline.js.map +7 -0
  96. package/dist/cjs/internal/makeCapability.d.ts +16 -0
  97. package/dist/cjs/internal/makeCapability.d.ts.map +1 -0
  98. package/dist/cjs/internal/makeCapability.js +44 -0
  99. package/dist/cjs/internal/makeCapability.js.map +7 -0
  100. package/dist/cjs/package.json +1 -0
  101. package/dist/cjs/test/FileSystemBatchContract.d.ts +15 -0
  102. package/dist/cjs/test/FileSystemBatchContract.d.ts.map +1 -0
  103. package/dist/cjs/test/FileSystemBatchContract.js +76 -0
  104. package/dist/cjs/test/FileSystemBatchContract.js.map +7 -0
  105. package/dist/cjs/test/HostContract.d.ts +225 -0
  106. package/dist/cjs/test/HostContract.d.ts.map +1 -0
  107. package/dist/cjs/test/HostContract.js +489 -0
  108. package/dist/cjs/test/HostContract.js.map +7 -0
  109. package/dist/cjs/test/TestGrantStore.d.ts +31 -0
  110. package/dist/cjs/test/TestGrantStore.d.ts.map +1 -0
  111. package/dist/cjs/test/TestGrantStore.js +65 -0
  112. package/dist/cjs/test/TestGrantStore.js.map +7 -0
  113. package/dist/esm/CapabilitySet.d.ts +6 -0
  114. package/dist/esm/CapabilitySet.d.ts.map +1 -0
  115. package/dist/esm/CapabilitySet.js +6 -0
  116. package/dist/esm/CapabilitySet.js.map +1 -0
  117. package/dist/esm/ChildProcessEnvironment.d.ts +60 -0
  118. package/dist/esm/ChildProcessEnvironment.d.ts.map +1 -0
  119. package/dist/esm/ChildProcessEnvironment.js +91 -0
  120. package/dist/esm/ChildProcessEnvironment.js.map +1 -0
  121. package/dist/esm/ChildProcessSpawner.d.ts +100 -0
  122. package/dist/esm/ChildProcessSpawner.d.ts.map +1 -0
  123. package/dist/esm/ChildProcessSpawner.js +342 -0
  124. package/dist/esm/ChildProcessSpawner.js.map +1 -0
  125. package/dist/esm/CommandLine.d.ts +173 -0
  126. package/dist/esm/CommandLine.d.ts.map +1 -0
  127. package/dist/esm/CommandLine.js +195 -0
  128. package/dist/esm/CommandLine.js.map +1 -0
  129. package/dist/esm/ContainedSpawner.d.ts +135 -0
  130. package/dist/esm/ContainedSpawner.d.ts.map +1 -0
  131. package/dist/esm/ContainedSpawner.js +180 -0
  132. package/dist/esm/ContainedSpawner.js.map +1 -0
  133. package/dist/esm/FileSystem.d.ts +452 -0
  134. package/dist/esm/FileSystem.d.ts.map +1 -0
  135. package/dist/esm/FileSystem.js +989 -0
  136. package/dist/esm/FileSystem.js.map +1 -0
  137. package/dist/esm/FileSystemBatch.d.ts +94 -0
  138. package/dist/esm/FileSystemBatch.d.ts.map +1 -0
  139. package/dist/esm/FileSystemBatch.js +26 -0
  140. package/dist/esm/FileSystemBatch.js.map +1 -0
  141. package/dist/esm/GrantEvent.d.ts +240 -0
  142. package/dist/esm/GrantEvent.d.ts.map +1 -0
  143. package/dist/esm/GrantEvent.js +155 -0
  144. package/dist/esm/GrantEvent.js.map +1 -0
  145. package/dist/esm/GrantStore.d.ts +270 -0
  146. package/dist/esm/GrantStore.d.ts.map +1 -0
  147. package/dist/esm/GrantStore.js +951 -0
  148. package/dist/esm/GrantStore.js.map +1 -0
  149. package/dist/esm/HostError.d.ts +36 -0
  150. package/dist/esm/HostError.d.ts.map +1 -0
  151. package/dist/esm/HostError.js +20 -0
  152. package/dist/esm/HostError.js.map +1 -0
  153. package/dist/esm/HostServices.d.ts +101 -0
  154. package/dist/esm/HostServices.d.ts.map +1 -0
  155. package/dist/esm/HostServices.js +76 -0
  156. package/dist/esm/HostServices.js.map +1 -0
  157. package/dist/esm/HttpClient.d.ts +177 -0
  158. package/dist/esm/HttpClient.d.ts.map +1 -0
  159. package/dist/esm/HttpClient.js +299 -0
  160. package/dist/esm/HttpClient.js.map +1 -0
  161. package/dist/esm/Jj.d.ts +67 -0
  162. package/dist/esm/Jj.d.ts.map +1 -0
  163. package/dist/esm/Jj.js +111 -0
  164. package/dist/esm/Jj.js.map +1 -0
  165. package/dist/esm/JournalGrantStore.d.ts +65 -0
  166. package/dist/esm/JournalGrantStore.d.ts.map +1 -0
  167. package/dist/esm/JournalGrantStore.js +350 -0
  168. package/dist/esm/JournalGrantStore.js.map +1 -0
  169. package/dist/esm/Path.d.ts +37 -0
  170. package/dist/esm/Path.d.ts.map +1 -0
  171. package/dist/esm/Path.js +34 -0
  172. package/dist/esm/Path.js.map +1 -0
  173. package/dist/esm/ProcessConfinement.d.ts +55 -0
  174. package/dist/esm/ProcessConfinement.d.ts.map +1 -0
  175. package/dist/esm/ProcessConfinement.js +106 -0
  176. package/dist/esm/ProcessConfinement.js.map +1 -0
  177. package/dist/esm/ProcessLedger.d.ts +260 -0
  178. package/dist/esm/ProcessLedger.d.ts.map +1 -0
  179. package/dist/esm/ProcessLedger.js +283 -0
  180. package/dist/esm/ProcessLedger.js.map +1 -0
  181. package/dist/esm/Rooted.d.ts +53 -0
  182. package/dist/esm/Rooted.d.ts.map +1 -0
  183. package/dist/esm/Rooted.js +104 -0
  184. package/dist/esm/Rooted.js.map +1 -0
  185. package/dist/esm/Unreachable.d.ts +27 -0
  186. package/dist/esm/Unreachable.d.ts.map +1 -0
  187. package/dist/esm/Unreachable.js +26 -0
  188. package/dist/esm/Unreachable.js.map +1 -0
  189. package/dist/esm/Workspace.d.ts +62 -0
  190. package/dist/esm/Workspace.d.ts.map +1 -0
  191. package/dist/esm/Workspace.js +51 -0
  192. package/dist/esm/Workspace.js.map +1 -0
  193. package/dist/esm/index.d.ts +161 -0
  194. package/dist/esm/index.d.ts.map +1 -0
  195. package/dist/esm/index.js +151 -0
  196. package/dist/esm/index.js.map +1 -0
  197. package/dist/esm/internal/Containment.d.ts +24 -0
  198. package/dist/esm/internal/Containment.d.ts.map +1 -0
  199. package/dist/esm/internal/Containment.js +27 -0
  200. package/dist/esm/internal/Containment.js.map +1 -0
  201. package/dist/esm/internal/Pipeline.d.ts +18 -0
  202. package/dist/esm/internal/Pipeline.d.ts.map +1 -0
  203. package/dist/esm/internal/Pipeline.js +68 -0
  204. package/dist/esm/internal/Pipeline.js.map +1 -0
  205. package/dist/esm/internal/makeCapability.d.ts +16 -0
  206. package/dist/esm/internal/makeCapability.d.ts.map +1 -0
  207. package/dist/esm/internal/makeCapability.js +22 -0
  208. package/dist/esm/internal/makeCapability.js.map +1 -0
  209. package/dist/esm/test/FileSystemBatchContract.d.ts +15 -0
  210. package/dist/esm/test/FileSystemBatchContract.d.ts.map +1 -0
  211. package/dist/esm/test/FileSystemBatchContract.js +57 -0
  212. package/dist/esm/test/FileSystemBatchContract.js.map +1 -0
  213. package/dist/esm/test/HostContract.d.ts +225 -0
  214. package/dist/esm/test/HostContract.d.ts.map +1 -0
  215. package/dist/esm/test/HostContract.js +460 -0
  216. package/dist/esm/test/HostContract.js.map +1 -0
  217. package/dist/esm/test/TestGrantStore.d.ts +31 -0
  218. package/dist/esm/test/TestGrantStore.d.ts.map +1 -0
  219. package/dist/esm/test/TestGrantStore.js +57 -0
  220. package/dist/esm/test/TestGrantStore.js.map +1 -0
  221. package/docs/README.md +162 -0
  222. package/docs/api.md +1532 -0
  223. package/docs/concepts/decoration-in-place.md +117 -0
  224. package/docs/concepts/filesystem-batches.md +58 -0
  225. package/docs/concepts/filesystem-confinement.md +168 -0
  226. package/docs/concepts/grant-decisions.md +181 -0
  227. package/docs/concepts/process-containment.md +155 -0
  228. package/docs/contract.md +14 -0
  229. package/docs/guides/adapt-a-new-host-platform.md +148 -0
  230. package/docs/guides/answer-permission-requests.md +151 -0
  231. package/docs/guides/authorize-network-and-model-calls.md +118 -0
  232. package/docs/guides/contain-spawned-processes.md +118 -0
  233. package/docs/guides/guard-a-host-bundle.md +109 -0
  234. package/docs/guides/persist-grants-across-restarts.md +135 -0
  235. package/docs/guides/write-a-capability-policy.md +159 -0
  236. package/docs/installation.md +115 -0
  237. package/docs/quickstart.md +182 -0
  238. package/docs/testing.md +108 -0
  239. package/docs/troubleshooting.md +281 -0
  240. package/package.json +344 -3
  241. package/src/CapabilitySet.ts +6 -0
  242. package/src/ChildProcessEnvironment.ts +98 -0
  243. package/src/ChildProcessSpawner.ts +414 -0
  244. package/src/CommandLine.ts +250 -0
  245. package/src/ContainedSpawner.ts +284 -0
  246. package/src/FileSystem.ts +1655 -0
  247. package/src/FileSystemBatch.ts +90 -0
  248. package/src/GrantEvent.ts +174 -0
  249. package/src/GrantStore.ts +1345 -0
  250. package/src/HostError.ts +37 -0
  251. package/src/HostServices.ts +133 -0
  252. package/src/HttpClient.ts +406 -0
  253. package/src/Jj.ts +159 -0
  254. package/src/JournalGrantStore.ts +461 -0
  255. package/src/Path.ts +47 -0
  256. package/src/ProcessConfinement.ts +156 -0
  257. package/src/ProcessLedger.ts +423 -0
  258. package/src/Rooted.ts +128 -0
  259. package/src/Unreachable.ts +30 -0
  260. package/src/Workspace.ts +65 -0
  261. package/src/index.ts +181 -0
  262. package/src/internal/Containment.ts +37 -0
  263. package/src/internal/Pipeline.ts +85 -0
  264. package/src/internal/makeCapability.ts +26 -0
  265. package/src/test/FileSystemBatchContract.ts +59 -0
  266. package/src/test/HostContract.ts +785 -0
  267. 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