@openclaw/fs-safe 0.8.6 → 0.9.0

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 (101) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/archive-durability.d.ts +24 -0
  3. package/dist/archive-durability.d.ts.map +1 -0
  4. package/dist/archive-durability.js +180 -0
  5. package/dist/archive-gzip-tail.d.ts.map +1 -1
  6. package/dist/archive-gzip-tail.js +2 -1
  7. package/dist/archive-input.js +4 -4
  8. package/dist/archive-kind.d.ts.map +1 -1
  9. package/dist/archive-kind.js +3 -2
  10. package/dist/archive-merge.d.ts +1 -0
  11. package/dist/archive-merge.d.ts.map +1 -1
  12. package/dist/archive-merge.js +41 -8
  13. package/dist/archive-native.d.ts +1 -0
  14. package/dist/archive-native.d.ts.map +1 -1
  15. package/dist/archive-native.js +1 -0
  16. package/dist/archive-options.d.ts +2 -0
  17. package/dist/archive-options.d.ts.map +1 -1
  18. package/dist/archive-parser.wasm +0 -0
  19. package/dist/archive-read.d.ts.map +1 -1
  20. package/dist/archive-read.js +5 -4
  21. package/dist/archive-staging.d.ts +1 -1
  22. package/dist/archive-staging.d.ts.map +1 -1
  23. package/dist/archive-staging.js +24 -19
  24. package/dist/archive.d.ts.map +1 -1
  25. package/dist/archive.js +9 -4
  26. package/dist/copy-publication.d.ts +6 -0
  27. package/dist/copy-publication.d.ts.map +1 -0
  28. package/dist/copy-publication.js +3 -0
  29. package/dist/directory-mode-node.d.ts.map +1 -1
  30. package/dist/directory-mode-node.js +4 -4
  31. package/dist/file-hash.d.ts.map +1 -1
  32. package/dist/file-hash.js +5 -4
  33. package/dist/file-store-sync-write.d.ts +1 -0
  34. package/dist/file-store-sync-write.d.ts.map +1 -1
  35. package/dist/file-store-sync-write.js +9 -6
  36. package/dist/file-store.d.ts +4 -0
  37. package/dist/file-store.d.ts.map +1 -1
  38. package/dist/file-store.js +10 -1
  39. package/dist/fs.d.ts.map +1 -1
  40. package/dist/fs.js +1 -2
  41. package/dist/guarded-mutation.js +1 -1
  42. package/dist/install-path.js +7 -7
  43. package/dist/json-document-store.d.ts +3 -0
  44. package/dist/json-document-store.d.ts.map +1 -1
  45. package/dist/json-document-store.js +1 -0
  46. package/dist/json-durable-queue-directory.js +3 -3
  47. package/dist/json-durable-queue-ownership.js +3 -2
  48. package/dist/json-durable-queue.d.ts.map +1 -1
  49. package/dist/json-durable-queue.js +18 -13
  50. package/dist/move-path-cleanup.d.ts.map +1 -1
  51. package/dist/move-path-cleanup.js +4 -3
  52. package/dist/move-path.d.ts.map +1 -1
  53. package/dist/move-path.js +31 -18
  54. package/dist/opened-file-failure.d.ts +1 -1
  55. package/dist/opened-file-failure.d.ts.map +1 -1
  56. package/dist/opened-file-failure.js +3 -2
  57. package/dist/permissions.js +3 -3
  58. package/dist/private-temp-workspace.d.ts.map +1 -1
  59. package/dist/private-temp-workspace.js +9 -3
  60. package/dist/publish-file.js +21 -21
  61. package/dist/replace-file-copy-fallback.d.ts.map +1 -1
  62. package/dist/replace-file-copy-fallback.js +30 -17
  63. package/dist/replace-file-copy-source.d.ts.map +1 -1
  64. package/dist/replace-file-copy-source.js +10 -5
  65. package/dist/replace-file.d.ts.map +1 -1
  66. package/dist/replace-file.js +8 -3
  67. package/dist/root-impl.d.ts +1 -1
  68. package/dist/root-impl.d.ts.map +1 -1
  69. package/dist/root-impl.js +16 -2
  70. package/dist/secret-file.d.ts +1 -0
  71. package/dist/secret-file.d.ts.map +1 -1
  72. package/dist/secret-file.js +1 -0
  73. package/dist/secret-read-async.js +5 -5
  74. package/dist/secure-file.d.ts +1 -1
  75. package/dist/secure-file.d.ts.map +1 -1
  76. package/dist/secure-file.js +19 -9
  77. package/dist/sibling-staged-file.js +7 -7
  78. package/dist/sibling-temp.d.ts.map +1 -1
  79. package/dist/sibling-temp.js +16 -7
  80. package/dist/sidecar-lock-acquire.d.ts +1 -1
  81. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  82. package/dist/sidecar-lock-acquire.js +4 -3
  83. package/dist/sidecar-lock-policy.js +2 -2
  84. package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
  85. package/dist/sidecar-lock-reclaim.js +16 -4
  86. package/dist/sidecar-lock.d.ts.map +1 -1
  87. package/dist/sidecar-lock.js +2 -1
  88. package/dist/temp-target.d.ts.map +1 -1
  89. package/dist/temp-target.js +9 -4
  90. package/dist/walk.js +2 -2
  91. package/dist/write-open-flags.d.ts.map +1 -1
  92. package/dist/write-open-flags.js +1 -2
  93. package/docs/archive.md +35 -2
  94. package/docs/file-store.md +48 -3
  95. package/docs/json-store.md +10 -0
  96. package/docs/reading.md +6 -0
  97. package/docs/root.md +2 -2
  98. package/docs/secret-file.md +6 -0
  99. package/docs/sidecar-lock.md +2 -0
  100. package/docs/writing.md +5 -3
  101. package/package.json +11 -11
