@vibe-agent-toolkit/utils 0.2.0-rc.11 → 0.2.0-rc.12

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 (322) hide show
  1. package/README.md +111 -9
  2. package/dist/archive/archive.d.ts +129 -0
  3. package/dist/archive/archive.d.ts.map +1 -0
  4. package/dist/archive/archive.js +223 -0
  5. package/dist/archive/archive.js.map +1 -0
  6. package/dist/archive/entries.d.ts +132 -0
  7. package/dist/archive/entries.d.ts.map +1 -0
  8. package/dist/archive/entries.js +223 -0
  9. package/dist/archive/entries.js.map +1 -0
  10. package/dist/archive/limits.d.ts +39 -0
  11. package/dist/archive/limits.d.ts.map +1 -0
  12. package/dist/archive/limits.js +59 -0
  13. package/dist/archive/limits.js.map +1 -0
  14. package/dist/archive/records.d.ts +14 -0
  15. package/dist/archive/records.d.ts.map +1 -0
  16. package/dist/archive/records.js +3 -0
  17. package/dist/archive/records.js.map +1 -0
  18. package/dist/archive/tar-reader.d.ts +18 -0
  19. package/dist/archive/tar-reader.d.ts.map +1 -0
  20. package/dist/archive/tar-reader.js +173 -0
  21. package/dist/archive/tar-reader.js.map +1 -0
  22. package/dist/archive/zip-reader.d.ts +76 -0
  23. package/dist/archive/zip-reader.d.ts.map +1 -0
  24. package/dist/archive/zip-reader.js +376 -0
  25. package/dist/archive/zip-reader.js.map +1 -0
  26. package/dist/archive.d.ts +14 -0
  27. package/dist/archive.d.ts.map +1 -0
  28. package/dist/archive.js +12 -0
  29. package/dist/archive.js.map +1 -0
  30. package/dist/asset-reference.d.ts +17 -1
  31. package/dist/asset-reference.d.ts.map +1 -1
  32. package/dist/asset-reference.js +31 -7
  33. package/dist/asset-reference.js.map +1 -1
  34. package/dist/dirent-kind.d.ts +13 -40
  35. package/dist/dirent-kind.d.ts.map +1 -1
  36. package/dist/dirent-kind.js +21 -69
  37. package/dist/dirent-kind.js.map +1 -1
  38. package/dist/entrypoint.d.ts +2 -2
  39. package/dist/entrypoint.d.ts.map +1 -1
  40. package/dist/entrypoint.js +4 -3
  41. package/dist/entrypoint.js.map +1 -1
  42. package/dist/env-flag.d.ts +2 -0
  43. package/dist/env-flag.d.ts.map +1 -1
  44. package/dist/env-flag.js +2 -0
  45. package/dist/env-flag.js.map +1 -1
  46. package/dist/errors/confirmed-absent.d.ts +41 -0
  47. package/dist/errors/confirmed-absent.d.ts.map +1 -0
  48. package/dist/errors/confirmed-absent.js +43 -0
  49. package/dist/errors/confirmed-absent.js.map +1 -0
  50. package/dist/errors/errno-table.d.ts +177 -0
  51. package/dist/errors/errno-table.d.ts.map +1 -0
  52. package/dist/errors/errno-table.js +343 -0
  53. package/dist/errors/errno-table.js.map +1 -0
  54. package/dist/errors/fs-boundary.d.ts +36 -0
  55. package/dist/errors/fs-boundary.d.ts.map +1 -0
  56. package/dist/errors/fs-boundary.js +121 -0
  57. package/dist/errors/fs-boundary.js.map +1 -0
  58. package/dist/errors/fs-fault.d.ts +176 -0
  59. package/dist/errors/fs-fault.d.ts.map +1 -0
  60. package/dist/errors/fs-fault.js +215 -0
  61. package/dist/errors/fs-fault.js.map +1 -0
  62. package/dist/errors/git-errno.d.ts +13 -0
  63. package/dist/errors/git-errno.d.ts.map +1 -0
  64. package/dist/errors/git-errno.js +45 -0
  65. package/dist/errors/git-errno.js.map +1 -0
  66. package/dist/errors/path-present.d.ts +72 -0
  67. package/dist/errors/path-present.d.ts.map +1 -0
  68. package/dist/errors/path-present.js +110 -0
  69. package/dist/errors/path-present.js.map +1 -0
  70. package/dist/errors/rag-index-empty.d.ts +16 -0
  71. package/dist/errors/rag-index-empty.d.ts.map +1 -0
  72. package/dist/errors/rag-index-empty.js +16 -0
  73. package/dist/errors/rag-index-empty.js.map +1 -0
  74. package/dist/errors/suppressed-faults.d.ts +34 -0
  75. package/dist/errors/suppressed-faults.d.ts.map +1 -0
  76. package/dist/errors/suppressed-faults.js +66 -0
  77. package/dist/errors/suppressed-faults.js.map +1 -0
  78. package/dist/file-crawler.d.ts +13 -18
  79. package/dist/file-crawler.d.ts.map +1 -1
  80. package/dist/file-crawler.js +140 -24
  81. package/dist/file-crawler.js.map +1 -1
  82. package/dist/fs-utils.d.ts +75 -50
  83. package/dist/fs-utils.d.ts.map +1 -1
  84. package/dist/fs-utils.js +148 -158
  85. package/dist/fs-utils.js.map +1 -1
  86. package/dist/fs.d.ts +4 -4
  87. package/dist/fs.d.ts.map +1 -1
  88. package/dist/fs.js +4 -4
  89. package/dist/fs.js.map +1 -1
  90. package/dist/git-snapshot.d.ts +25 -17
  91. package/dist/git-snapshot.d.ts.map +1 -1
  92. package/dist/git-snapshot.js +65 -11
  93. package/dist/git-snapshot.js.map +1 -1
  94. package/dist/git-tracker.d.ts +11 -3
  95. package/dist/git-tracker.d.ts.map +1 -1
  96. package/dist/git-tracker.js +29 -6
  97. package/dist/git-tracker.js.map +1 -1
  98. package/dist/git-utils.d.ts.map +1 -1
  99. package/dist/git-utils.js +7 -35
  100. package/dist/git-utils.js.map +1 -1
  101. package/dist/git.d.ts +1 -1
  102. package/dist/git.d.ts.map +1 -1
  103. package/dist/git.js +1 -1
  104. package/dist/git.js.map +1 -1
  105. package/dist/gitignore-checker.d.ts.map +1 -1
  106. package/dist/gitignore-checker.js +5 -4
  107. package/dist/gitignore-checker.js.map +1 -1
  108. package/dist/in-order.d.ts +93 -0
  109. package/dist/in-order.d.ts.map +1 -0
  110. package/dist/in-order.js +165 -0
  111. package/dist/in-order.js.map +1 -0
  112. package/dist/index.d.ts +20 -4
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.js +30 -4
  115. package/dist/index.js.map +1 -1
  116. package/dist/kill-process-tree.d.ts +43 -0
  117. package/dist/kill-process-tree.d.ts.map +1 -0
  118. package/dist/kill-process-tree.js +48 -0
  119. package/dist/kill-process-tree.js.map +1 -0
  120. package/dist/link-following.d.ts +115 -0
  121. package/dist/link-following.d.ts.map +1 -0
  122. package/dist/link-following.js +283 -0
  123. package/dist/link-following.js.map +1 -0
  124. package/dist/listing-refusal.d.ts +44 -0
  125. package/dist/listing-refusal.d.ts.map +1 -1
  126. package/dist/listing-refusal.js +79 -2
  127. package/dist/listing-refusal.js.map +1 -1
  128. package/dist/name-fold.d.ts +25 -0
  129. package/dist/name-fold.d.ts.map +1 -0
  130. package/dist/name-fold.js +40 -0
  131. package/dist/name-fold.js.map +1 -0
  132. package/dist/path-containment.d.ts +3 -1
  133. package/dist/path-containment.d.ts.map +1 -1
  134. package/dist/path-containment.js +13 -7
  135. package/dist/path-containment.js.map +1 -1
  136. package/dist/path-utils.d.ts +14 -17
  137. package/dist/path-utils.d.ts.map +1 -1
  138. package/dist/path-utils.js +37 -55
  139. package/dist/path-utils.js.map +1 -1
  140. package/dist/process.d.ts +2 -0
  141. package/dist/process.d.ts.map +1 -1
  142. package/dist/process.js +2 -0
  143. package/dist/process.js.map +1 -1
  144. package/dist/project-utils.d.ts.map +1 -1
  145. package/dist/project-utils.js +8 -9
  146. package/dist/project-utils.js.map +1 -1
  147. package/dist/safe-exec.d.ts +6 -2
  148. package/dist/safe-exec.d.ts.map +1 -1
  149. package/dist/safe-exec.js +1 -1
  150. package/dist/safe-exec.js.map +1 -1
  151. package/dist/skill-targets.d.ts.map +1 -1
  152. package/dist/skill-targets.js +3 -1
  153. package/dist/skill-targets.js.map +1 -1
  154. package/dist/skill-test/spawn-claude.d.ts +0 -15
  155. package/dist/skill-test/spawn-claude.d.ts.map +1 -1
  156. package/dist/skill-test/spawn-claude.js +11 -50
  157. package/dist/skill-test/spawn-claude.js.map +1 -1
  158. package/dist/special-file.d.ts +18 -0
  159. package/dist/special-file.d.ts.map +1 -0
  160. package/dist/special-file.js +21 -0
  161. package/dist/special-file.js.map +1 -0
  162. package/dist/terminating-signals.d.ts +129 -0
  163. package/dist/terminating-signals.d.ts.map +1 -0
  164. package/dist/terminating-signals.js +210 -0
  165. package/dist/terminating-signals.js.map +1 -0
  166. package/dist/test-helpers.d.ts +9 -5
  167. package/dist/test-helpers.d.ts.map +1 -1
  168. package/dist/test-helpers.js +18 -21
  169. package/dist/test-helpers.js.map +1 -1
  170. package/dist/test-scratch-guard.d.ts +34 -0
  171. package/dist/test-scratch-guard.d.ts.map +1 -0
  172. package/dist/test-scratch-guard.js +45 -0
  173. package/dist/test-scratch-guard.js.map +1 -0
  174. package/dist/testing/archive-fixtures.d.ts +147 -0
  175. package/dist/testing/archive-fixtures.d.ts.map +1 -0
  176. package/dist/testing/archive-fixtures.js +449 -0
  177. package/dist/testing/archive-fixtures.js.map +1 -0
  178. package/dist/testing/executables.d.ts +6 -0
  179. package/dist/testing/executables.d.ts.map +1 -1
  180. package/dist/testing/executables.js +20 -9
  181. package/dist/testing/executables.js.map +1 -1
  182. package/dist/testing/fault-fs-preload.d.ts +16 -0
  183. package/dist/testing/fault-fs-preload.d.ts.map +1 -0
  184. package/dist/testing/fault-fs-preload.js +22 -0
  185. package/dist/testing/fault-fs-preload.js.map +1 -0
  186. package/dist/testing/fault-fs.d.ts +115 -0
  187. package/dist/testing/fault-fs.d.ts.map +1 -0
  188. package/dist/testing/fault-fs.js +411 -0
  189. package/dist/testing/fault-fs.js.map +1 -0
  190. package/dist/testing/fault-spec.d.ts +83 -0
  191. package/dist/testing/fault-spec.d.ts.map +1 -0
  192. package/dist/testing/fault-spec.js +132 -0
  193. package/dist/testing/fault-spec.js.map +1 -0
  194. package/dist/testing/hostile-tree.d.ts.map +1 -1
  195. package/dist/testing/hostile-tree.js +3 -4
  196. package/dist/testing/hostile-tree.js.map +1 -1
  197. package/dist/testing/platform-gates.d.ts +8 -0
  198. package/dist/testing/platform-gates.d.ts.map +1 -1
  199. package/dist/testing/platform-gates.js +24 -0
  200. package/dist/testing/platform-gates.js.map +1 -1
  201. package/dist/testing/temp-dir.d.ts +31 -0
  202. package/dist/testing/temp-dir.d.ts.map +1 -1
  203. package/dist/testing/temp-dir.js +63 -0
  204. package/dist/testing/temp-dir.js.map +1 -1
  205. package/dist/testing/tier-timeout.d.ts +34 -0
  206. package/dist/testing/tier-timeout.d.ts.map +1 -0
  207. package/dist/testing/tier-timeout.js +45 -0
  208. package/dist/testing/tier-timeout.js.map +1 -0
  209. package/dist/testing/tree-snapshot.d.ts +71 -0
  210. package/dist/testing/tree-snapshot.d.ts.map +1 -0
  211. package/dist/testing/tree-snapshot.js +163 -0
  212. package/dist/testing/tree-snapshot.js.map +1 -0
  213. package/dist/testing.d.ts +6 -0
  214. package/dist/testing.d.ts.map +1 -1
  215. package/dist/testing.js +8 -1
  216. package/dist/testing.js.map +1 -1
  217. package/dist/text-file.d.ts +14 -1
  218. package/dist/text-file.d.ts.map +1 -1
  219. package/dist/text-file.js +41 -8
  220. package/dist/text-file.js.map +1 -1
  221. package/dist/timing-dump.d.ts.map +1 -1
  222. package/dist/timing-dump.js +2 -10
  223. package/dist/timing-dump.js.map +1 -1
  224. package/dist/tree-change/apply-decisions.d.ts +101 -0
  225. package/dist/tree-change/apply-decisions.d.ts.map +1 -0
  226. package/dist/tree-change/apply-decisions.js +139 -0
  227. package/dist/tree-change/apply-decisions.js.map +1 -0
  228. package/dist/tree-change/apply.d.ts +98 -0
  229. package/dist/tree-change/apply.d.ts.map +1 -0
  230. package/dist/tree-change/apply.js +364 -0
  231. package/dist/tree-change/apply.js.map +1 -0
  232. package/dist/tree-change/copy-decisions.d.ts +58 -0
  233. package/dist/tree-change/copy-decisions.d.ts.map +1 -0
  234. package/dist/tree-change/copy-decisions.js +66 -0
  235. package/dist/tree-change/copy-decisions.js.map +1 -0
  236. package/dist/tree-change/copy-tree.d.ts +125 -0
  237. package/dist/tree-change/copy-tree.d.ts.map +1 -0
  238. package/dist/tree-change/copy-tree.js +279 -0
  239. package/dist/tree-change/copy-tree.js.map +1 -0
  240. package/dist/tree-change/files.d.ts +167 -0
  241. package/dist/tree-change/files.d.ts.map +1 -0
  242. package/dist/tree-change/files.js +423 -0
  243. package/dist/tree-change/files.js.map +1 -0
  244. package/dist/tree-change/followed-walk.d.ts +54 -0
  245. package/dist/tree-change/followed-walk.d.ts.map +1 -0
  246. package/dist/tree-change/followed-walk.js +81 -0
  247. package/dist/tree-change/followed-walk.js.map +1 -0
  248. package/dist/tree-change/host-chain.d.ts +48 -0
  249. package/dist/tree-change/host-chain.d.ts.map +1 -0
  250. package/dist/tree-change/host-chain.js +85 -0
  251. package/dist/tree-change/host-chain.js.map +1 -0
  252. package/dist/tree-change/identity-compare.d.ts +61 -0
  253. package/dist/tree-change/identity-compare.d.ts.map +1 -0
  254. package/dist/tree-change/identity-compare.js +115 -0
  255. package/dist/tree-change/identity-compare.js.map +1 -0
  256. package/dist/tree-change/identity.d.ts +62 -0
  257. package/dist/tree-change/identity.d.ts.map +1 -0
  258. package/dist/tree-change/identity.js +152 -0
  259. package/dist/tree-change/identity.js.map +1 -0
  260. package/dist/tree-change/index.d.ts +24 -0
  261. package/dist/tree-change/index.d.ts.map +1 -0
  262. package/dist/tree-change/index.js +14 -0
  263. package/dist/tree-change/index.js.map +1 -0
  264. package/dist/tree-change/link-chain.d.ts +180 -0
  265. package/dist/tree-change/link-chain.d.ts.map +1 -0
  266. package/dist/tree-change/link-chain.js +377 -0
  267. package/dist/tree-change/link-chain.js.map +1 -0
  268. package/dist/tree-change/link-containment.d.ts +105 -0
  269. package/dist/tree-change/link-containment.d.ts.map +1 -0
  270. package/dist/tree-change/link-containment.js +196 -0
  271. package/dist/tree-change/link-containment.js.map +1 -0
  272. package/dist/tree-change/link-walk.d.ts +49 -0
  273. package/dist/tree-change/link-walk.d.ts.map +1 -0
  274. package/dist/tree-change/link-walk.js +69 -0
  275. package/dist/tree-change/link-walk.js.map +1 -0
  276. package/dist/tree-change/plan.d.ts +206 -0
  277. package/dist/tree-change/plan.d.ts.map +1 -0
  278. package/dist/tree-change/plan.js +320 -0
  279. package/dist/tree-change/plan.js.map +1 -0
  280. package/dist/tree-change/readable-tree.d.ts +44 -0
  281. package/dist/tree-change/readable-tree.d.ts.map +1 -0
  282. package/dist/tree-change/readable-tree.js +52 -0
  283. package/dist/tree-change/readable-tree.js.map +1 -0
  284. package/dist/tree-change/rollback-error.d.ts +29 -0
  285. package/dist/tree-change/rollback-error.d.ts.map +1 -0
  286. package/dist/tree-change/rollback-error.js +31 -0
  287. package/dist/tree-change/rollback-error.js.map +1 -0
  288. package/dist/tree-change/staging-names.d.ts +27 -0
  289. package/dist/tree-change/staging-names.d.ts.map +1 -0
  290. package/dist/tree-change/staging-names.js +40 -0
  291. package/dist/tree-change/staging-names.js.map +1 -0
  292. package/dist/tree-change/tree-walk.d.ts +81 -0
  293. package/dist/tree-change/tree-walk.d.ts.map +1 -0
  294. package/dist/tree-change/tree-walk.js +212 -0
  295. package/dist/tree-change/tree-walk.js.map +1 -0
  296. package/dist/win32-namespace.d.ts +24 -0
  297. package/dist/win32-namespace.d.ts.map +1 -0
  298. package/dist/win32-namespace.js +31 -0
  299. package/dist/win32-namespace.js.map +1 -0
  300. package/dist/yaml/surgical-yaml.d.ts +12 -3
  301. package/dist/yaml/surgical-yaml.d.ts.map +1 -1
  302. package/dist/yaml/surgical-yaml.js +16 -6
  303. package/dist/yaml/surgical-yaml.js.map +1 -1
  304. package/eslint/README.md +12 -4
  305. package/eslint/rules/fs-binding-scan.cjs +278 -0
  306. package/eslint/rules/no-adhoc-errno.cjs +364 -0
  307. package/eslint/rules/no-bare-symlink-in-tests.cjs +2 -3
  308. package/eslint/rules/no-blind-catch.cjs +1 -1
  309. package/eslint/rules/no-destructive-fs.cjs +359 -0
  310. package/eslint/rules/no-existssync.cjs +70 -0
  311. package/eslint/rules/no-fragile-entrypoint-guard.cjs +3 -2
  312. package/eslint/rules/no-link-following-fs.cjs +68 -0
  313. package/eslint/rules/no-literal-process-exit.cjs +3 -16
  314. package/eslint/rules/no-literal-test-timeout.cjs +214 -0
  315. package/eslint/rules/no-raw-signal-listener.cjs +224 -0
  316. package/eslint/rules/no-stdout-outside-writer.cjs +135 -0
  317. package/package.json +10 -2
  318. package/dist/errors/errno.d.ts +0 -51
  319. package/dist/errors/errno.d.ts.map +0 -1
  320. package/dist/errors/errno.js +0 -112
  321. package/dist/errors/errno.js.map +0 -1
  322. package/eslint/rules/no-fs-promises-cp.cjs +0 -43
