@openclaw/fs-safe 0.13.1 → 0.15.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 (256) hide show
  1. package/CHANGELOG.md +64 -1
  2. package/README.md +10 -4
  3. package/dist/advanced.d.ts +1 -1
  4. package/dist/advanced.d.ts.map +1 -1
  5. package/dist/advanced.js +1 -1
  6. package/dist/archive-merge.d.ts.map +1 -1
  7. package/dist/archive-merge.js +113 -46
  8. package/dist/archive-read.d.ts.map +1 -1
  9. package/dist/archive-read.js +4 -4
  10. package/dist/archive-zip-directory.d.ts +4 -0
  11. package/dist/archive-zip-directory.d.ts.map +1 -1
  12. package/dist/archive-zip-directory.js +2 -0
  13. package/dist/archive-zip-entry.d.ts +6 -2
  14. package/dist/archive-zip-entry.d.ts.map +1 -1
  15. package/dist/archive-zip-entry.js +23 -8
  16. package/dist/archive-zip-integrity.d.ts.map +1 -1
  17. package/dist/archive-zip-integrity.js +3 -4
  18. package/dist/archive-zip-loader.d.ts.map +1 -1
  19. package/dist/archive-zip-loader.js +107 -31
  20. package/dist/archive-zip-names.d.ts +1 -0
  21. package/dist/archive-zip-names.d.ts.map +1 -1
  22. package/dist/archive-zip-names.js +6 -0
  23. package/dist/archive.js +6 -5
  24. package/dist/bounded-read-stream.d.ts +0 -1
  25. package/dist/bounded-read-stream.d.ts.map +1 -1
  26. package/dist/bounded-read-stream.js +0 -6
  27. package/dist/copy-publication.d.ts +6 -0
  28. package/dist/copy-publication.d.ts.map +1 -1
  29. package/dist/copy-publication.js +3 -0
  30. package/dist/copy-tree-portable.d.ts.map +1 -1
  31. package/dist/copy-tree-portable.js +44 -24
  32. package/dist/copy.d.ts.map +1 -1
  33. package/dist/copy.js +29 -11
  34. package/dist/directory-mode-owner.js +5 -5
  35. package/dist/file-handle-transfer.d.ts +2 -0
  36. package/dist/file-handle-transfer.d.ts.map +1 -1
  37. package/dist/file-handle-transfer.js +57 -2
  38. package/dist/file-identity.d.ts.map +1 -1
  39. package/dist/file-identity.js +18 -4
  40. package/dist/file-lock-sync-admission.d.ts +19 -0
  41. package/dist/file-lock-sync-admission.d.ts.map +1 -0
  42. package/dist/file-lock-sync-admission.js +93 -0
  43. package/dist/file-lock-sync-root-acquire.d.ts +4 -0
  44. package/dist/file-lock-sync-root-acquire.d.ts.map +1 -0
  45. package/dist/file-lock-sync-root-acquire.js +370 -0
  46. package/dist/file-lock-sync-root-arbitration.d.ts +18 -0
  47. package/dist/file-lock-sync-root-arbitration.d.ts.map +1 -0
  48. package/dist/file-lock-sync-root-arbitration.js +66 -0
  49. package/dist/file-lock-sync-root-held.d.ts +34 -0
  50. package/dist/file-lock-sync-root-held.d.ts.map +1 -0
  51. package/dist/file-lock-sync-root-held.js +393 -0
  52. package/dist/file-lock-sync-root-io.d.ts +44 -0
  53. package/dist/file-lock-sync-root-io.d.ts.map +1 -0
  54. package/dist/file-lock-sync-root-io.js +209 -0
  55. package/dist/file-lock-sync-root-mutation.d.ts +17 -0
  56. package/dist/file-lock-sync-root-mutation.d.ts.map +1 -0
  57. package/dist/file-lock-sync-root-mutation.js +277 -0
  58. package/dist/file-lock-sync-root-options.d.ts +20 -0
  59. package/dist/file-lock-sync-root-options.d.ts.map +1 -0
  60. package/dist/file-lock-sync-root-options.js +58 -0
  61. package/dist/file-lock-sync-root-registration.d.ts +2 -0
  62. package/dist/file-lock-sync-root-registration.d.ts.map +1 -0
  63. package/dist/file-lock-sync-root-registration.js +90 -0
  64. package/dist/file-lock-sync-root.d.ts +36 -0
  65. package/dist/file-lock-sync-root.d.ts.map +1 -0
  66. package/dist/file-lock-sync-root.js +361 -0
  67. package/dist/file-lock-sync-stale-admission.d.ts +24 -0
  68. package/dist/file-lock-sync-stale-admission.d.ts.map +1 -0
  69. package/dist/file-lock-sync-stale-admission.js +205 -0
  70. package/dist/file-lock-sync.d.ts.map +1 -1
  71. package/dist/file-lock-sync.js +245 -205
  72. package/dist/file-store-prune.d.ts.map +1 -1
  73. package/dist/file-store-prune.js +5 -1
  74. package/dist/file-store-sync-directory.d.ts.map +1 -1
  75. package/dist/file-store-sync-directory.js +14 -7
  76. package/dist/file-store-sync-write.js +3 -3
  77. package/dist/file-store.d.ts.map +1 -1
  78. package/dist/file-store.js +47 -12
  79. package/dist/guest-dispatch-python.js +1 -1
  80. package/dist/json-document-store.d.ts.map +1 -1
  81. package/dist/json-document-store.js +22 -15
  82. package/dist/json-durable-queue-ownership.d.ts +0 -1
  83. package/dist/json-durable-queue-ownership.d.ts.map +1 -1
  84. package/dist/json-durable-queue-ownership.js +0 -6
  85. package/dist/native-binding.d.ts +8 -0
  86. package/dist/native-binding.d.ts.map +1 -1
  87. package/dist/native-parent-admission.d.ts +3 -2
  88. package/dist/native-parent-admission.d.ts.map +1 -1
  89. package/dist/native-parent-admission.js +24 -5
  90. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  91. package/dist/native-pinned-write-windows.js +7 -7
  92. package/dist/native-pinned-write.d.ts.map +1 -1
  93. package/dist/native-pinned-write.js +202 -172
  94. package/dist/native-policy-directory-observation.d.ts +12 -0
  95. package/dist/native-policy-directory-observation.d.ts.map +1 -0
  96. package/dist/native-policy-directory-observation.js +61 -0
  97. package/dist/native-policy-parent-windows.d.ts +14 -0
  98. package/dist/native-policy-parent-windows.d.ts.map +1 -0
  99. package/dist/native-policy-parent-windows.js +200 -0
  100. package/dist/native-rename-outcome.d.ts +4 -0
  101. package/dist/native-rename-outcome.d.ts.map +1 -0
  102. package/dist/native-rename-outcome.js +8 -0
  103. package/dist/native-staged-file.d.ts +2 -2
  104. package/dist/native-staged-file.d.ts.map +1 -1
  105. package/dist/native-staged-file.js +42 -40
  106. package/dist/output.d.ts.map +1 -1
  107. package/dist/output.js +12 -8
  108. package/dist/path-prefix.d.ts.map +1 -1
  109. package/dist/path-prefix.js +30 -8
  110. package/dist/path-suffix-aliases.d.ts +2 -0
  111. package/dist/path-suffix-aliases.d.ts.map +1 -1
  112. package/dist/path-suffix-aliases.js +25 -17
  113. package/dist/permission-exec.d.ts +2 -0
  114. package/dist/permission-exec.d.ts.map +1 -1
  115. package/dist/permission-exec.js +150 -21
  116. package/dist/permissions-windows.js +1 -1
  117. package/dist/pinned-mutation-admission.d.ts.map +1 -1
  118. package/dist/pinned-mutation-admission.js +23 -29
  119. package/dist/pinned-mutation-observation.d.ts +7 -2
  120. package/dist/pinned-mutation-observation.d.ts.map +1 -1
  121. package/dist/pinned-mutation-observation.js +81 -18
  122. package/dist/pinned-mutation-shared-route.d.ts +1 -0
  123. package/dist/pinned-mutation-shared-route.d.ts.map +1 -1
  124. package/dist/pinned-mutation-shared-route.js +1 -1
  125. package/dist/pinned-write-types.d.ts +2 -0
  126. package/dist/pinned-write-types.d.ts.map +1 -1
  127. package/dist/private-temp-workspace.d.ts.map +1 -1
  128. package/dist/private-temp-workspace.js +75 -121
  129. package/dist/regular-file.d.ts.map +1 -1
  130. package/dist/regular-file.js +1 -15
  131. package/dist/replace-directory.d.ts.map +1 -1
  132. package/dist/replace-directory.js +256 -18
  133. package/dist/replace-file-copy-fallback.d.ts.map +1 -1
  134. package/dist/replace-file-copy-fallback.js +62 -70
  135. package/dist/replace-file-copy-source.d.ts.map +1 -1
  136. package/dist/replace-file-copy-source.js +10 -12
  137. package/dist/replace-file-temp-owner.d.ts +5 -2
  138. package/dist/replace-file-temp-owner.d.ts.map +1 -1
  139. package/dist/replace-file-temp-owner.js +65 -30
  140. package/dist/replace-file.js +6 -6
  141. package/dist/retained-directory-replacement.d.ts +26 -0
  142. package/dist/retained-directory-replacement.d.ts.map +1 -0
  143. package/dist/retained-directory-replacement.js +193 -0
  144. package/dist/root-boundary.d.ts +1 -0
  145. package/dist/root-boundary.d.ts.map +1 -1
  146. package/dist/root-boundary.js +4 -0
  147. package/dist/root-context.d.ts +0 -8
  148. package/dist/root-context.d.ts.map +1 -1
  149. package/dist/root-context.js +0 -3
  150. package/dist/root-create-input.d.ts +6 -0
  151. package/dist/root-create-input.d.ts.map +1 -1
  152. package/dist/root-create-input.js +5 -1
  153. package/dist/root-directory-creation.d.ts.map +1 -1
  154. package/dist/root-directory-creation.js +5 -4
  155. package/dist/root-directory-list.d.ts +1 -0
  156. package/dist/root-directory-list.d.ts.map +1 -1
  157. package/dist/root-directory-list.js +1 -0
  158. package/dist/root-impl.d.ts.map +1 -1
  159. package/dist/root-impl.js +20 -11
  160. package/dist/root-move-noreplace.d.ts.map +1 -1
  161. package/dist/root-move-noreplace.js +2 -2
  162. package/dist/root-path-errors.d.ts +1 -0
  163. package/dist/root-path-errors.d.ts.map +1 -1
  164. package/dist/root-path-errors.js +11 -2
  165. package/dist/root-path-existing.d.ts.map +1 -1
  166. package/dist/root-path-existing.js +12 -37
  167. package/dist/root-path.js +1 -13
  168. package/dist/root-remove.d.ts +1 -0
  169. package/dist/root-remove.d.ts.map +1 -1
  170. package/dist/root-remove.js +4 -0
  171. package/dist/root-walk.d.ts +1 -1
  172. package/dist/root-walk.d.ts.map +1 -1
  173. package/dist/root-walk.js +17 -2
  174. package/dist/root-write-admission.d.ts +0 -2
  175. package/dist/root-write-admission.d.ts.map +1 -1
  176. package/dist/root-write-admission.js +1 -15
  177. package/dist/root-write-compatibility.d.ts +1 -2
  178. package/dist/root-write-compatibility.d.ts.map +1 -1
  179. package/dist/root-write-compatibility.js +17 -68
  180. package/dist/root-write-complete-parent.d.ts.map +1 -1
  181. package/dist/root-write-complete-parent.js +10 -24
  182. package/dist/root-write-lock-binding.d.ts +15 -0
  183. package/dist/root-write-lock-binding.d.ts.map +1 -0
  184. package/dist/root-write-lock-binding.js +162 -0
  185. package/dist/root-write-verification.d.ts.map +1 -1
  186. package/dist/root-write-verification.js +29 -42
  187. package/dist/secret-file.d.ts.map +1 -1
  188. package/dist/secret-file.js +2 -24
  189. package/dist/secret-read-async.d.ts.map +1 -1
  190. package/dist/secret-read-async.js +3 -24
  191. package/dist/secret-read-policy.d.ts +6 -2
  192. package/dist/secret-read-policy.d.ts.map +1 -1
  193. package/dist/secret-read-policy.js +26 -2
  194. package/dist/sibling-temp.d.ts.map +1 -1
  195. package/dist/sibling-temp.js +23 -12
  196. package/dist/sidecar-lock-acquire.d.ts +2 -28
  197. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  198. package/dist/sidecar-lock-acquire.js +288 -199
  199. package/dist/sidecar-lock-admission-context.d.ts +19 -0
  200. package/dist/sidecar-lock-admission-context.d.ts.map +1 -0
  201. package/dist/sidecar-lock-admission-context.js +60 -0
  202. package/dist/sidecar-lock-admission-parser.d.ts +43 -0
  203. package/dist/sidecar-lock-admission-parser.d.ts.map +1 -0
  204. package/dist/sidecar-lock-admission-parser.js +113 -0
  205. package/dist/sidecar-lock-admission.d.ts +35 -0
  206. package/dist/sidecar-lock-admission.d.ts.map +1 -0
  207. package/dist/sidecar-lock-admission.js +7 -0
  208. package/dist/sidecar-lock-reclaim.d.ts +9 -4
  209. package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
  210. package/dist/sidecar-lock-reclaim.js +80 -25
  211. package/dist/sidecar-lock-stale-admission.d.ts +39 -0
  212. package/dist/sidecar-lock-stale-admission.d.ts.map +1 -0
  213. package/dist/sidecar-lock-stale-admission.js +232 -0
  214. package/dist/sidecar-lock-target.d.ts +8 -0
  215. package/dist/sidecar-lock-target.d.ts.map +1 -0
  216. package/dist/sidecar-lock-target.js +55 -0
  217. package/dist/sidecar-lock.d.ts.map +1 -1
  218. package/dist/sidecar-lock.js +100 -16
  219. package/dist/staged-directory.d.ts +5 -2
  220. package/dist/staged-directory.d.ts.map +1 -1
  221. package/dist/staged-directory.js +37 -1
  222. package/dist/temp-workspace-admission.d.ts.map +1 -1
  223. package/dist/temp-workspace-admission.js +28 -4
  224. package/dist/temp-workspace-descriptor.d.ts.map +1 -1
  225. package/dist/temp-workspace-descriptor.js +9 -27
  226. package/dist/temp-workspace-owner.d.ts.map +1 -1
  227. package/dist/temp-workspace-owner.js +8 -8
  228. package/dist/walk.d.ts +5 -1
  229. package/dist/walk.d.ts.map +1 -1
  230. package/dist/walk.js +19 -6
  231. package/dist/windows-owner.d.ts.map +1 -1
  232. package/dist/windows-owner.js +2 -2
  233. package/docs/advanced.md +1 -1
  234. package/docs/archive.md +36 -9
  235. package/docs/atomic.md +85 -8
  236. package/docs/copy.md +35 -0
  237. package/docs/file-store.md +19 -0
  238. package/docs/guest.md +1 -1
  239. package/docs/json-store.md +5 -0
  240. package/docs/native-helper.md +10 -3
  241. package/docs/native.md +8 -0
  242. package/docs/output.md +6 -0
  243. package/docs/path-prefix.md +10 -0
  244. package/docs/path-suffix-aliases.md +51 -6
  245. package/docs/permissions.md +13 -0
  246. package/docs/public-api.md +3 -2
  247. package/docs/root.md +22 -3
  248. package/docs/sidecar-lock.md +109 -4
  249. package/docs/staged-file.md +7 -3
  250. package/docs/temp.md +20 -3
  251. package/docs/walk.md +67 -1
  252. package/docs/writing.md +11 -1
  253. package/package.json +10 -10
  254. package/dist/darwin-acl.d.ts +0 -4
  255. package/dist/darwin-acl.d.ts.map +0 -1
  256. package/dist/darwin-acl.js +0 -24
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidecar-lock-admission-context.d.ts","sourceRoot":"","sources":["../src/sidecar-lock-admission-context.ts"],"names":[],"mappings":"AAEA,KAAK,iBAAiB,GAAG;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,wBAAwB,GAAG,SAAS,iBAAiB,EAAE,CAAC;AAkB7D,wFAAwF;AACxF,wBAAgB,+BAA+B,IAAI,wBAAwB,CAM1E;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,wBAAwB,EAClC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,oBAAoB,EAAE,MAAM,GAC3B,OAAO,CAGT;AA4BD,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,wBAAwB,EAClC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,oBAAoB,EAAE,MAAM;mBAMd,OAAO;oBAHA,OAAO;eAKf,IAAI;eAKJ,IAAI;QAMX,CAAC,YAAY,MAAM,CAAC,GAAG,CAAC;EAI/B"}
@@ -0,0 +1,60 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ const EMPTY_ANCESTRY = Object.freeze([]);
3
+ const GLOBAL_CONTEXT_KEY = Symbol.for("fsSafe.sidecarLockAdmissionAsyncContext");
4
+ function admissionContext() {
5
+ const shared = globalThis;
6
+ return shared[GLOBAL_CONTEXT_KEY] ??=
7
+ new AsyncLocalStorage();
8
+ }
9
+ /** Capture active ancestry now; later lifecycle changes cannot reclassify this call. */
10
+ export function captureSidecarAdmissionAncestry() {
11
+ const current = admissionContext().getStore();
12
+ if (!current)
13
+ return EMPTY_ANCESTRY;
14
+ const active = current.ancestry.filter((entry) => entry.active);
15
+ if (current.active)
16
+ active.push(current);
17
+ return active.length === 0 ? EMPTY_ANCESTRY : active;
18
+ }
19
+ export function ancestryHasSidecarAdmission(ancestry, admissions, normalizedTargetPath) {
20
+ return ancestry.some((entry) => entry.admissions === admissions && entry.normalizedTargetPath === normalizedTargetPath);
21
+ }
22
+ function createSidecarAdmissionScope(ancestry, admissions, normalizedTargetPath, token) {
23
+ return { active: false, admissions, ancestry, normalizedTargetPath, started: false, token };
24
+ }
25
+ function activateSidecarAdmissionScope(scope) {
26
+ if (scope.started)
27
+ throw new Error("sidecar admission scope cannot be reactivated");
28
+ scope.started = true;
29
+ scope.active = true;
30
+ }
31
+ function deactivateSidecarAdmissionScope(scope) {
32
+ if (scope)
33
+ scope.active = false;
34
+ }
35
+ function runInSidecarAdmissionScope(scope, callback) {
36
+ return scope?.active ? admissionContext().run(scope, callback) : callback();
37
+ }
38
+ export function createSidecarAdmissionController(ancestry, admissions, normalizedTargetPath) {
39
+ let scope;
40
+ const hasToken = () => scope !== undefined && admissions.get(normalizedTargetPath) === scope.token;
41
+ return {
42
+ get owns() { return scope !== undefined; },
43
+ hasToken,
44
+ release() {
45
+ deactivateSidecarAdmissionScope(scope);
46
+ if (hasToken())
47
+ admissions.delete(normalizedTargetPath);
48
+ scope = undefined;
49
+ },
50
+ reserve() {
51
+ const token = {};
52
+ scope = createSidecarAdmissionScope(ancestry, admissions, normalizedTargetPath, token);
53
+ admissions.set(normalizedTargetPath, token);
54
+ activateSidecarAdmissionScope(scope);
55
+ },
56
+ run(callback) {
57
+ return runInSidecarAdmissionScope(scope, callback);
58
+ },
59
+ };
60
+ }
@@ -0,0 +1,43 @@
1
+ import type { Root } from "./root-impl.js";
2
+ import type { HeldSidecarLock } from "./sidecar-lock-admission.js";
3
+ import { readSidecarLockRawSnapshot, type SidecarLockSnapshot } from "./sidecar-lock-reclaim.js";
4
+ type SidecarAdmissionRunner = {
5
+ hasToken(): boolean;
6
+ run<T>(callback: () => T): T;
7
+ };
8
+ export declare function runSidecarAdmissionBoundary<T>(admission: SidecarAdmissionRunner, assertCurrent: () => void, callback: () => T): T;
9
+ export declare function awaitSidecarAdmissionBoundary<T>(admission: SidecarAdmissionRunner, assertCurrent: () => void, callback: () => T | PromiseLike<T>): Promise<{
10
+ readonly value: Awaited<T>;
11
+ }>;
12
+ export type SidecarLockParserState = {
13
+ observed: boolean;
14
+ parser?: (raw: string) => unknown;
15
+ };
16
+ export declare function observeSidecarLockParser(state: SidecarLockParserState, admission: SidecarAdmissionRunner, accessor: () => ((raw: string) => unknown) | undefined, assertCurrent: () => void): void;
17
+ export declare function scopedSidecarLockParser(state: SidecarLockParserState, admission: SidecarAdmissionRunner, assertCurrent: () => void): ((raw: string) => unknown) | undefined;
18
+ /** Cleanup may outlive its reservation; never invoke user code after authority is lost. */
19
+ export declare function conditionalSidecarLockParser(state: SidecarLockParserState, admission: SidecarAdmissionRunner, isCurrent: () => boolean): ((raw: string) => unknown) | undefined;
20
+ export declare function parseSidecarLockSnapshotUnderAdmission(raw: Awaited<ReturnType<typeof readSidecarLockRawSnapshot>>, state: SidecarLockParserState, admission: SidecarAdmissionRunner, assertCurrent: () => void): SidecarLockSnapshot | null;
21
+ export type HeldSidecarParserObservation = {
22
+ kind: "observed";
23
+ } | {
24
+ kind: "holder-changed";
25
+ } | {
26
+ kind: "read-error";
27
+ error: unknown;
28
+ transientDenial: boolean;
29
+ };
30
+ /** Emulate the historical EEXIST read/parser boundary without opening another sidecar. */
31
+ export declare function observeHeldSidecarParser(params: {
32
+ admission: SidecarAdmissionRunner;
33
+ assertToken(): void;
34
+ currentHeld(): HeldSidecarLock | undefined;
35
+ held: HeldSidecarLock;
36
+ lockPath: string;
37
+ lockRoot?: Root;
38
+ parserState: SidecarLockParserState;
39
+ parserAccessor(): ((raw: string) => unknown) | undefined;
40
+ isTransientDenial(error: unknown): boolean;
41
+ }): Promise<HeldSidecarParserObservation>;
42
+ export {};
43
+ //# sourceMappingURL=sidecar-lock-admission-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidecar-lock-admission-parser.d.ts","sourceRoot":"","sources":["../src/sidecar-lock-admission-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ import { parseSidecarLockSnapshot, readSidecarLockRawSnapshot, } from "./sidecar-lock-reclaim.js";
2
+ export function runSidecarAdmissionBoundary(admission, assertCurrent, callback) {
3
+ assertCurrent();
4
+ try {
5
+ return admission.run(callback);
6
+ }
7
+ finally {
8
+ assertCurrent();
9
+ }
10
+ }
11
+ export async function awaitSidecarAdmissionBoundary(admission, assertCurrent, callback) {
12
+ assertCurrent();
13
+ try {
14
+ return await admission.run(async () => {
15
+ const boxed = Object.create(null);
16
+ Object.defineProperty(boxed, "value", {
17
+ value: undefined, enumerable: true, writable: true,
18
+ });
19
+ boxed.value = await callback();
20
+ return boxed;
21
+ });
22
+ }
23
+ finally {
24
+ assertCurrent();
25
+ }
26
+ }
27
+ export function observeSidecarLockParser(state, admission, accessor, assertCurrent) {
28
+ assertCurrent();
29
+ if (state.observed)
30
+ return;
31
+ try {
32
+ state.parser = admission.run(accessor);
33
+ }
34
+ finally {
35
+ assertCurrent();
36
+ }
37
+ state.observed = true;
38
+ }
39
+ export function scopedSidecarLockParser(state, admission, assertCurrent) {
40
+ const parser = state.parser;
41
+ return parser && ((raw) => admission.run(() => {
42
+ assertCurrent();
43
+ try {
44
+ return Reflect.apply(parser, undefined, [raw]);
45
+ }
46
+ finally {
47
+ assertCurrent();
48
+ }
49
+ }));
50
+ }
51
+ /** Cleanup may outlive its reservation; never invoke user code after authority is lost. */
52
+ export function conditionalSidecarLockParser(state, admission, isCurrent) {
53
+ const parser = state.parser;
54
+ return parser && ((raw) => {
55
+ if (!isCurrent())
56
+ return undefined;
57
+ return admission.run(() => {
58
+ if (!isCurrent())
59
+ return undefined;
60
+ let parsed;
61
+ try {
62
+ parsed = Reflect.apply(parser, undefined, [raw]);
63
+ }
64
+ catch (error) {
65
+ if (!isCurrent())
66
+ return undefined;
67
+ throw error;
68
+ }
69
+ return isCurrent() ? parsed : undefined;
70
+ });
71
+ });
72
+ }
73
+ export function parseSidecarLockSnapshotUnderAdmission(raw, state, admission, assertCurrent) {
74
+ return parseSidecarLockSnapshot(raw, scopedSidecarLockParser(state, admission, assertCurrent));
75
+ }
76
+ /** Emulate the historical EEXIST read/parser boundary without opening another sidecar. */
77
+ export async function observeHeldSidecarParser(params) {
78
+ const holderChanged = {};
79
+ const assertCurrentHolder = () => {
80
+ params.assertToken();
81
+ if (params.currentHeld() !== params.held || params.held.releasePromise) {
82
+ throw holderChanged;
83
+ }
84
+ };
85
+ let transientDenial = false;
86
+ let raw;
87
+ try {
88
+ raw = await params.admission.run(async () => await readSidecarLockRawSnapshot(params.lockPath, {
89
+ lockRoot: params.lockRoot,
90
+ rejectNonFile: true,
91
+ discardObservation: "changed",
92
+ onOpenFailure: (error) => { transientDenial = params.isTransientDenial(error); },
93
+ }));
94
+ }
95
+ catch (error) {
96
+ return { kind: "read-error", error, transientDenial };
97
+ }
98
+ params.assertToken();
99
+ if (params.currentHeld() !== params.held || params.held.releasePromise) {
100
+ return { kind: "holder-changed" };
101
+ }
102
+ try {
103
+ observeSidecarLockParser(params.parserState, params.admission, params.parserAccessor, assertCurrentHolder);
104
+ parseSidecarLockSnapshotUnderAdmission(raw, params.parserState, params.admission, assertCurrentHolder);
105
+ assertCurrentHolder();
106
+ }
107
+ catch (error) {
108
+ if (error === holderChanged)
109
+ return { kind: "holder-changed" };
110
+ throw error;
111
+ }
112
+ return { kind: "observed" };
113
+ }
@@ -0,0 +1,35 @@
1
+ import type { NativeFileHandle } from "./native-operations.js";
2
+ import type { Root } from "./root-impl.js";
3
+ import type { SidecarLockSnapshot } from "./sidecar-lock-reclaim.js";
4
+ import type { SidecarLockHandle } from "./sidecar-lock-types.js";
5
+ type SidecarFileHandle = Pick<NativeFileHandle, "fd" | "close" | "stat" | "writeFile">;
6
+ export type HeldSidecarLock = {
7
+ refCount: number;
8
+ reentrantOwner?: string;
9
+ handle: SidecarFileHandle;
10
+ lockPath: string;
11
+ snapshot: SidecarLockSnapshot;
12
+ acquiredAt: number;
13
+ metadata: Record<string, unknown>;
14
+ releasePromise?: Promise<void>;
15
+ lockRoot?: Root;
16
+ retainOnExit?: boolean;
17
+ parsePayload?: (raw: string) => unknown;
18
+ compromiseTimer?: NodeJS.Timeout;
19
+ };
20
+ export type SidecarLockAcquisitionContext = {
21
+ held: Map<string, HeldSidecarLock>;
22
+ admissions: Map<string, object>;
23
+ reclaimGuards: Set<string>;
24
+ ensureExitCleanupRegistered(): {
25
+ armed: boolean;
26
+ };
27
+ armExitCleanup(lifecycle: {
28
+ armed: boolean;
29
+ }): void;
30
+ assertRetainOnExitSupported(retainOnExit: boolean | undefined): void;
31
+ handleForHeldLock(normalizedTargetPath: string, held: HeldSidecarLock): SidecarLockHandle;
32
+ };
33
+ export declare function sidecarLockTimeout(lockPath: string, normalizedTargetPath: string): Error;
34
+ export {};
35
+ //# sourceMappingURL=sidecar-lock-admission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidecar-lock-admission.d.ts","sourceRoot":"","sources":["../src/sidecar-lock-admission.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,KAAK,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,EAAE,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,WAAW,CAAC,CAAC;AAEvF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,cAAc,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACnC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,2BAA2B,IAAI;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;IAClD,cAAc,CAAC,SAAS,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACpD,2BAA2B,CAAC,YAAY,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC;IACrE,iBAAiB,CAAC,oBAAoB,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,iBAAiB,CAAC;CAC3F,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,GAAG,KAAK,CAMxF"}
@@ -0,0 +1,7 @@
1
+ export function sidecarLockTimeout(lockPath, normalizedTargetPath) {
2
+ return Object.assign(new Error(`file lock timeout for ${normalizedTargetPath}`), {
3
+ code: "file_lock_timeout",
4
+ lockPath,
5
+ normalizedTargetPath,
6
+ });
7
+ }
@@ -38,7 +38,7 @@ export declare function readSidecarLockRawSnapshotSync(lockPath: string, options
38
38
  rejectNonFile?: boolean;
39
39
  onOpenFailure?: (error: unknown) => void;
40
40
  }): SidecarLockRawSnapshot | null;