@@ -18,7 +18,7 @@ export async function unlinkBestEffort(filePath) {
18
18
  }
19
19
  export async function jsonDurableQueueEntryExists(filePath) {
20
20
  try {
21
- const stat = await fs.promises.lstat(filePath);
21
+ const stat = fs.lstatSync(filePath);
22
22
  return stat.isFile();
23
23
  }
24
24
  catch (error) {
@@ -30,7 +30,7 @@ export async function jsonDurableQueueEntryExists(filePath) {
30
30
  }
31
31
  async function unlinkStaleTmpBestEffort(filePath, now, maxAgeMs) {
32
32
  try {
33
- const stat = await fs.promises.stat(filePath);
33
+ const stat = fs.statSync(filePath);
34
34
  if (stat.isFile() && now - stat.mtimeMs >= maxAgeMs) {
35
35
  await unlinkBestEffort(filePath);
36
36
  }
@@ -113,22 +113,27 @@ async function isDarwinSystemAlias(dir, stat) {
113
113
  if (resolved !== "/tmp" && resolved !== "/var") {
114
114
  return false;
115
115
  }
116
- return await fs.promises.realpath(resolved).then((realPath) => realPath === `/private${resolved}`, () => false);
116
+ try {
117
+ return fs.realpathSync.native(resolved) === `/private${resolved}`;
118
+ }
119
+ catch {
120
+ return false;
121
+ }
117
122
  }
118
123
  async function assertNoSymlinkDirectorySegments(validationRoot, dir, allowMissing) {
119
124
  let base = path.resolve(validationRoot.path);
120
125
  let target = path.resolve(dir);
121
126
  let current = base;
122
- let baseStat = await fs.promises.lstat(base);
127
+ let baseStat = fs.lstatSync(base);
123
128
  if (baseStat.isSymbolicLink() && validationRoot.allowSymlinkBase) {
124
129
  const relative = path.relative(base, target);
125
130
  if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
126
131
  throw new Error(`durable queue path is not a directory: ${dir}`);
127
132
  }
128
- base = await fs.promises.realpath(base);
133
+ base = fs.realpathSync.native(base);
129
134
  target = path.join(base, ...relative.split(path.sep).filter(Boolean));
130
135
  current = base;
131
- baseStat = await fs.promises.lstat(base);
136
+ baseStat = fs.lstatSync(base);
132
137
  }
133
138
  if (baseStat.isSymbolicLink() || !baseStat.isDirectory()) {
134
139
  throw new Error(`durable queue path is not a directory: ${dir}`);
@@ -142,7 +147,7 @@ async function assertNoSymlinkDirectorySegments(validationRoot, dir, allowMissin
142
147
  current = path.join(current, segment);
143
148
  let stat;
144
149
  try {
145
- stat = await fs.promises.lstat(current);
150
+ stat = fs.lstatSync(current);
146
151
  }
147
152
  catch (error) {
148
153
  if (allowMissing && error.code === "ENOENT") {
@@ -153,7 +158,7 @@ async function assertNoSymlinkDirectorySegments(validationRoot, dir, allowMissin
153
158
  if (stat.isSymbolicLink() || !stat.isDirectory()) {
154
159
  if (stat.isSymbolicLink() && validationRoot.allowSymlinkBase) {
155
160
  if (await isDarwinSystemAlias(current, stat)) {
156
- current = await fs.promises.realpath(current);
161
+ current = fs.realpathSync.native(current);
157
162
  continue;
158
163
  }
159
164
  }
@@ -172,7 +177,7 @@ async function chmodQueueDirectory(dir) {
172
177
  let handle;
173
178
  try {
174
179
  handle = await fs.promises.open(dir, fs.constants.O_RDONLY | noFollow | directoryFlag);
175
- const stat = await handle.stat();
180
+ const stat = fs.fstatSync(handle.fd);
176
181
  if (!stat.isDirectory()) {
177
182
  throw new Error(`durable queue path is not a directory: ${dir}`);
178
183
  }
@@ -193,7 +198,7 @@ async function chmodQueueDirectory(dir) {
193
198
  }
194
199
  }
195
200
  }
196
- const stat = await fs.promises.lstat(dir);
201
+ const stat = fs.lstatSync(dir);
197
202
  if (stat.isSymbolicLink() || !stat.isDirectory()) {
198
203
  throw new Error(`durable queue path is not a directory: ${dir}`);
199
204
  }
@@ -218,7 +223,7 @@ async function inspectQueueEntry(inspect, maxBytes, expected) {
218
223
  try {
219
224
  return await inspectFileIdentity(async () => {
220
225
  try {
221
- const stat = await inspect();
226
+ const stat = inspect();
222
227
  if (stat.isSymbolicLink() || !stat.isFile()) {
223
228
  throw new Error("queue entry is not a regular file");
224
229
  }
@@ -242,11 +247,11 @@ async function inspectQueueEntry(inspect, maxBytes, expected) {
242
247
  }
243
248
  }
244
249
  async function readBoundedUtf8File(params) {
245
- const inspectPath = () => fs.promises.lstat(params.filePath, { bigint: true });
250
+ const inspectPath = () => fs.lstatSync(params.filePath, { bigint: true });
246
251
  const initialStat = await inspectQueueEntry(inspectPath, params.maxBytes);
247
252
  const handle = await fs.promises.open(params.filePath, resolveReadOpenFlags());
248
253
  try {
249
- const openedStat = await inspectQueueEntry(() => handle.stat({ bigint: true }), params.maxBytes, initialStat);
254
+ const openedStat = await inspectQueueEntry(() => fs.fstatSync(handle.fd, { bigint: true }), params.maxBytes, initialStat);
250
255
  await inspectQueueEntry(inspectPath, params.maxBytes, openedStat);
251
256
  const chunks = [];
252
257
  const scratch = Buffer.allocUnsafe(Math.min(64 * 1024, params.maxBytes + 1));
@@ -1 +1 @@
1
- {"version":3,"file":"move-path-cleanup.d.ts","sourceRoot":"","sources":["../src/move-path-cleanup.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAC3B,CAAC,aAAa,GAAG;IACf,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,mBAAmB,CAAA;KAAE,CAAC,CAAC;IACjE,IAAI,EAAE,WAAW,CAAC;CACnB,CAAC,GACF,CAAC,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEvC,KAAK,wBAAwB,GAAG,SAAS,GAAG,OAAO,CAAC;AAEpD,KAAK,iBAAiB,GAAG;IACvB,QAAQ,EAAE,aAAa,CAAC;IACxB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;CAC7C,CAAC;AAEF,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,aAAa,CAUhB;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,GAAG,OAAO,CAUxE;AAMD,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,CAI5D;AAED,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,aAAa,GACtB,OAAO,CAAC,IAAI,CAAC,CAIf;AA0BD,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,mBAAmB,GAC5B,uBAAuB,CAuBzB;AAsDD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,uBAAuB,EAC9B,oBAAoB,EAAE,MAAM,IAAI,GAC/B,OAAO,CAAC,wBAAwB,CAAC,CAyDnC"}
1
+ {"version":3,"file":"move-path-cleanup.d.ts","sourceRoot":"","sources":["../src/move-path-cleanup.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAC3B,CAAC,aAAa,GAAG;IACf,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,mBAAmB,CAAA;KAAE,CAAC,CAAC;IACjE,IAAI,EAAE,WAAW,CAAC;CACnB,CAAC,GACF,CAAC,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEvC,KAAK,wBAAwB,GAAG,SAAS,GAAG,OAAO,CAAC;AAEpD,KAAK,iBAAiB,GAAG;IACvB,QAAQ,EAAE,aAAa,CAAC;IACxB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;CAC7C,CAAC;AAEF,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,aAAa,CAUhB;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,GAAG,OAAO,CAUxE;AAMD,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,CAI5D;AAED,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,aAAa,GACtB,OAAO,CAAC,IAAI,CAAC,CAIf;AA0BD,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,mBAAmB,GAC5B,uBAAuB,CAuBzB;AAsDD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,uBAAuB,EAC9B,oBAAoB,EAAE,MAAM,IAAI,GAC/B,OAAO,CAAC,wBAAwB,CAAC,CAyDnC"}
@@ -1,3 +1,4 @@
1
+ import fsSync from "node:fs";
1
2
  import fs from "node:fs/promises";
2
3
  import path from "node:path";
3
4
  export function entryIdentity(stat) {
@@ -29,7 +30,7 @@ export function sourceChangedError(sourcePath) {
29
30
  });
30
31
  }
31
32
  export async function assertSourceStillMatches(sourcePath, identity) {
32
- if (!sameIdentity(identity, entryIdentity(await fs.lstat(sourcePath)))) {
33
+ if (!sameIdentity(identity, entryIdentity(fsSync.lstatSync(sourcePath)))) {
33
34
  throw sourceChangedError(sourcePath);
34
35
  }
35
36
  }
@@ -93,7 +94,7 @@ async function observeOwnedAliasUnlink(sourcePath, group) {
93
94
  }
94
95
  let observed;
95
96
  try {
96
- observed = await fs.lstat(remainingPath);
97
+ observed = fsSync.lstatSync(remainingPath);
97
98
  }
98
99
  catch (error) {
99
100
  if (error?.code === "ENOENT") {
@@ -118,7 +119,7 @@ export async function cleanupCopiedEntry(sourcePath, manifest, state, assertBefo
118
119
  }
119
120
  let currentStat;
120
121
  try {
121
- currentStat = await fs.lstat(sourcePath);
122
+ currentStat = fsSync.lstatSync(sourcePath);
122
123
  }
123
124
  catch (error) {
124
125
  if (error?.code === "ENOENT") {
@@ -1 +1 @@
1
- {"version":3,"file":"move-path.d.ts","sourceRoot":"","sources":["../src/move-path.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAAC;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb,CAAC,CAAC;AAEH,MAAM,MAAM,+BAA+B,GAAG;IAC5C,qFAAqF;IACrF,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAClC,uFAAuF;IACvF,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,qFAAqF;IACrF,sBAAsB,CAAC,EAAE,CAAC,OAAO,EAAE,0BAA0B,KAAK,IAAI,CAAC;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IACrC,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,KAAK,sBAAsB,GAAG,cAAc,GAAG,uBAAuB,CAAC;AAEvE,wBAAgB,oCAAoC,CAClD,KAAK,EAAE,OAAO,EACd,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,sBAAsB,GAAG,SAAS,CASpC;AAsPD,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,CAqHf"}
1
+ {"version":3,"file":"move-path.d.ts","sourceRoot":"","sources":["../src/move-path.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,0BAA0B,GAAG,QAAQ,CAAC;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb,CAAC,CAAC;AAEH,MAAM,MAAM,+BAA+B,GAAG;IAC5C,qFAAqF;IACrF,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAClC,uFAAuF;IACvF,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,qFAAqF;IACrF,sBAAsB,CAAC,EAAE,CAAC,OAAO,EAAE,0BAA0B,KAAK,IAAI,CAAC;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IACrC,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,KAAK,sBAAsB,GAAG,cAAc,GAAG,uBAAuB,CAAC;AAEvE,wBAAgB,oCAAoC,CAClD,KAAK,EAAE,OAAO,EACd,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,sBAAsB,GAAG,SAAS,CASpC;AAuQD,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,IAAI,CAAC,CAqHf"}
package/dist/move-path.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { constants as fsConstants } from "node:fs";
2
+ import fsSync, { constants as fsConstants } from "node:fs";
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import { FsSafeError } from "./errors.js";
@@ -29,7 +29,7 @@ async function preflightSourceHardlinks(sourcePath) {
29
29
  let discovered = 1;
30
30
  while (pending.length > 0) {
31
31
  const current = pending.pop();
32
- const stat = await fs.lstat(current);
32
+ const stat = fsSync.lstatSync(current);
33
33
  if (stat.isFile() && stat.nlink > 1) {
34
34
  throw hardlinkedSourceError(current);
35
35
  }
@@ -52,21 +52,19 @@ function isSameOrDescendant(parentPath, candidatePath) {
52
52
  (!path.isAbsolute(relative) && !relative.startsWith(`..${path.sep}`) && relative !== ".."));
53
53
  }
54
54
  async function assertCopyDestinationOutsideSource(sourcePath, targetPath, expectedIdentity) {
55
- const sourceStat = await fs.lstat(sourcePath);
55
+ const sourceStat = fsSync.lstatSync(sourcePath);
56
56
  const sourceIdentity = entryIdentity(sourceStat);
57
57
  if (expectedIdentity && !sameIdentity(expectedIdentity, sourceIdentity)) {
58
58
  throw sourceChangedError(sourcePath);
59
59
  }
60
60
  const normalizedSource = path.resolve(sourcePath);
61
61
  const normalizedTarget = path.resolve(targetPath);
62
- const [sourceParentReal, targetParentReal] = await Promise.all([
63
- fs.realpath(path.dirname(normalizedSource)),
64
- fs.realpath(path.dirname(normalizedTarget)),
65
- ]);
62
+ const sourceParentReal = fsSync.realpathSync.native(path.dirname(normalizedSource));
63
+ const targetParentReal = fsSync.realpathSync.native(path.dirname(normalizedTarget));
66
64
  const sourceCandidate = path.join(sourceParentReal, path.basename(normalizedSource));
67
65
  const targetCandidate = path.join(targetParentReal, path.basename(normalizedTarget));
68
66
  const sourceBoundary = sourceStat.isDirectory()
69
- ? await fs.realpath(sourcePath)
67
+ ? fsSync.realpathSync.native(sourcePath)
70
68
  : sourceCandidate;
71
69
  const unsafeTarget = sourceStat.isDirectory()
72
70
  ? isSameOrDescendant(sourceBoundary, targetCandidate)
@@ -103,6 +101,7 @@ async function writeAll(handle, buffer, bytesRead) {
103
101
  }
104
102
  async function copyRegularFilePinned(params) {
105
103
  let destinationCreated = false;
104
+ let openedIdentity;
106
105
  let sourceHandle;
107
106
  try {
108
107
  sourceHandle = await fs.open(params.from, resolveReadOpenFlags());
@@ -115,13 +114,24 @@ async function copyRegularFilePinned(params) {
115
114
  throw error;
116
115
  }
117
116
  try {
118
- const openedStat = await sourceHandle.stat();
117
+ const openedStat = fsSync.fstatSync(sourceHandle.fd);
118
+ openedIdentity = entryIdentity(openedStat);
119
119
  if (params.rejectHardlinks && openedStat.nlink > 1) {
120
120
  throw hardlinkedSourceError(params.from);
121
121
  }
122
- if (!openedStat.isFile() || !sameIdentity(params.identity, entryIdentity(openedStat))) {
122
+ const openAdvancedWindowsCtime = process.platform === "win32" &&
123
+ params.identity.dev === openedIdentity.dev &&
124
+ params.identity.ino === openedIdentity.ino &&
125
+ params.identity.mode === openedIdentity.mode &&
126
+ params.identity.nlink === openedIdentity.nlink &&
127
+ params.identity.size === openedIdentity.size &&
128
+ params.identity.mtimeMs === openedIdentity.mtimeMs &&
129
+ openedIdentity.ctimeMs >= params.identity.ctimeMs;
130
+ if (!openedStat.isFile() ||
131
+ (!sameIdentity(params.identity, openedIdentity) && !openAdvancedWindowsCtime)) {
123
132
  throw sourceChangedError(params.from);
124
133
  }
134
+ await assertSourceStillMatches(params.from, openedIdentity);
125
135
  const destinationHandle = await fs.open(params.to, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL, modeBits(params.mode) || 0o666);
126
136
  destinationCreated = true;
127
137
  try {
@@ -135,11 +145,11 @@ async function copyRegularFilePinned(params) {
135
145
  }
136
146
  // Re-check the opened source before the staged tree can be committed. If
137
147
  // it changed while we copied, the caller should retry the move.
138
- const finalSourceStat = await sourceHandle.stat();
148
+ const finalSourceStat = fsSync.fstatSync(sourceHandle.fd);
139
149
  if (params.rejectHardlinks && finalSourceStat.nlink > 1) {
140
150
  throw hardlinkedSourceError(params.from);
141
151
  }
142
- if (!sameIdentity(params.identity, entryIdentity(finalSourceStat))) {
152
+ if (!sameIdentity(openedIdentity, entryIdentity(finalSourceStat))) {
143
153
  throw sourceChangedError(params.from);
144
154
  }
145
155
  await destinationHandle.chmod(modeBits(params.mode));
@@ -157,15 +167,18 @@ async function copyRegularFilePinned(params) {
157
167
  finally {
158
168
  await sourceHandle.close();
159
169
  }
170
+ return openedIdentity;
160
171
  }
161
172
  async function copyEntryWithManifest(from, to, options, expectedIdentity) {
162
- const sourceStat = await fs.lstat(from);
173
+ const sourceStat = fsSync.lstatSync(from);
163
174
  const identity = entryIdentity(sourceStat);
164
175
  if (expectedIdentity && !sameIdentity(expectedIdentity, identity)) {
165
176
  throw sourceChangedError(from);
166
177
  }
167
178
  if (sourceStat.isSymbolicLink()) {
168
- await fs.symlink(await fs.readlink(from), to);
179
+ const target = await fs.readlink(from);
180
+ const targetType = process.platform === "win32" && fsSync.statSync(from).isDirectory() ? "junction" : undefined;
181
+ await fs.symlink(target, to, targetType);
169
182
  // readlink() is path-based; verify the symlink we copied is still the one
170
183
  // we inspected before letting the staged destination become visible.
171
184
  await assertSourceStillMatches(from, identity);
@@ -202,14 +215,14 @@ async function copyEntryWithManifest(from, to, options, expectedIdentity) {
202
215
  if (options.sourceHardlinks === "reject" && sourceStat.nlink > 1) {
203
216
  throw hardlinkedSourceError(from);
204
217
  }
205
- await copyRegularFilePinned({
218
+ const copiedIdentity = await copyRegularFilePinned({
206
219
  from,
207
220
  identity,
208
221
  mode: sourceStat.mode,
209
222
  rejectHardlinks: options.sourceHardlinks === "reject",
210
223
  to,
211
224
  });
212
- return { ...identity, kind: "leaf" };
225
+ return { ...copiedIdentity, kind: "leaf" };
213
226
  }
214
227
  function assertSynchronousResult(returned, name) {
215
228
  if (returned !== undefined) {
@@ -293,7 +306,7 @@ export async function movePathWithCopyFallback(options) {
293
306
  ...(rejectHardlinks ? { budget: { discovered: 1 } } : {}),
294
307
  }, sourceIdentity);
295
308
  const cleanupState = createCleanupCopiedEntryState(sourcePath, manifest);
296
- unregisterStaged.setIdentity(await fs.lstat(staged, { bigint: true }));
309
+ unregisterStaged.setIdentity(fsSync.lstatSync(staged, { bigint: true }));
297
310
  await assertCopyDestinationOutsideSource(sourcePath, targetPath, manifest);
298
311
  await guardedRename({
299
312
  from: staged,
@@ -313,7 +326,7 @@ export async function movePathWithCopyFallback(options) {
313
326
  finally {
314
327
  if (!destinationPublished) {
315
328
  try {
316
- const stagedIdentity = await fs.lstat(staged, { bigint: true });
329
+ const stagedIdentity = fsSync.lstatSync(staged, { bigint: true });
317
330
  if (!stagedIdentity.isSymbolicLink())
318
331
  unregisterStaged.setIdentity(stagedIdentity);
319
332
  }
@@ -1,4 +1,4 @@
1
- import type { BigIntStats } from "node:fs";
1
+ import { type BigIntStats } from "node:fs";
2
2
  import type { FileHandle } from "node:fs/promises";
3
3
  export declare function recordFileOpenFailure(error: unknown, filePath: string): never;
4
4
  export declare function recordExclusiveCreateFailure(error: unknown, filePath: string): never;
@@ -1 +1 @@
1
- {"version":3,"file":"opened-file-failure.d.ts","sourceRoot":"","sources":["../src/opened-file-failure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAKnD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAG7E;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAGpF;AAED,wBAAgB,yBAAyB,CACvC,KAAK,GAAE,KAA8E,GACpF,KAAK,CAGP;AAED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,WAAW,GACpB,OAAO,CAAC,IAAI,CAAC,CASf"}
1
+ {"version":3,"file":"opened-file-failure.d.ts","sourceRoot":"","sources":["../src/opened-file-failure.ts"],"names":[],"mappings":"AAAA,OAAe,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAKnD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAG7E;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAGpF;AAED,wBAAgB,yBAAyB,CACvC,KAAK,GAAE,KAA8E,GACpF,KAAK,CAGP;AAED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,WAAW,GACpB,OAAO,CAAC,IAAI,CAAC,CASf"}
@@ -1,3 +1,4 @@
1
+ import fsSync, {} from "node:fs";
1
2
  import { FsSafeError } from "./errors.js";
2
3
  import { inspectFileIdentity } from "./strict-file-identity.js";
3
4
  import { isFileObservationFailure, recordFileObservationFailure } from "./file-observation.js";
@@ -19,7 +20,7 @@ export async function recordPreOpenFileChange(error, handle, filePath, before, o
19
20
  return;
20
21
  try {
21
22
  await inspectFileIdentity(async () => before);
22
- const current = await inspectFileIdentity(() => handle.stat({ bigint: true }), opened);
23
+ const current = await inspectFileIdentity(() => fsSync.fstatSync(handle.fd, { bigint: true }), opened);
23
24
  if (current.isFile() && [0n, 1n].includes(current.nlink) &&
24
25
  (before.dev !== current.dev || before.ino !== current.ino)) {
25
26
  // A stale pathname sample is not proof that its former inode was unlinked.
@@ -35,7 +36,7 @@ export async function recordOpenedFileFailure(error, handle, filePath, identity)
35
36
  !identity.isFile() || identity.nlink > 1n)
36
37
  return;
37
38
  try {
38
- const current = await inspectFileIdentity(() => handle.stat({ bigint: true }), identity);
39
+ const current = await inspectFileIdentity(() => fsSync.fstatSync(handle.fd, { bigint: true }), identity);
39
40
  if (current.isFile() && current.nlink === 0n)
40
41
  recordFileObservationFailure(error, `unlinked:${filePath}`);
41
42
  }
@@ -1,9 +1,9 @@
1
- import fs from "node:fs/promises";
1
+ import fsSync from "node:fs";
2
2
  import { formatIcaclsResetCommand, inspectWindowsPermissions, } from "./permissions-windows.js";
3
3
  export { createIcaclsResetCommand, formatIcaclsResetCommand, formatWindowsAclSummary, inspectWindowsAcl, parseIcaclsOutput, resolveWindowsUserPrincipal, summarizeWindowsAcl, } from "./permissions-windows.js";
4
4
  export async function safeStat(targetPath) {
5
5
  try {
6
- const lst = await fs.lstat(targetPath);
6
+ const lst = fsSync.lstatSync(targetPath);
7
7
  return {
8
8
  ok: true,
9
9
  isSymlink: lst.isSymbolicLink(),
@@ -46,7 +46,7 @@ export async function inspectPathPermissions(targetPath, opts) {
46
46
  let effectiveIsDir = st.isDir;
47
47
  if (st.isSymlink) {
48
48
  try {
49
- const target = await fs.stat(targetPath);
49
+ const target = fsSync.statSync(targetPath);
50
50
  effectiveMode = typeof target.mode === "number" ? target.mode : st.mode;
51
51
  effectiveIsDir = target.isDirectory();
52
52
  }
@@ -1 +1 @@
1
- {"version":3,"file":"private-temp-workspace.d.ts","sourceRoot":"","sources":["../src/private-temp-workspace.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"private-temp-workspace.d.ts","sourceRoot":"","sources":["../src/private-temp-workspace.ts"],"names":[],"mappings":"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"}
@@ -67,7 +67,7 @@ function throwTempWorkspaceOpenFailure(failure) {
67
67
  }
68
68
  async function ensurePrivateDirectory(dir, mode) {
69
69
  await fs.mkdir(dir, { recursive: true, mode });
70
- const stat = await fs.stat(dir);
70
+ const stat = fsSync.statSync(dir);
71
71
  if (!stat.isDirectory()) {
72
72
  throw new Error(`Temp root must be a directory: ${dir}`);
73
73
  }
@@ -91,7 +91,13 @@ async function createTempWorkspace(options) {
91
91
  const mode = options.mode ?? 0o600;
92
92
  const cleanupSafety = resolveTempWorkspaceCleanupSafety(options.cleanupSafety);
93
93
  const requestedRoot = path.resolve(options.rootDir);
94
- const root = await fs.realpath(requestedRoot).catch(() => requestedRoot);
94
+ let root = requestedRoot;
95
+ try {
96
+ root = fsSync.realpathSync.native(requestedRoot);
97
+ }
98
+ catch {
99
+ root = requestedRoot;
100
+ }
95
101
  await ensurePrivateDirectory(root, dirMode);
96
102
  const capability = new TempWorkspaceCleanupCapability(root, cleanupSafety);
97
103
  let dir;
@@ -101,7 +107,7 @@ async function createTempWorkspace(options) {
101
107
  try {
102
108
  dir = await fs.mkdtemp(path.join(root, sanitizeTempPrefix(options.prefix)));
103
109
  await fs.chmod(dir, dirMode).catch(() => undefined);
104
- stat = await fs.lstat(dir, { bigint: true });
110
+ stat = fsSync.lstatSync(dir, { bigint: true });
105
111
  if (stat.isSymbolicLink() || !stat.isDirectory()) {
106
112
  throw new Error(`Temp workspace must be a directory: ${dir}`);
107
113
  }
@@ -40,8 +40,8 @@ async function openNativeParent(filePath) {
40
40
  const parentPath = path.dirname(filePath);
41
41
  const handle = await fs.open(parentPath, directoryOpenFlags());
42
42
  try {
43
- const pathname = await fs.lstat(parentPath, { bigint: true });
44
- const opened = await handle.stat({ bigint: true });
43
+ const pathname = fsSync.lstatSync(parentPath, { bigint: true });
44
+ const opened = fsSync.fstatSync(handle.fd, { bigint: true });
45
45
  if (pathname.isSymbolicLink() || !sameFileIdentity(pathname, opened)) {
46
46
  throw new FsSafeError("path-mismatch", "publication parent changed while opening");
47
47
  }
@@ -55,8 +55,8 @@ async function openNativeParent(filePath) {
55
55
  async function assertPinnedSourceCurrent(params) {
56
56
  // Windows file indexes can exceed Number.MAX_SAFE_INTEGER, so publication
57
57
  // fences must compare bigint stats instead of rounded numeric identities.
58
- const opened = await params.handle.stat({ bigint: true });
59
- const current = await fs.lstat(params.sourcePath, { bigint: true });
58
+ const opened = fsSync.fstatSync(params.handle.fd, { bigint: true });
59
+ const current = fsSync.lstatSync(params.sourcePath, { bigint: true });
60
60
  if (!opened.isFile() ||
61
61
  current.isSymbolicLink() ||
62
62
  !current.isFile() ||
@@ -96,9 +96,9 @@ async function copyPinnedSource(params) {
96
96
  fsSync.fchmodSync(nativeFd, 0o600);
97
97
  createdIdentity = fsSync.fstatSync(nativeFd, { bigint: true });
98
98
  await getFsSafeTestHooks()?.afterPublishTargetCreated?.("exclusive-copy", params.targetPath, createdIdentity);
99
- const identity = await fs.lstat(params.targetPath, { bigint: true });
99
+ const identity = fsSync.lstatSync(params.targetPath, { bigint: true });
100
100
  target = await fs.open(params.targetPath, sourceOpenFlags());
101
- const opened = await target.stat({ bigint: true });
101
+ const opened = fsSync.fstatSync(target.fd, { bigint: true });
102
102
  if (identity.isSymbolicLink() ||
103
103
  !identity.isFile() ||
104
104
  !sameFileIdentity(createdIdentity, identity) ||
@@ -126,11 +126,11 @@ async function copyPinnedSource(params) {
126
126
  }
127
127
  }
128
128
  const target = await fs.open(params.targetPath, "wx+", 0o600);
129
- const createdIdentity = await target.stat({ bigint: true });
129
+ const createdIdentity = fsSync.fstatSync(target.fd, { bigint: true });
130
130
  rememberCreatedTarget(params.failure, createdIdentity, "copy-verify");
131
131
  try {
132
132
  await target.chmod(0o600);
133
- const exactIdentity = await target.stat({ bigint: true });
133
+ const exactIdentity = fsSync.fstatSync(target.fd, { bigint: true });
134
134
  await getFsSafeTestHooks()?.afterPublishTargetCreated?.("exclusive-copy", params.targetPath, exactIdentity);
135
135
  const hash = createHash("sha256");
136
136
  const buffer = Buffer.allocUnsafe(64 * 1024);
@@ -164,7 +164,7 @@ async function removeCreatedTargetIfUnchanged(targetPath, identity) {
164
164
  return "unknown";
165
165
  }
166
166
  try {
167
- const current = await fs.lstat(targetPath, { bigint: true });
167
+ const current = fsSync.lstatSync(targetPath, { bigint: true });
168
168
  if (!current.isSymbolicLink() && sameFileIdentityForCleanup(current, identity)) {
169
169
  await fs.rm(targetPath);
170
170
  return "removed";
@@ -195,7 +195,7 @@ export async function publishFileExclusive(params) {
195
195
  if (params.parentReceipt && path.resolve(params.parentReceipt.path) !== parentPath) {
196
196
  throw new FsSafeError("path-mismatch", "publication parent receipt does not match target parent");
197
197
  }
198
- const sourcePathStat = await fs.lstat(sourcePath);
198
+ const sourcePathStat = fsSync.lstatSync(sourcePath);
199
199
  if (sourcePathStat.isSymbolicLink() || !sourcePathStat.isFile()) {
200
200
  throw new FsSafeError("not-file", "publication source must be a regular file");
201
201
  }
@@ -212,9 +212,9 @@ export async function publishFileExclusive(params) {
212
212
  parent = await pinDirectory(params.parentReceipt ?? parentPath, {
213
213
  label: "publication parent",
214
214
  });
215
- const sourceIdentity = await source.stat();
216
- const sourceExactIdentity = await source.stat({ bigint: true });
217
- const sourcePathExactIdentity = await fs.lstat(sourcePath, { bigint: true });
215
+ const sourceIdentity = fsSync.fstatSync(source.fd);
216
+ const sourceExactIdentity = fsSync.fstatSync(source.fd, { bigint: true });
217
+ const sourcePathExactIdentity = fsSync.lstatSync(sourcePath, { bigint: true });
218
218
  if (sourcePathExactIdentity.isSymbolicLink() ||
219
219
  !sourcePathExactIdentity.isFile() ||
220
220
  !sameFileIdentity(sourcePathExactIdentity, sourceExactIdentity) ||
@@ -241,14 +241,14 @@ export async function publishFileExclusive(params) {
241
241
  // A failed post-rename fence must not delete the only remaining name.
242
242
  failure.preserveTarget = true;
243
243
  await getFsSafeTestHooks()?.afterPublishTargetCreated?.("rename-noreplace", targetPath, sourceExactIdentity);
244
- const targetExactIdentity = await fs.lstat(targetPath, { bigint: true });
244
+ const targetExactIdentity = fsSync.lstatSync(targetPath, { bigint: true });
245
245
  if (targetExactIdentity.isSymbolicLink() ||
246
246
  !targetExactIdentity.isFile() ||
247
247
  !sameFileIdentity(targetExactIdentity, sourceExactIdentity)) {
248
248
  throw new FsSafeError("path-mismatch", "no-replace publication target changed");
249
249
  }
250
250
  try {
251
- await fs.lstat(sourcePath);
251
+ fsSync.lstatSync(sourcePath);
252
252
  throw new FsSafeError("path-mismatch", "no-replace publication source still exists");
253
253
  }
254
254
  catch (error) {
@@ -257,7 +257,7 @@ export async function publishFileExclusive(params) {
257
257
  }
258
258
  }
259
259
  syncFileBestEffortSync(sourceNativeParent.handle.fd);
260
- const targetIdentity = await fs.lstat(targetPath);
260
+ const targetIdentity = fsSync.lstatSync(targetPath);
261
261
  return {
262
262
  method: "rename-noreplace",
263
263
  identity: targetIdentity,
@@ -278,14 +278,14 @@ export async function publishFileExclusive(params) {
278
278
  }
279
279
  rememberCreatedTarget(failure, sourceExactIdentity, "hardlink-verify");
280
280
  await getFsSafeTestHooks()?.afterPublishTargetCreated?.("hardlink", targetPath, sourceExactIdentity);
281
- const targetExactIdentity = await fs.lstat(targetPath, { bigint: true });
281
+ const targetExactIdentity = fsSync.lstatSync(targetPath, { bigint: true });
282
282
  if (targetExactIdentity.isSymbolicLink() ||
283
283
  !targetExactIdentity.isFile() ||
284
284
  !sameFileIdentity(targetExactIdentity, sourceExactIdentity)) {
285
285
  throw new FsSafeError("path-mismatch", "hardlink publication target changed");
286
286
  }
287
287
  await assertPinnedSourceCurrent({ sourcePath, handle: source, identity: sourceExactIdentity });
288
- const targetIdentity = await fs.lstat(targetPath);
288
+ const targetIdentity = fsSync.lstatSync(targetPath);
289
289
  return {
290
290
  method: "hardlink",
291
291
  identity: targetIdentity,
@@ -316,9 +316,9 @@ export async function publishFileExclusive(params) {
316
316
  failure,
317
317
  });
318
318
  target = copied.handle;
319
- targetIdentity = await target.stat();
320
- const targetPathStat = await fs.lstat(targetPath);
321
- const targetPathExactStat = await fs.lstat(targetPath, { bigint: true });
319
+ targetIdentity = fsSync.fstatSync(target.fd);
320
+ const targetPathStat = fsSync.lstatSync(targetPath);
321
+ const targetPathExactStat = fsSync.lstatSync(targetPath, { bigint: true });
322
322
  const copiedBack = await hashFileHandle(target, native);
323
323
  const sourceAfter = await hashFileHandle(source, native);
324
324
  if (targetPathStat.isSymbolicLink() ||
@@ -1 +1 @@
1
- {"version":3,"file":"replace-file-copy-fallback.d.ts","sourceRoot":"","sources":["../src/replace-file-copy-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAc,MAAM,SAAS,CAAC;AAO/D,MAAM,MAAM,oCAAoC,GAAG,QAAQ,CAAC;AAC5D,MAAM,MAAM,oCAAoC,GAAG,kBAAkB,GAAG,MAAM,CAAC;AAC/E,MAAM,MAAM,+BAA+B,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAC5E,MAAM,MAAM,sCAAsC,GAAG;IACnD,OAAO,EAAE,+BAA+B,CAAC;CAC1C,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,KAAK,EAAE,cAAc,kBAAkB,EAAE,KAAK,CAAC;IAC/C,IAAI,EAAE,cAAc,kBAAkB,EAAE,IAAI,CAAC;IAC7C,EAAE,EAAE,cAAc,kBAAkB,EAAE,EAAE,CAAC;IACzC,MAAM,EAAE,cAAc,kBAAkB,EAAE,MAAM,CAAC;CAClD,CAAC;AAEF,KAAK,cAAc,GAAG,IAAI,CACxB,OAAO,MAAM,EACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,eAAe,GACf,WAAW,GACX,UAAU,GACV,UAAU,GACV,QAAQ,GACR,YAAY,GACZ,WAAW,CACd,CAAC;AAuFF,wBAAsB,+BAA+B,CACnD,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,oCAAoC,GAC5C,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,oCAAoC,GAC5C,IAAI,CAwBN;AAmID,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,QAAQ,EAAE,eAAe,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,oCAAoC,CAAC;IAC5D,OAAO,EAAE,oCAAoC,CAAC;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,WAAW,CAAC;IACrC,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,OAAO,CAAC,IAAI,CAAC,CAwDhB;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,QAAQ,EAAE,cAAc,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,oCAAoC,CAAC;IAC5D,OAAO,EAAE,oCAAoC,CAAC;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,WAAW,CAAC;IACrC,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,IAAI,CAkEP"}
1
+ {"version":3,"file":"replace-file-copy-fallback.d.ts","sourceRoot":"","sources":["../src/replace-file-copy-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAc,MAAM,SAAS,CAAC;AAO/D,MAAM,MAAM,oCAAoC,GAAG,QAAQ,CAAC;AAC5D,MAAM,MAAM,oCAAoC,GAAG,kBAAkB,GAAG,MAAM,CAAC;AAC/E,MAAM,MAAM,+BAA+B,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAC5E,MAAM,MAAM,sCAAsC,GAAG;IACnD,OAAO,EAAE,+BAA+B,CAAC;CAC1C,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,KAAK,EAAE,cAAc,kBAAkB,EAAE,KAAK,CAAC;IAC/C,IAAI,EAAE,cAAc,kBAAkB,EAAE,IAAI,CAAC;IAC7C,EAAE,EAAE,cAAc,kBAAkB,EAAE,EAAE,CAAC;IACzC,MAAM,EAAE,cAAc,kBAAkB,EAAE,MAAM,CAAC;CAClD,CAAC;AAEF,KAAK,cAAc,GAAG,IAAI,CACxB,OAAO,MAAM,EACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,eAAe,GACf,WAAW,GACX,UAAU,GACV,UAAU,GACV,QAAQ,GACR,YAAY,GACZ,WAAW,CACd,CAAC;AA0FF,wBAAsB,+BAA+B,CACnD,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,oCAAoC,GAC5C,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,oCAAoC,GAC5C,IAAI,CAwBN;AAoID,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,QAAQ,EAAE,eAAe,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,oCAAoC,CAAC;IAC5D,OAAO,EAAE,oCAAoC,CAAC;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,WAAW,CAAC;IACrC,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,OAAO,CAAC,IAAI,CAAC,CA4DhB;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,QAAQ,EAAE,cAAc,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,oCAAoC,CAAC;IAC5D,OAAO,EAAE,oCAAoC,CAAC;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,WAAW,CAAC;IACrC,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,IAAI,CAkEP"}