@@ -0,0 +1,115 @@
1
+ /**
2
+ * `installFaultFs` — an in-process fs fault injector for tests.
3
+ *
4
+ * A destructive verb is only as safe as its behaviour on the filesystem's bad
5
+ * days: a full disk, a read-only mount, a file the OS refuses deep in a tree. The
6
+ * real thing cannot be summoned on demand, so this module fails a chosen call
7
+ * with a chosen errno, and can rewrite what `stat` reports (an `ino` of 0, a
8
+ * case-aliased identity).
9
+ *
10
+ * ## How it reaches code that never asked to be faulted
11
+ *
12
+ * 1. It replaces the methods ON THE BUILTIN OBJECTS — `require('node:fs')`,
13
+ * `fs.promises` (the same object as `node:fs/promises`) and
14
+ * `FileHandle.prototype` — for all three API styles (`*Sync`, callback,
15
+ * promise).
16
+ * 2. It then calls `module.syncBuiltinESMExports()`, so a named import
17
+ * (`import { rmSync } from 'node:fs'`) sees the replacement. `restore()` undoes
18
+ * both steps.
19
+ * 3. Dependencies that look methods up on the module object at call time — adm-zip
20
+ * (`require('fs')`, reading the tree the archive module zips) and write streams
21
+ * (`createWriteStream` resolves `open`/`write` from the module) — see the
22
+ * replacements with no extra seam. `test/integration/fault-fs-reach` and
23
+ * `test/integration/archive-extract` pin every one of these; none is assumed. (No archive
24
+ * library WRITES for VAT: an extraction's writes are the archive module's own `fs` calls.)
25
+ * 4. An fd to path map, filled from intercepted `open*`, gives handle and stream
26
+ * operations the path they act on.
27
+ * 5. `realpathSync.native` and `realpath.native` are properties of the functions, not
28
+ * of the module, so they are wrapped on the wrappers, traced as the op `realpath`.
29
+ *
30
+ * ## What it cannot reach
31
+ *
32
+ * ⚠️ `cpSync`, and on Node >= 23 recursive `rmSync`, are implemented natively: the
33
+ * harness can fail the CALL (family `write` / `remove`) but never a file inside it.
34
+ * A verb that wants per-file fault coverage must not use those calls.
35
+ *
36
+ * ⚠️ Recursive `fs.rm` / `fs.promises.rm` keep the `fs` functions they found the first time Node
37
+ * removed a tree in this process. If that was outside a session — on Node 22 any earlier
38
+ * recursive `rmSync`, which is what a test's cleanup does; on later Nodes an earlier `fs.rm` — no
39
+ * session sees or fails a call INSIDE the removal, only the `rm` call itself.
40
+ *
41
+ * ⚠️ A `FileHandle` is only traced when it was opened AFTER `installFaultFs`:
42
+ * the fd to path map cannot know the path of a handle that was already open, and
43
+ * the prototype is patched when the first traced handle is opened. Install first,
44
+ * then open.
45
+ *
46
+ * ## Counting
47
+ *
48
+ * Calls made synchronously from inside an intercepted call (an `fs` function that
49
+ * is itself written in terms of another public one) are not traced again, so one
50
+ * caller-visible operation is one {@link FsCall}. A rule fires on its `nth` match
51
+ * only; the calls around it succeed. `FsApi` `'stream'` is reserved: stream I/O
52
+ * reaches the harness as `callback` calls on the fs object, which is what it is —
53
+ * so one stream write is three calls (`open`, `write`, `close`), each counted.
54
+ *
55
+ * ⛔ Framework-free, like everything under `testing/`: no `vitest` import, so the
56
+ * `./testing` subpath keeps the empty third-party set its purity pin asserts.
57
+ */
58
+ import { type FaultRule, type FsApi, type FsOpFamily } from './fault-spec.js';
59
+ export interface FsCall {
60
+ readonly seq: number;
61
+ readonly op: string;
62
+ readonly family: FsOpFamily;
63
+ readonly api: FsApi;
64
+ /** Forward-slash, absolute; for handle ops, the path the fd was opened at. */
65
+ readonly path: string;
66
+ /** The rename / copyFile / symlink target side. */
67
+ readonly dest?: string;
68
+ }
69
+ export interface StatRewrite {
70
+ readonly op: 'stat' | 'lstat' | 'fstat';
71
+ readonly path: (path: string) => boolean;
72
+ readonly rewrite: <S extends {
73
+ ino: number | bigint;
74
+ dev: number | bigint;
75
+ }>(stats: S) => S;
76
+ }
77
+ export interface FaultFsSession {
78
+ /** Only calls under `within`. */
79
+ readonly calls: readonly FsCall[];
80
+ /** The calls that were failed. */
81
+ readonly fired: readonly FsCall[];
82
+ /** Idempotent; also undoes `syncBuiltinESMExports`. */
83
+ restore(): void;
84
+ /**
85
+ * Resolves once no traced asynchronous call is in flight. A caller's promise can settle while
86
+ * work it started goes on — Node's `rm` rejects on the first child that fails and leaves its
87
+ * siblings running — so a run is over when this resolves, not when its verb returns: restore
88
+ * earlier and that work runs on under the NEXT session.
89
+ *
90
+ * @throws Error when calls are still in flight after 10 s (a run that never settles is hung)
91
+ */
92
+ settled(): Promise<void>;
93
+ }
94
+ /**
95
+ * Run `work` — synchronous fs calls a TEST makes on its own behalf while a session is active (a mock
96
+ * standing in for a child process, capturing what it was handed) — outside the session: none of its
97
+ * calls is traced, so none becomes an injection point, and none is failed. Without it a harness's own
98
+ * bookkeeping is indistinguishable from the code under test.
99
+ *
100
+ * ⛔ Synchronous only: an asynchronous call started inside returns to the event loop with the session
101
+ * active again, and its continuation is traced.
102
+ */
103
+ export declare function untracedFs<T>(work: () => T): T;
104
+ /**
105
+ * Trace and fault fs calls on paths under `within` until `restore()` is called.
106
+ *
107
+ * One session at a time: a second install while one is active throws, because two
108
+ * stacked sets of wrappers cannot be unwound independently.
109
+ */
110
+ export declare function installFaultFs(options: {
111
+ within: string;
112
+ faults?: readonly FaultRule[];
113
+ rewrites?: readonly StatRewrite[];
114
+ }): FaultFsSession;
115
+ //# sourceMappingURL=fault-fs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fault-fs.d.ts","sourceRoot":"","sources":["../../src/testing/fault-fs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAQH,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,KAAK,EAAE,KAAK,UAAU,EAA4D,MAAM,iBAAiB,CAAC;AAExI,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC;IACpB,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;IACxC,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACzC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,SAAS;QAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC;CAC7F;AAED,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,kCAAkC;IAClC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,uDAAuD;IACvD,OAAO,IAAI,IAAI,CAAC;IAChB;;;;;;;OAOG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAqCD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAO9C;AAmQD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IAC9B,QAAQ,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;CACnC,GAAG,cAAc,CAwCjB"}
@@ -0,0 +1,411 @@
1
+ /**
2
+ * `installFaultFs` — an in-process fs fault injector for tests.
3
+ *
4
+ * A destructive verb is only as safe as its behaviour on the filesystem's bad
5
+ * days: a full disk, a read-only mount, a file the OS refuses deep in a tree. The
6
+ * real thing cannot be summoned on demand, so this module fails a chosen call
7
+ * with a chosen errno, and can rewrite what `stat` reports (an `ino` of 0, a
8
+ * case-aliased identity).
9
+ *
10
+ * ## How it reaches code that never asked to be faulted
11
+ *
12
+ * 1. It replaces the methods ON THE BUILTIN OBJECTS — `require('node:fs')`,
13
+ * `fs.promises` (the same object as `node:fs/promises`) and
14
+ * `FileHandle.prototype` — for all three API styles (`*Sync`, callback,
15
+ * promise).
16
+ * 2. It then calls `module.syncBuiltinESMExports()`, so a named import
17
+ * (`import { rmSync } from 'node:fs'`) sees the replacement. `restore()` undoes
18
+ * both steps.
19
+ * 3. Dependencies that look methods up on the module object at call time — adm-zip
20
+ * (`require('fs')`, reading the tree the archive module zips) and write streams
21
+ * (`createWriteStream` resolves `open`/`write` from the module) — see the
22
+ * replacements with no extra seam. `test/integration/fault-fs-reach` and
23
+ * `test/integration/archive-extract` pin every one of these; none is assumed. (No archive
24
+ * library WRITES for VAT: an extraction's writes are the archive module's own `fs` calls.)
25
+ * 4. An fd to path map, filled from intercepted `open*`, gives handle and stream
26
+ * operations the path they act on.
27
+ * 5. `realpathSync.native` and `realpath.native` are properties of the functions, not
28
+ * of the module, so they are wrapped on the wrappers, traced as the op `realpath`.
29
+ *
30
+ * ## What it cannot reach
31
+ *
32
+ * ⚠️ `cpSync`, and on Node >= 23 recursive `rmSync`, are implemented natively: the
33
+ * harness can fail the CALL (family `write` / `remove`) but never a file inside it.
34
+ * A verb that wants per-file fault coverage must not use those calls.
35
+ *
36
+ * ⚠️ Recursive `fs.rm` / `fs.promises.rm` keep the `fs` functions they found the first time Node
37
+ * removed a tree in this process. If that was outside a session — on Node 22 any earlier
38
+ * recursive `rmSync`, which is what a test's cleanup does; on later Nodes an earlier `fs.rm` — no
39
+ * session sees or fails a call INSIDE the removal, only the `rm` call itself.
40
+ *
41
+ * ⚠️ A `FileHandle` is only traced when it was opened AFTER `installFaultFs`:
42
+ * the fd to path map cannot know the path of a handle that was already open, and
43
+ * the prototype is patched when the first traced handle is opened. Install first,
44
+ * then open.
45
+ *
46
+ * ## Counting
47
+ *
48
+ * Calls made synchronously from inside an intercepted call (an `fs` function that
49
+ * is itself written in terms of another public one) are not traced again, so one
50
+ * caller-visible operation is one {@link FsCall}. A rule fires on its `nth` match
51
+ * only; the calls around it succeed. `FsApi` `'stream'` is reserved: stream I/O
52
+ * reaches the harness as `callback` calls on the fs object, which is what it is —
53
+ * so one stream write is three calls (`open`, `write`, `close`), each counted.
54
+ *
55
+ * ⛔ Framework-free, like everything under `testing/`: no `vitest` import, so the
56
+ * `./testing` subpath keeps the empty third-party set its purity pin asserts.
57
+ */
58
+ import fs from 'node:fs';
59
+ import { syncBuiltinESMExports } from 'node:module';
60
+ import { fileURLToPath } from 'node:url';
61
+ import { relativeEscapesRoot, safePath } from '../path-core.js';
62
+ import { injectedErrnoError, OPS } from './fault-spec.js';
63
+ /** How long {@link FaultFsSession.settled} waits for in-flight calls before it calls the run hung. */
64
+ const SETTLE_LIMIT_MS = 10_000;
65
+ /** `FileHandle.prototype` method name to the op it is traced as. */
66
+ const HANDLE_OPS = {
67
+ read: 'read', readFile: 'readFile', write: 'write', writev: 'writev', writeFile: 'writeFile',
68
+ appendFile: 'appendFile', stat: 'fstat', chmod: 'fchmod', truncate: 'ftruncate', sync: 'fsync',
69
+ };
70
+ const REWRITABLE_OPS = new Set(['stat', 'lstat', 'fstat']);
71
+ let active;
72
+ /** Non-zero while an intercepted call is running its original body synchronously, or while {@link untracedFs} runs. */
73
+ let depth = 0;
74
+ /**
75
+ * Run `work` — synchronous fs calls a TEST makes on its own behalf while a session is active (a mock
76
+ * standing in for a child process, capturing what it was handed) — outside the session: none of its
77
+ * calls is traced, so none becomes an injection point, and none is failed. Without it a harness's own
78
+ * bookkeeping is indistinguishable from the code under test.
79
+ *
80
+ * ⛔ Synchronous only: an asynchronous call started inside returns to the event loop with the session
81
+ * active again, and its continuation is traced.
82
+ */
83
+ export function untracedFs(work) {
84
+ depth += 1;
85
+ try {
86
+ return work();
87
+ }
88
+ finally {
89
+ depth -= 1;
90
+ }
91
+ }
92
+ const isUnder = (root, path) => !relativeEscapesRoot(safePath.relative(root, path));
93
+ function toPath(value) {
94
+ if (typeof value === 'string')
95
+ return safePath.resolve(value);
96
+ if (value instanceof URL)
97
+ return safePath.resolve(fileURLToPath(value));
98
+ if (Buffer.isBuffer(value))
99
+ return safePath.resolve(value.toString());
100
+ return undefined;
101
+ }
102
+ function openFamily(flags) {
103
+ if (flags === undefined)
104
+ return 'read';
105
+ if (typeof flags === 'number') {
106
+ const writing = fs.constants.O_WRONLY | fs.constants.O_RDWR | fs.constants.O_CREAT | fs.constants.O_TRUNC | fs.constants.O_APPEND;
107
+ return (flags & writing) === 0 ? 'read' : 'write';
108
+ }
109
+ return typeof flags === 'string' && /^[rs]+$/.test(flags) ? 'read' : 'write';
110
+ }
111
+ /** The fd a call acts on: the handle's own for a handle method, else the first argument. */
112
+ function fdOf(style, self, args) {
113
+ const value = style === 'handle' ? self?.fd : args[0];
114
+ return typeof value === 'number' ? value : undefined;
115
+ }
116
+ function locate(spec, style, self, args, session) {
117
+ // A handle method acts on its own fd whatever its module twin takes (`readFile` takes a path there).
118
+ if (spec.shape === 'fd' || style === 'handle') {
119
+ const fd = fdOf(style, self, args);
120
+ const path = fd === undefined ? undefined : session.fdPaths.get(fd);
121
+ return path === undefined ? undefined : { path };
122
+ }
123
+ const first = toPath(args[0]);
124
+ if (spec.shape === 'one')
125
+ return first === undefined ? undefined : { path: first };
126
+ const second = toPath(args[1]);
127
+ if (first === undefined || second === undefined)
128
+ return undefined;
129
+ return spec.shape === 'link' ? { path: second, dest: first } : { path: first, dest: second };
130
+ }
131
+ /** Record the call if it is under `within`; undefined means it is not ours to touch. */
132
+ function trace(session, op, spec, style, self, args) {
133
+ const located = locate(spec, style, self, args, session);
134
+ if (located === undefined)
135
+ return undefined;
136
+ const { path, dest } = located;
137
+ if (!isUnder(session.within, path) && !(dest !== undefined && isUnder(session.within, dest)))
138
+ return undefined;
139
+ const flags = typeof args[1] === 'function' ? undefined : args[1];
140
+ const family = spec.family === 'open' ? openFamily(flags) : spec.family;
141
+ const call = {
142
+ seq: session.mutableCalls.length + 1, op, family, api: style, path, ...(dest === undefined ? {} : { dest }),
143
+ };
144
+ session.mutableCalls.push(call);
145
+ return call;
146
+ }
147
+ /** Whether `call` is `earlier` asked again: the same operation, through the same API, on the same paths. */
148
+ function isRepeatOf(earlier, call) {
149
+ return earlier.op === call.op && earlier.api === call.api && earlier.path === call.path && earlier.dest === call.dest;
150
+ }
151
+ function faultFor(session, call) {
152
+ // A retry of the call an `everyTry` rule just failed fails again, and is no new match of any rule.
153
+ const { last } = session.retried;
154
+ if (last !== undefined && isRepeatOf(last.call, call))
155
+ return last.rule.errno;
156
+ delete session.retried.last;
157
+ for (const rule of session.faults) {
158
+ const matches = (rule.family === undefined || rule.family === call.family)
159
+ && (rule.op === undefined || rule.op === call.op)
160
+ && (rule.path(call.path) || (call.dest !== undefined && rule.path(call.dest)));
161
+ if (!matches)
162
+ continue;
163
+ const seen = (session.hits.get(rule) ?? 0) + 1;
164
+ session.hits.set(rule, seen);
165
+ if (seen !== (rule.nth ?? 1))
166
+ continue;
167
+ if (rule.everyTry === true)
168
+ session.retried.last = { rule, call };
169
+ return rule.errno;
170
+ }
171
+ return undefined;
172
+ }
173
+ function fail(style, args, error) {
174
+ if (style === 'promise' || style === 'handle')
175
+ return Promise.reject(error);
176
+ const callback = [...args].reverse().find((arg) => typeof arg === 'function');
177
+ if (style === 'callback' && callback !== undefined) {
178
+ process.nextTick(callback, error);
179
+ return undefined;
180
+ }
181
+ throw error;
182
+ }
183
+ /**
184
+ * An injected `close` fault, as the OS reports one: close(2) releases the descriptor even when
185
+ * it returns an error, so the real close runs and only then is the fault reported. A close the
186
+ * injection skipped would leave the descriptor open for the rest of the process, and Node closes
187
+ * an abandoned `FileHandle` during garbage collection as an uncaught error. A real close that
188
+ * itself fails is reported as itself: the injection only ever adds a fault, never hides one.
189
+ */
190
+ function failAfterClosing(style, close, args, error) {
191
+ depth += 1;
192
+ try {
193
+ if (style === 'promise' || style === 'handle') {
194
+ return close(...args).then(() => {
195
+ throw error;
196
+ });
197
+ }
198
+ const index = args.findLastIndex((arg) => typeof arg === 'function');
199
+ if (style === 'callback' && index >= 0) {
200
+ const callback = args[index];
201
+ // A real close that itself fails is reported as itself, as the other styles do.
202
+ close(...args.slice(0, index), (closeError) => callback(closeError ?? error));
203
+ return undefined;
204
+ }
205
+ // A real close that itself fails is a real fault, and propagates as one.
206
+ close(...args);
207
+ throw error;
208
+ }
209
+ finally {
210
+ depth -= 1;
211
+ }
212
+ }
213
+ function patch(session, target, key, make) {
214
+ const original = target[key];
215
+ if (typeof original !== 'function')
216
+ return;
217
+ const wrapped = make(original);
218
+ // Carry `realpath.native`, util.promisify.custom and the like over to the wrapper.
219
+ for (const own of Reflect.ownKeys(original)) {
220
+ if (own === 'length' || own === 'name' || own === 'prototype')
221
+ continue;
222
+ Object.defineProperty(wrapped, own, Object.getOwnPropertyDescriptor(original, own));
223
+ }
224
+ session.patches.push({ target, key, original });
225
+ target[key] = wrapped;
226
+ }
227
+ function rewriteStats(session, op, path, value) {
228
+ let stats = value;
229
+ for (const rule of session.rewrites) {
230
+ if (rule.op === op && rule.path(path))
231
+ stats = rule.rewrite(stats);
232
+ }
233
+ return stats;
234
+ }
235
+ /** What to do with a successful result: map an opened fd, patch the first handle, rewrite stats. */
236
+ function adaptResult(session, op, call, value) {
237
+ if (typeof value !== 'object' || value === null) {
238
+ if (op === 'open' && typeof value === 'number')
239
+ session.fdPaths.set(value, call.path);
240
+ return value;
241
+ }
242
+ if (op === 'open') {
243
+ const fd = value.fd;
244
+ if (typeof fd === 'number')
245
+ session.fdPaths.set(fd, call.path);
246
+ patchFileHandle(session, value);
247
+ return value;
248
+ }
249
+ return REWRITABLE_OPS.has(op) ? rewriteStats(session, op, call.path, value) : value;
250
+ }
251
+ /**
252
+ * Route a callback API's success value through `settle`, and count the call in flight until its
253
+ * callback runs; other arguments pass untouched.
254
+ */
255
+ function settleInCallback(session, args, settle) {
256
+ const index = args.findLastIndex((arg) => typeof arg === 'function');
257
+ const callback = args[index];
258
+ if (callback === undefined)
259
+ return [...args];
260
+ const out = [...args];
261
+ session.inFlight += 1;
262
+ out[index] = (error, value, ...rest) => {
263
+ session.inFlight -= 1;
264
+ return callback(error, error ? value : settle(value), ...rest);
265
+ };
266
+ return out;
267
+ }
268
+ /** Count a promise-API call in flight until it settles, whichever way. */
269
+ function countedPromise(session, result, settle) {
270
+ session.inFlight += 1;
271
+ return result.finally(() => {
272
+ session.inFlight -= 1;
273
+ }).then(settle);
274
+ }
275
+ /**
276
+ * The wrapper acts for whichever session is active WHEN IT IS CALLED, not the one that made it: code
277
+ * that captured an `fs` function while a session was active (Node's own `fs.promises.rm` does, on its
278
+ * first call) keeps calling this wrapper after `restore()`, and must be traced by every later session.
279
+ */
280
+ function makeWrapper(op, spec, style, original) {
281
+ return function wrapped(...args) {
282
+ const session = active;
283
+ if (session === undefined || depth > 0)
284
+ return original.apply(this, args);
285
+ const call = trace(session, op, spec, style, this, args);
286
+ if (call === undefined)
287
+ return original.apply(this, args);
288
+ const errno = faultFor(session, call);
289
+ if (errno !== undefined) {
290
+ session.mutableFired.push(call);
291
+ const error = injectedErrnoError(errno, op, call.path);
292
+ return op === 'close' ? failAfterClosing(style, original.bind(this), args, error) : fail(style, args, error);
293
+ }
294
+ // A close forgets its fd whatever the API; the fd is only known before the call.
295
+ const closing = op === 'close' ? fdOf(style, this, args) : undefined;
296
+ const settle = (value) => {
297
+ if (closing !== undefined)
298
+ session.fdPaths.delete(closing);
299
+ return adaptResult(session, op, call, value);
300
+ };
301
+ const callArgs = style === 'callback' ? settleInCallback(session, args, settle) : args;
302
+ depth += 1;
303
+ let result;
304
+ try {
305
+ result = original.apply(this, callArgs);
306
+ }
307
+ finally {
308
+ depth -= 1;
309
+ }
310
+ if (style === 'sync')
311
+ return settle(result);
312
+ if (style === 'promise' || style === 'handle')
313
+ return countedPromise(session, result, settle);
314
+ return result;
315
+ };
316
+ }
317
+ const nextTurn = () => new Promise((resolve) => {
318
+ setImmediate(resolve);
319
+ });
320
+ /** Turn the event loop until nothing the session traced is in flight: a completed call's continuation may start the next one. */
321
+ async function settledOf(session, deadline = Date.now() + SETTLE_LIMIT_MS) {
322
+ await nextTurn();
323
+ if (session.inFlight === 0)
324
+ return;
325
+ if (Date.now() > deadline)
326
+ throw new Error(`fault-fs: ${session.inFlight} traced fs call(s) still in flight after ${SETTLE_LIMIT_MS} ms`);
327
+ return settledOf(session, deadline);
328
+ }
329
+ /**
330
+ * Patch `FileHandle.prototype` from the first traced handle, and every traced handle's own
331
+ * `close` (Node defines it per handle, as a class field); `restore()` puts all of them back.
332
+ */
333
+ function patchFileHandle(session, handle) {
334
+ const close = OPS['close'];
335
+ if (close !== undefined)
336
+ patch(session, handle, 'close', (original) => makeWrapper('close', close, 'handle', original));
337
+ if (session.handlePatched)
338
+ return;
339
+ session.handlePatched = true;
340
+ const prototype = Object.getPrototypeOf(handle);
341
+ for (const [method, op] of Object.entries(HANDLE_OPS)) {
342
+ const spec = OPS[op];
343
+ if (spec !== undefined)
344
+ patch(session, prototype, method, (original) => makeWrapper(op, spec, 'handle', original));
345
+ }
346
+ }
347
+ /**
348
+ * Wrap `realpathSync.native` and `realpath.native` as the op `realpath`. They hang off the
349
+ * functions as properties, so {@link patch} copies them onto the wrappers UNWRAPPED, and
350
+ * `normalizePath` asks the native one first: without this every product realpath fault was
351
+ * out of the harness's reach. The patch lands on the wrapper, which `restore()` drops along
352
+ * with its property; the originals never change.
353
+ */
354
+ function patchNativeRealpath(session) {
355
+ const spec = OPS['realpath'];
356
+ if (spec === undefined)
357
+ return;
358
+ patch(session, fs.realpathSync, 'native', (original) => makeWrapper('realpath', spec, 'sync', original));
359
+ patch(session, fs.realpath, 'native', (original) => makeWrapper('realpath', spec, 'callback', original));
360
+ }
361
+ /**
362
+ * Trace and fault fs calls on paths under `within` until `restore()` is called.
363
+ *
364
+ * One session at a time: a second install while one is active throws, because two
365
+ * stacked sets of wrappers cannot be unwound independently.
366
+ */
367
+ export function installFaultFs(options) {
368
+ if (active !== undefined)
369
+ throw new Error('installFaultFs: a fault session is already active; restore() it first');
370
+ const mutableCalls = [];
371
+ const mutableFired = [];
372
+ const session = {
373
+ within: safePath.resolve(options.within),
374
+ faults: options.faults ?? [],
375
+ rewrites: options.rewrites ?? [],
376
+ hits: new Map(),
377
+ retried: {},
378
+ fdPaths: new Map(),
379
+ patches: [],
380
+ mutableCalls,
381
+ mutableFired,
382
+ handlePatched: false,
383
+ restored: false,
384
+ inFlight: 0,
385
+ calls: mutableCalls,
386
+ fired: mutableFired,
387
+ restore() {
388
+ if (session.restored)
389
+ return;
390
+ session.restored = true;
391
+ for (const { target, key, original } of session.patches.toReversed()) {
392
+ target[key] = original;
393
+ }
394
+ session.fdPaths.clear();
395
+ if (active === session)
396
+ active = undefined;
397
+ syncBuiltinESMExports();
398
+ },
399
+ settled: () => settledOf(session),
400
+ };
401
+ active = session;
402
+ for (const [op, spec] of Object.entries(OPS)) {
403
+ patch(session, fs, `${op}Sync`, (original) => makeWrapper(op, spec, 'sync', original));
404
+ patch(session, fs, op, (original) => makeWrapper(op, spec, 'callback', original));
405
+ patch(session, fs.promises, op, (original) => makeWrapper(op, spec, 'promise', original));
406
+ }
407
+ patchNativeRealpath(session);
408
+ syncBuiltinESMExports();
409
+ return session;
410
+ }
411
+ //# sourceMappingURL=fault-fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fault-fs.js","sourceRoot":"","sources":["../../src/testing/fault-fs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * The fault harness's vocabulary, with no filesystem call in it: the errnos it raises, the
3
+ * operations it traces and their families, a rule, the error a rule raises, and the JSON spec a
4
+ * spawned process is handed (`VAT_FAULT_FS`). `fault-fs.ts` is the half that patches `node:fs`.
5
+ *
6
+ * ⛔ Framework-free, like everything under `testing/`.
7
+ */
8
+ /**
9
+ * Every errno the injector raises (`EDQUOT` too on a host whose `os.constants.errno` lacks it: it gets
10
+ * `-1`). At least one of every class of the errno table: `EEXIST` / `ENOTEMPTY` (`occupied`) are what a
11
+ * concurrent writer or a name collision produces, `EIO` (`device`) what a failing disk does, `UNKNOWN`
12
+ * (`device` too) what Windows reports for a network share that is not there AND for credentials the
13
+ * share refused (libuv `src/win/error.c:172`; `-1` too). `ENETUNREACH`, `EHOSTUNREACH`, `ECONNRESET`
14
+ * and `ENOTCONN` are what a file call on a share raises when the network fails (`device`);
15
+ * `EINVAL` and `ENAMETOOLONG` are Windows' ambiguous answers for a link (:102-105, :129). `EWHATEVER`
16
+ * is no errno at all: one no table lists, which must still be classified, never handed back raw.
17
+ */
18
+ export declare const INJECTED_ERRNOS: readonly ["EACCES", "EPERM", "ENOSPC", "EDQUOT", "EROFS", "EMFILE", "ENFILE", "ENOENT", "EISDIR", "ENOTDIR", "EBUSY", "EXDEV", "EEXIST", "ENOTEMPTY", "EIO", "UNKNOWN", "ENETUNREACH", "EHOSTUNREACH", "ECONNRESET", "ENOTCONN", "EINVAL", "ENAMETOOLONG", "EWHATEVER"];
19
+ export type InjectedErrno = (typeof INJECTED_ERRNOS)[number];
20
+ declare const FS_OP_FAMILIES: readonly ["read", "write", "meta", "list", "remove", "rename", "create"];
21
+ export type FsOpFamily = (typeof FS_OP_FAMILIES)[number];
22
+ export type FsApi = 'sync' | 'callback' | 'promise' | 'handle' | 'stream';
23
+ export interface FaultRule {
24
+ readonly family?: FsOpFamily;
25
+ readonly op?: string;
26
+ readonly path: (path: string) => boolean;
27
+ /** 1-based among calls matching family / op / path. Default 1. */
28
+ readonly nth?: number;
29
+ readonly errno: InjectedErrno;
30
+ /**
31
+ * Also fail every IMMEDIATE repeat of the call the rule failed — the same operation on the same
32
+ * paths, no other traced call between: what a retry is. The repeats are not matches of their own,
33
+ * so `nth` numbers the same calls whoever retries.
34
+ *
35
+ * Without it a rule fails exactly one call, and a caller that retries turns that into a success:
36
+ * under win32 every tree-change rename is retried on `EACCES`, `EPERM` and `EBUSY`. A test that
37
+ * means "this rename is refused" sets it, and then asks the same thing on every platform.
38
+ */
39
+ readonly everyTry?: boolean;
40
+ }
41
+ /**
42
+ * Build the error Node would have thrown for `errno`.
43
+ *
44
+ * `errno` carries libuv's negative number (`-28` for `ENOSPC`) from `os.constants`;
45
+ * a code the host lacks (`EDQUOT` on win32) gets `-1`.
46
+ */
47
+ export declare function injectedErrnoError(errno: InjectedErrno, syscall: string, path: string): NodeJS.ErrnoException;
48
+ export type PathShape = 'fd' | 'one' | 'two' | 'link';
49
+ export interface OpSpec {
50
+ /** `'open'` is decided by the flags: a read-only open is `read`, anything else `write`. */
51
+ readonly family: FsOpFamily | 'open';
52
+ /** Where the path is: an fd, the first argument, the first two, or `symlink(target, path)`. */
53
+ readonly shape: PathShape;
54
+ }
55
+ /** Every operation the harness traces and can fail; the three wrappers are generated from it. */
56
+ export declare const OPS: Readonly<Record<string, OpSpec>>;
57
+ /**
58
+ * A {@link FaultRule} that can cross a process boundary as JSON: the path is matched by a
59
+ * substring of its forward-slash form, since a predicate cannot be serialised.
60
+ */
61
+ export interface FaultSpec {
62
+ readonly family?: FsOpFamily;
63
+ readonly op?: string;
64
+ readonly pathIncludes: string;
65
+ readonly nth?: number;
66
+ readonly errno: InjectedErrno;
67
+ }
68
+ /** What `fault-fs-preload` reads from `VAT_FAULT_FS`: the session's root and its faults. */
69
+ export interface FaultFsSpec {
70
+ readonly within: string;
71
+ readonly faults: readonly FaultSpec[];
72
+ }
73
+ /** The rule a {@link FaultSpec} stands for; what the spec leaves out, the injector defaults. */
74
+ export declare function faultRuleOf(spec: FaultSpec): FaultRule;
75
+ /**
76
+ * Parse the `VAT_FAULT_FS` JSON a spawned process is handed.
77
+ *
78
+ * @throws Error naming the variable when the text is not JSON, has no `within`, or holds a fault
79
+ * the injector cannot raise as written: a malformed spec must not run the process unfaulted
80
+ */
81
+ export declare function faultFsSpecOf(text: string): FaultFsSpec;
82
+ export {};
83
+ //# sourceMappingURL=fault-spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fault-spec.d.ts","sourceRoot":"","sources":["../../src/testing/fault-spec.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,yQAGlB,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,QAAA,MAAM,cAAc,0EAA2E,CAAC;AAChG,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AACzD,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE1E,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACzC,kEAAkE;IAClE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,cAAc,CAQ7G;AAED,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AACtD,MAAM,WAAW,MAAM;IACrB,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAAC;IACrC,+FAA+F;IAC/F,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;CAC3B;AAED,iGAAiG;AACjG,eAAO,MAAM,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAkChD,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;CAC/B;AAED,4FAA4F;AAC5F,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;CACvC;AAED,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAQtD;AA2BD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAavD"}