41
- export declare function removeSidecarLockIfUnchangedSync(lockPath: string, observed: SidecarLockSnapshot): boolean;
41
+ export declare function removeSidecarLockIfUnchangedSync(lockPath: string, observed: SidecarLockSnapshot, assertAuthorized?: () => void): boolean;
42
42
  export declare function sidecarLockSnapshotMatches(current: SidecarLockSnapshot, observed: SidecarLockSnapshot): boolean;
43
43
  export declare function removeSidecarLockIfUnchanged(lockPath: string, observed: SidecarLockSnapshot | null, options?: {
44
44
  lockRoot?: Root;
@@ -48,9 +48,12 @@ export declare function sidecarLockSnapshotStillPresent(lockPath: string, observ
48
48
  lockRoot?: Root;
49
49
  parsePayload?: (raw: string) => unknown;
50
50
  }): Promise<boolean>;
51
- export declare function sidecarReclaimGuardExists(pathname: string): Promise<boolean>;
52
- export declare function tryAcquireSidecarReclaimGuard(reclaimGuards: Set<string>, reclaimGuardPath: string): Promise<boolean>;
53
- export declare function releaseSidecarReclaimGuard(reclaimGuards: Set<string>, reclaimGuardPath: string): Promise<void>;
51
+ export type SidecarReclaimGuard = {
52
+ assertHeld?(): Promise<void>;
53
+ release(): Promise<void>;
54
+ };
55
+ export declare function sidecarReclaimGuardExists(pathname: string, lockRoot?: Root): Promise<boolean>;
56
+ export declare function tryAcquireSidecarReclaimGuard(reclaimGuards: Set<string>, pathname: string, lockRoot?: Root): Promise<SidecarReclaimGuard | undefined>;
54
57
  export declare function removeStaleSidecarLockIfAllowed(params: {
55
58
  lockPath: string;
56
59
  normalizedTargetPath: string;
@@ -58,6 +61,8 @@ export declare function removeStaleSidecarLockIfAllowed(params: {
58
61
  shouldRemoveStaleLock?: (snapshot: SidecarLockStaleSnapshot) => boolean | Promise<boolean>;
59
62
  lockRoot?: Root;
60
63
  parsePayload?: (raw: string) => unknown;
64
+ assertAuthorized?: () => void;
65
+ assertGuardHeld?: () => Promise<void>;
61
66
  }): Promise<"removed" | "changed" | "not-approved">;
62
67
  export {};
63
68
  //# sourceMappingURL=sidecar-lock-reclaim.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sidecar-lock-reclaim.d.ts","sourceRoot":"","sources":["../src/sidecar-lock-reclaim.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sidecar-lock-reclaim.d.ts","sourceRoot":"","sources":["../src/sidecar-lock-reclaim.ts"],"names":[],"mappings":"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"}
@@ -4,10 +4,11 @@ import fs from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import { readFileDescriptorBoundedSync, readFileHandleBounded } from "./bounded-read.js";
6
6
  import { FsSafeError } from "./errors.js";
7
- import { sameFileIdentity } from "./file-identity.js";
7
+ import { sameFileIdentity, sameFileIdentityForCleanup } from "./file-identity.js";
8
8
  import { resolveReadOpenFlags } from "./read-open-flags.js";
9
9
  import { openSidecarRoot } from "./sidecar-lock-root.js";
10
10
  import { getFsSafeTestHooks } from "./test-hooks.js";
11
+ import { sidecarExclusiveCreate } from "./root-create-input.js";
11
12
  const MAX_LOCK_PAYLOAD_BYTES = 1024 * 1024;
12
13
  const SIDECAR_LOCK_OWNERSHIP_TOKEN_BYTES = 16;
13
14
  const SIDECAR_LOCK_OWNERSHIP_TOKEN_BITS = SIDECAR_LOCK_OWNERSHIP_TOKEN_BYTES * 8;
@@ -204,10 +205,13 @@ export function readSidecarLockRawSnapshotSync(lockPath, options = {}) {
204
205
  fsSync.closeSync(fd);
205
206
  }
206
207
  }
207
- export function removeSidecarLockIfUnchangedSync(lockPath, observed) {
208
+ export function removeSidecarLockIfUnchangedSync(lockPath, observed, assertAuthorized) {
209
+ assertAuthorized?.();
208
210
  const current = readSidecarLockSnapshotSync(lockPath);
211
+ assertAuthorized?.();
209
212
  if (!current || !sidecarLockSnapshotMatches(current, observed))
210
213
  return false;
214
+ assertAuthorized?.();
211
215
  fsSync.rmSync(lockPath);
212
216
  return true;
213
217
  }
@@ -220,7 +224,9 @@ export function sidecarLockSnapshotMatches(current, observed) {
220
224
  readSidecarLockOwnershipToken(observed.raw) === observed.ownershipToken &&
221
225
  current.raw === observed.raw);
222
226
  }
223
- if (observed.stat && current.stat && !sameFileIdentity(observed.stat, current.stat)) {
227
+ if (observed.stat &&
228
+ current.stat &&
229
+ !sameFileIdentityForCleanup(observed.stat, current.stat)) {
224
230
  return false;
225
231
  }
226
232
  if (observed.raw !== undefined) {
@@ -251,41 +257,84 @@ export async function sidecarLockSnapshotStillPresent(lockPath, observed, option
251
257
  });
252
258
  return !!current && !!observed && sidecarLockSnapshotMatches(current, observed);
253
259
  }
254
- export async function sidecarReclaimGuardExists(pathname) {
260
+ export async function sidecarReclaimGuardExists(pathname, lockRoot) {
255
261
  try {
256
- fsSync.lstatSync(pathname);
262
+ if (lockRoot)
263
+ await lockRoot.stat(relativeSidecarLockPath(lockRoot, pathname));
264
+ else
265
+ fsSync.lstatSync(pathname);
257
266
  return true;
258
267
  }
259
- catch (err) {
260
- if (err.code === "ENOENT") {
268
+ catch (error) {
269
+ const code = error?.code;
270
+ if (code === "ENOENT" || (lockRoot && code === "not-found"))
261
271
  return false;
262
- }
263
- throw err;
272
+ throw error;
264
273
  }
265
274
  }
266
- export async function tryAcquireSidecarReclaimGuard(reclaimGuards, reclaimGuardPath) {
275
+ export async function tryAcquireSidecarReclaimGuard(reclaimGuards, pathname, lockRoot) {
276
+ if (lockRoot) {
277
+ const relative = relativeSidecarLockPath(lockRoot, pathname);
278
+ const payload = { pid: process.pid, createdAt: new Date().toISOString() };
279
+ const snapshot = { ...serializeSidecarLockPayload(payload), payload };
280
+ try {
281
+ await lockRoot.create(relative, snapshot.raw, { ...sidecarExclusiveCreate, mkdir: false, mode: 0o600, durable: false });
282
+ }
283
+ catch (error) {
284
+ if (error instanceof FsSafeError && error.code === "already-exists")
285
+ return;
286
+ // A raw reclaimer can create its directory after our occupancy check.
287
+ if (error instanceof FsSafeError && error.code === "not-file") {
288
+ await sidecarReclaimGuardExists(pathname, lockRoot);
289
+ return;
290
+ }
291
+ throw error;
292
+ }
293
+ // This attempt owns only its minted token and exact bytes. Root guards
294
+ // never enter raw exit cleanup; interrupted/ambiguous operations preserve them.
295
+ let released = false;
296
+ const changed = () => new FsSafeError("path-mismatch", "sidecar reclaim guard ownership changed");
297
+ return {
298
+ async assertHeld() {
299
+ if (released || !await sidecarLockSnapshotStillPresent(pathname, snapshot, { lockRoot }))
300
+ throw changed();
301
+ },
302
+ async release() {
303
+ if (released)
304
+ return;
305
+ if (!await removeSidecarLockIfUnchanged(pathname, snapshot, { lockRoot }))
306
+ throw changed();
307
+ released = true;
308
+ },
309
+ };
310
+ }
267
311
  try {
268
- await fs.mkdir(reclaimGuardPath);
269
- reclaimGuards.add(reclaimGuardPath);
270
- return true;
312
+ await fs.mkdir(pathname);
271
313
  }
272
- catch (err) {
273
- if (err.code === "EEXIST") {
274
- return false;
275
- }
276
- throw err;
314
+ catch (error) {
315
+ if (error?.code === "EEXIST")
316
+ return;
317
+ throw error;
277
318
  }
278
- }
279
- export async function releaseSidecarReclaimGuard(reclaimGuards, reclaimGuardPath) {
280
- await fs.rmdir(reclaimGuardPath);
281
- reclaimGuards.delete(reclaimGuardPath);
319
+ reclaimGuards.add(pathname);
320
+ let released = false;
321
+ return { async release() {
322
+ if (released)
323
+ return;
324
+ await fs.rmdir(pathname);
325
+ reclaimGuards.delete(pathname);
326
+ released = true;
327
+ } };
282
328
  }
283
329
  export async function removeStaleSidecarLockIfAllowed(params) {
284
330
  if (!params.shouldRemoveStaleLock || params.snapshot.raw === undefined) {
285
331
  return "not-approved";
286
332
  }
287
333
  const ioOptions = { lockRoot: params.lockRoot, parsePayload: params.parsePayload };
288
- if (!(await sidecarLockSnapshotStillPresent(params.lockPath, params.snapshot, ioOptions))) {
334
+ params.assertAuthorized?.();
335
+ const presentBeforeApproval = await sidecarLockSnapshotStillPresent(params.lockPath, params.snapshot, ioOptions);
336
+ params.assertAuthorized?.();
337
+ if (!presentBeforeApproval) {
289
338
  return "changed";
290
339
  }
291
340
  if (!(await params.shouldRemoveStaleLock({
@@ -296,12 +345,18 @@ export async function removeStaleSidecarLockIfAllowed(params) {
296
345
  }))) {
297
346
  return "not-approved";
298
347
  }
299
- if (!(await sidecarLockSnapshotStillPresent(params.lockPath, params.snapshot, ioOptions))) {
348
+ params.assertAuthorized?.();
349
+ const presentBeforeRemoval = await sidecarLockSnapshotStillPresent(params.lockPath, params.snapshot, ioOptions);
350
+ params.assertAuthorized?.();
351
+ if (!presentBeforeRemoval) {
300
352
  return "changed";
301
353
  }
354
+ if (params.assertGuardHeld)
355
+ await params.assertGuardHeld();
356
+ params.assertAuthorized?.();
302
357
  try {
303
358
  if (params.lockRoot) {
304
- await params.lockRoot.remove(relativeSidecarLockPath(params.lockRoot, params.lockPath));
359
+ await params.lockRoot.remove(relativeSidecarLockPath(params.lockRoot, params.lockPath), { assertBeforeMutation: params.assertAuthorized });
305
360
  }
306
361
  else {
307
362
  await fs.rm(params.lockPath);
@@ -0,0 +1,39 @@
1
+ import { type SidecarReclaimGuard } from "./sidecar-lock-reclaim.js";
2
+ import { type SidecarLockParserState } from "./sidecar-lock-admission-parser.js";
3
+ import type { Root } from "./root-impl.js";
4
+ import type { SidecarLockAcquireOptions } from "./sidecar-lock-types.js";
5
+ type SidecarAdmissionRunner = {
6
+ hasToken(): boolean;
7
+ run<T>(callback: () => T): T;
8
+ };
9
+ type StaleOptions = Pick<SidecarLockAcquireOptions<Record<string, unknown>>, "shouldReclaim" | "shouldRemoveStaleLock" | "staleRecovery">;
10
+ export type SidecarLockStaleOptionsState = {
11
+ shouldReclaimObserved?: boolean;
12
+ shouldReclaim?: StaleOptions["shouldReclaim"];
13
+ shouldRemoveObserved?: boolean;
14
+ shouldRemove?: StaleOptions["shouldRemoveStaleLock"];
15
+ staleRecoveryObserved?: boolean;
16
+ staleRecovery?: StaleOptions["staleRecovery"];
17
+ };
18
+ /** Handle one historical EEXIST attempt while its canonical admission is held. */
19
+ export declare function handleStaleSidecarAdmission<TPayload extends Record<string, unknown>>(params: {
20
+ admission: SidecarAdmissionRunner;
21
+ assertToken(): void;
22
+ currentHeld(): unknown;
23
+ lockPath: string;
24
+ lockRoot?: Root;
25
+ normalizedTargetPath: string;
26
+ options: SidecarLockAcquireOptions<TPayload>;
27
+ parserState: SidecarLockParserState;
28
+ reclaimGuardPath: string;
29
+ reclaimGuards: Set<string>;
30
+ setReclaimGuard(guard: SidecarReclaimGuard): void;
31
+ releaseReclaimGuard(): Promise<void>;
32
+ staleOptionsState: SidecarLockStaleOptionsState;
33
+ staleMs: number;
34
+ withinDenialBudget(): boolean;
35
+ retryOrRethrowDenial(error: unknown): Promise<void>;
36
+ waitForRetry(): Promise<void>;
37
+ }): Promise<void>;
38
+ export {};
39
+ //# sourceMappingURL=sidecar-lock-stale-admission.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidecar-lock-stale-admission.d.ts","sourceRoot":"","sources":["../src/sidecar-lock-stale-admission.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,mBAAmB,EACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAIL,KAAK,sBAAsB,EAC5B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAEzE,KAAK,sBAAsB,GAAG;IAC5B,QAAQ,IAAI,OAAO,CAAC;IACpB,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;CAC9B,CAAC;AAEF,KAAK,YAAY,GAAG,IAAI,CACtB,yBAAyB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAClD,eAAe,GAAG,uBAAuB,GAAG,eAAe,CAC5D,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,aAAa,CAAC,EAAE,YAAY,CAAC,eAAe,CAAC,CAAC;IAC9C,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,YAAY,CAAC,EAAE,YAAY,CAAC,uBAAuB,CAAC,CAAC;IACrD,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,aAAa,CAAC,EAAE,YAAY,CAAC,eAAe,CAAC,CAAC;CAC/C,CAAC;AAEF,kFAAkF;AAClF,wBAAsB,2BAA2B,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxF,MAAM,EAAE;IACN,SAAS,EAAE,sBAAsB,CAAC;IAClC,WAAW,IAAI,IAAI,CAAC;IACpB,WAAW,IAAI,OAAO,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,OAAO,EAAE,yBAAyB,CAAC,QAAQ,CAAC,CAAC;IAC7C,WAAW,EAAE,sBAAsB,CAAC;IACpC,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,eAAe,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAClD,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,iBAAiB,EAAE,4BAA4B,CAAC;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,IAAI,OAAO,CAAC;IAC9B,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B,GACA,OAAO,CAAC,IAAI,CAAC,CA0Nf"}