@narumitw/pi-sync 0.49.15 → 0.50.1

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 (213) hide show
  1. package/README.md +49 -16
  2. package/dist/chunks/attention-widget-Z7G3SYYJ.ts +9 -0
  3. package/dist/chunks/attention-widget-Z7G3SYYJ.ts.map +7 -0
  4. package/dist/chunks/chunk-2B2ELUF5.ts +29 -0
  5. package/dist/chunks/chunk-2B2ELUF5.ts.map +7 -0
  6. package/dist/chunks/{chunk-ARN2672F.ts → chunk-4CHTZMEO.ts} +23 -55
  7. package/dist/chunks/chunk-4CHTZMEO.ts.map +7 -0
  8. package/dist/chunks/chunk-6LP3MCR2.ts +115 -0
  9. package/dist/chunks/chunk-6LP3MCR2.ts.map +7 -0
  10. package/dist/chunks/{chunk-JF6MZCT4.ts → chunk-AUSDLD6F.ts} +4 -4
  11. package/dist/chunks/chunk-AUSDLD6F.ts.map +7 -0
  12. package/dist/chunks/{chunk-5T442OP3.ts → chunk-C7XY5NF5.ts} +210 -373
  13. package/dist/chunks/chunk-C7XY5NF5.ts.map +7 -0
  14. package/dist/chunks/chunk-CCSN3F47.ts +51 -0
  15. package/dist/chunks/chunk-CCSN3F47.ts.map +7 -0
  16. package/dist/chunks/{chunk-4U6AFLJB.ts → chunk-CHH26D4F.ts} +3 -2
  17. package/dist/chunks/chunk-CHH26D4F.ts.map +7 -0
  18. package/dist/chunks/chunk-CPEHGXJV.ts +50 -0
  19. package/dist/chunks/chunk-CPEHGXJV.ts.map +7 -0
  20. package/dist/chunks/{chunk-5MQ76BK5.ts → chunk-D5X27LCA.ts} +2 -2
  21. package/dist/chunks/chunk-D5X27LCA.ts.map +7 -0
  22. package/dist/chunks/{chunk-WSOBYHIZ.ts → chunk-ETTRJDNP.ts} +4 -4
  23. package/dist/chunks/chunk-ETTRJDNP.ts.map +7 -0
  24. package/dist/chunks/{chunk-44LPGQTI.ts → chunk-G4RNQQZS.ts} +9 -7
  25. package/dist/chunks/chunk-G4RNQQZS.ts.map +7 -0
  26. package/dist/chunks/{chunk-5LZALPCD.ts → chunk-HUDQATCL.ts} +25 -25
  27. package/dist/chunks/chunk-HUDQATCL.ts.map +7 -0
  28. package/dist/chunks/{chunk-X7SQ2EQM.ts → chunk-IJ6FJFA5.ts} +9 -9
  29. package/dist/chunks/chunk-IJ6FJFA5.ts.map +7 -0
  30. package/dist/chunks/{chunk-YQ6UW7IF.ts → chunk-JAAKLL6M.ts} +2 -2
  31. package/dist/chunks/chunk-JAAKLL6M.ts.map +7 -0
  32. package/dist/chunks/chunk-KW7YQEZN.ts +1255 -0
  33. package/dist/chunks/chunk-KW7YQEZN.ts.map +7 -0
  34. package/dist/chunks/{chunk-DFXBKHGN.ts → chunk-LCRI3ABW.ts} +21 -8
  35. package/dist/chunks/chunk-LCRI3ABW.ts.map +7 -0
  36. package/dist/chunks/chunk-LTBWGSBV.ts +12 -0
  37. package/dist/chunks/chunk-LTBWGSBV.ts.map +7 -0
  38. package/dist/chunks/chunk-NJFUIVYL.ts +17 -0
  39. package/dist/chunks/chunk-NJFUIVYL.ts.map +7 -0
  40. package/dist/chunks/{chunk-HVUSHMVL.ts → chunk-OEX4ABY6.ts} +39 -37
  41. package/dist/chunks/chunk-OEX4ABY6.ts.map +7 -0
  42. package/dist/chunks/{chunk-HGV24P4T.ts → chunk-Q2TUM5X6.ts} +3 -3
  43. package/dist/chunks/chunk-Q2TUM5X6.ts.map +7 -0
  44. package/dist/chunks/chunk-S7YBZR42.ts +146 -0
  45. package/dist/chunks/chunk-S7YBZR42.ts.map +7 -0
  46. package/dist/chunks/{chunk-GLQLN5GV.ts → chunk-SL64RLXR.ts} +8 -6
  47. package/dist/chunks/chunk-SL64RLXR.ts.map +7 -0
  48. package/dist/chunks/chunk-SZWBGPID.ts +57 -0
  49. package/dist/chunks/chunk-SZWBGPID.ts.map +7 -0
  50. package/dist/chunks/chunk-T2B5IU74.ts +25 -0
  51. package/dist/chunks/chunk-T2B5IU74.ts.map +7 -0
  52. package/dist/chunks/chunk-T3HH3UEJ.ts +96 -0
  53. package/dist/chunks/chunk-T3HH3UEJ.ts.map +7 -0
  54. package/dist/chunks/chunk-TEN2ZBJV.ts +101 -0
  55. package/dist/chunks/chunk-TEN2ZBJV.ts.map +7 -0
  56. package/dist/chunks/chunk-THMF4BVL.ts +18 -0
  57. package/dist/chunks/chunk-THMF4BVL.ts.map +7 -0
  58. package/dist/chunks/chunk-Z4DQF6L3.ts +12 -0
  59. package/dist/chunks/chunk-Z4DQF6L3.ts.map +7 -0
  60. package/dist/chunks/{chunk-F5QL2GPY.ts → chunk-ZI5MXMEX.ts} +5 -99
  61. package/dist/chunks/chunk-ZI5MXMEX.ts.map +7 -0
  62. package/dist/chunks/{file-selection-RKEHALNQ.ts → file-selection-FTTKAOYX.ts} +10 -7
  63. package/dist/chunks/file-selection-FTTKAOYX.ts.map +7 -0
  64. package/dist/chunks/{git-backend-E3UOUUOM.ts → git-backend-GMVS4PD4.ts} +39 -14
  65. package/dist/chunks/git-backend-GMVS4PD4.ts.map +7 -0
  66. package/dist/chunks/manager-result-dispatcher-FKIBM3GO.ts +22 -0
  67. package/dist/chunks/manager-ui-R3XPUFFS.ts +1680 -0
  68. package/dist/chunks/manager-ui-R3XPUFFS.ts.map +7 -0
  69. package/dist/chunks/remote-selection-ui-D7RBRJNL.ts +21 -0
  70. package/dist/chunks/{s3-backend-552LXHE4.ts → s3-backend-66O4FGBH.ts} +94 -16
  71. package/dist/chunks/s3-backend-66O4FGBH.ts.map +7 -0
  72. package/dist/chunks/{setup-switch-5YDG5YGJ.ts → setup-switch-IS2PEZ3J.ts} +9 -9
  73. package/dist/chunks/setup-wizard-IUB6ESEU.ts +89 -0
  74. package/dist/chunks/setup-wizard-IUB6ESEU.ts.map +7 -0
  75. package/dist/chunks/{snapshot-2RYBUDRA.ts → snapshot-HKEWCVK6.ts} +7 -6
  76. package/dist/chunks/sync-inspection-CST2KL65.ts +20 -0
  77. package/dist/chunks/{sync-operations-3LLIHWZO.ts → sync-operations-LBX3GKCM.ts} +369 -289
  78. package/dist/chunks/sync-operations-LBX3GKCM.ts.map +7 -0
  79. package/dist/chunks/{sync-state-SRN6YRBK.ts → sync-state-Q2FTXTW6.ts} +6 -6
  80. package/dist/chunks/sync-state-Q2FTXTW6.ts.map +7 -0
  81. package/dist/chunks/{webdav-backend-ZD5RHRIU.ts → webdav-backend-ECIKD7SO.ts} +26 -13
  82. package/dist/chunks/webdav-backend-ECIKD7SO.ts.map +7 -0
  83. package/dist/index.ts +476 -341
  84. package/dist/index.ts.map +4 -4
  85. package/docs/settings.md +30 -6
  86. package/package.json +1 -1
  87. package/src/{backend-factory.ts → backends/backend-factory.ts} +4 -4
  88. package/src/backends/backend-identity.ts +40 -0
  89. package/src/backends/backend-types.ts +81 -0
  90. package/src/{git-backend.ts → backends/git/git-backend.ts} +43 -19
  91. package/src/{git-config.ts → backends/git/git-config.ts} +1 -0
  92. package/src/{git-storage.ts → backends/git/git-storage.ts} +2 -2
  93. package/src/{s3-backend.ts → backends/s3/s3-backend.ts} +45 -9
  94. package/src/{s3-client.ts → backends/s3/s3-client.ts} +56 -8
  95. package/src/{sync-backend.ts → backends/sync-backend.ts} +1 -1
  96. package/src/{webdav-backend.ts → backends/webdav/webdav-backend.ts} +22 -10
  97. package/src/{webdav-client.ts → backends/webdav/webdav-client.ts} +1 -1
  98. package/src/{webdav-config.ts → backends/webdav/webdav-config.ts} +1 -0
  99. package/src/commands/command-execution.ts +330 -0
  100. package/src/commands/command-handler.ts +201 -0
  101. package/src/commands/command-types.ts +18 -0
  102. package/src/{command.ts → commands/command.ts} +2 -1
  103. package/src/commands/setup-completions.ts +6 -0
  104. package/src/settings/config.ts +238 -0
  105. package/src/{settings-management.ts → settings/settings-management.ts} +39 -14
  106. package/src/settings/settings-store.ts +109 -0
  107. package/src/settings/settings-types.ts +128 -0
  108. package/src/settings/settings-validation.ts +443 -0
  109. package/src/snapshot/session-paths.ts +84 -0
  110. package/src/{snapshot-apply.ts → snapshot/snapshot-apply.ts} +3 -3
  111. package/src/{snapshot-codec.ts → snapshot/snapshot-codec.ts} +2 -2
  112. package/src/{snapshot-transaction.ts → snapshot/snapshot-transaction.ts} +5 -4
  113. package/src/snapshot/snapshot-types.ts +38 -0
  114. package/src/{snapshot.ts → snapshot/snapshot.ts} +39 -20
  115. package/src/state/json-file.ts +20 -0
  116. package/src/{lock.ts → state/lock.ts} +3 -2
  117. package/src/{operation-availability.ts → state/operation-availability.ts} +2 -2
  118. package/src/state/state-types.ts +20 -0
  119. package/src/state/sync-state-store.ts +62 -0
  120. package/src/sync/automatic-sync.ts +103 -0
  121. package/src/{remote-snapshot.ts → sync/remote-snapshot.ts} +27 -4
  122. package/src/{setup-switch.ts → sync/setup-switch.ts} +6 -7
  123. package/src/sync/signals.ts +10 -0
  124. package/src/sync/startup-check.ts +128 -0
  125. package/src/{sync-decision.ts → sync/sync-decision.ts} +6 -3
  126. package/src/sync/sync-error-guidance.ts +53 -0
  127. package/src/sync/sync-inspection.ts +51 -0
  128. package/src/sync/sync-loaders.ts +54 -0
  129. package/src/{sync-operations.ts → sync/sync-mutations.ts} +34 -325
  130. package/src/sync/sync-operations.ts +12 -0
  131. package/src/{sync-policy.ts → sync/sync-policy.ts} +2 -2
  132. package/src/sync/sync-queries.ts +235 -0
  133. package/src/{sync-state.ts → sync/sync-state.ts} +5 -4
  134. package/src/sync-extension.ts +64 -708
  135. package/src/sync.ts +8 -8
  136. package/src/types.ts +47 -286
  137. package/src/ui/attention-widget.ts +1 -0
  138. package/src/ui/automatic-sync-summary.ts +6 -0
  139. package/src/{cancellable-operation.ts → ui/cancellable-operation.ts} +4 -4
  140. package/src/{file-selection.ts → ui/file-selection.ts} +5 -3
  141. package/src/{manager-attention.ts → ui/manager-attention.ts} +5 -1
  142. package/src/{manager-recovery.ts → ui/manager-recovery.ts} +6 -2
  143. package/src/{manager-state.ts → ui/manager-state.ts} +35 -14
  144. package/src/ui/manager-ui.ts +386 -0
  145. package/src/{remote-selection-ui.ts → ui/remote-selection-ui.ts} +21 -13
  146. package/src/{secret-input.ts → ui/secret-input.ts} +22 -4
  147. package/src/{settings-ui.ts → ui/settings-ui.ts} +17 -13
  148. package/src/ui/setup/git-ui.ts +293 -0
  149. package/src/{s3-credentials-ui.ts → ui/setup/s3-credentials-ui.ts} +13 -21
  150. package/src/ui/setup/s3-ui.ts +217 -0
  151. package/src/ui/setup/setup-actions.ts +127 -0
  152. package/src/ui/setup/setup-location-ui.ts +167 -0
  153. package/src/ui/setup/setup-name-ui.ts +34 -0
  154. package/src/ui/setup/setup-prompts.ts +92 -0
  155. package/src/ui/setup/setup-review.ts +70 -0
  156. package/src/ui/setup/setup-switcher.ts +181 -0
  157. package/src/ui/setup/setup-wizard.ts +39 -0
  158. package/src/ui/setup/text-input.ts +78 -0
  159. package/src/ui/setup/webdav-ui.ts +330 -0
  160. package/src/{storage-connections-ui.ts → ui/storage-connections-ui.ts} +99 -69
  161. package/src/ui/sync-attention.ts +187 -0
  162. package/src/{sync-format.ts → ui/sync-format.ts} +7 -13
  163. package/src/{sync-resolution-ui.ts → ui/sync-resolution-ui.ts} +4 -3
  164. package/src/{sync-setups-ui.ts → ui/sync-setups-ui.ts} +23 -10
  165. package/src/ui/terminal-text.ts +4 -0
  166. package/dist/chunks/chunk-44LPGQTI.ts.map +0 -7
  167. package/dist/chunks/chunk-4U6AFLJB.ts.map +0 -7
  168. package/dist/chunks/chunk-5LZALPCD.ts.map +0 -7
  169. package/dist/chunks/chunk-5MQ76BK5.ts.map +0 -7
  170. package/dist/chunks/chunk-5T442OP3.ts.map +0 -7
  171. package/dist/chunks/chunk-ARN2672F.ts.map +0 -7
  172. package/dist/chunks/chunk-C7LXZ3UH.ts +0 -167
  173. package/dist/chunks/chunk-C7LXZ3UH.ts.map +0 -7
  174. package/dist/chunks/chunk-DFXBKHGN.ts.map +0 -7
  175. package/dist/chunks/chunk-E4COSV32.ts +0 -82
  176. package/dist/chunks/chunk-E4COSV32.ts.map +0 -7
  177. package/dist/chunks/chunk-F5QL2GPY.ts.map +0 -7
  178. package/dist/chunks/chunk-GLQLN5GV.ts.map +0 -7
  179. package/dist/chunks/chunk-HGV24P4T.ts.map +0 -7
  180. package/dist/chunks/chunk-HVUSHMVL.ts.map +0 -7
  181. package/dist/chunks/chunk-JF6MZCT4.ts.map +0 -7
  182. package/dist/chunks/chunk-WSOBYHIZ.ts.map +0 -7
  183. package/dist/chunks/chunk-X7SQ2EQM.ts.map +0 -7
  184. package/dist/chunks/chunk-YQ6UW7IF.ts.map +0 -7
  185. package/dist/chunks/file-selection-RKEHALNQ.ts.map +0 -7
  186. package/dist/chunks/git-backend-E3UOUUOM.ts.map +0 -7
  187. package/dist/chunks/manager-result-dispatcher-LWPYJAD5.ts +0 -18
  188. package/dist/chunks/manager-ui-NF4RUHBY.ts +0 -2785
  189. package/dist/chunks/manager-ui-NF4RUHBY.ts.map +0 -7
  190. package/dist/chunks/remote-selection-ui-NUF5P4ZJ.ts +0 -17
  191. package/dist/chunks/s3-backend-552LXHE4.ts.map +0 -7
  192. package/dist/chunks/sync-operations-3LLIHWZO.ts.map +0 -7
  193. package/dist/chunks/webdav-backend-ZD5RHRIU.ts.map +0 -7
  194. package/src/config.ts +0 -923
  195. package/src/git-ui.ts +0 -307
  196. package/src/manager-helpers.ts +0 -64
  197. package/src/manager-ui.ts +0 -997
  198. package/src/sync-attention.ts +0 -100
  199. package/src/webdav-ui.ts +0 -421
  200. /package/dist/chunks/{manager-result-dispatcher-LWPYJAD5.ts.map → manager-result-dispatcher-FKIBM3GO.ts.map} +0 -0
  201. /package/dist/chunks/{remote-selection-ui-NUF5P4ZJ.ts.map → remote-selection-ui-D7RBRJNL.ts.map} +0 -0
  202. /package/dist/chunks/{setup-switch-5YDG5YGJ.ts.map → setup-switch-IS2PEZ3J.ts.map} +0 -0
  203. /package/dist/chunks/{snapshot-2RYBUDRA.ts.map → snapshot-HKEWCVK6.ts.map} +0 -0
  204. /package/dist/chunks/{sync-state-SRN6YRBK.ts.map → sync-inspection-CST2KL65.ts.map} +0 -0
  205. /package/src/{git-runner.ts → backends/git/git-runner.ts} +0 -0
  206. /package/src/{config-errors.ts → settings/config-errors.ts} +0 -0
  207. /package/src/{config-file.ts → settings/config-file.ts} +0 -0
  208. /package/src/{snapshot-paths.ts → snapshot/snapshot-paths.ts} +0 -0
  209. /package/src/{lock-policy.ts → state/lock-policy.ts} +0 -0
  210. /package/src/{lockfile-fs.ts → state/lockfile-fs.ts} +0 -0
  211. /package/src/{state-directory.ts → state/state-directory.ts} +0 -0
  212. /package/src/{sync-errors.ts → sync/sync-errors.ts} +0 -0
  213. /package/src/{manager-result-dispatcher.ts → ui/manager-result-dispatcher.ts} +0 -0
@@ -2,36 +2,21 @@
2
2
  // @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
3
3
  import {
4
4
  safeTerminalText
5
- } from "./chunk-C7LXZ3UH.ts";
5
+ } from "./chunk-LTBWGSBV.ts";
6
6
  import {
7
7
  syncConfigReviewFingerprint
8
- } from "./chunk-5T442OP3.ts";
8
+ } from "./chunk-C7XY5NF5.ts";
9
+ import {
10
+ filterSnapshotForConfigPolicy
11
+ } from "./chunk-OEX4ABY6.ts";
9
12
  import {
10
13
  inspectRemoteSelection,
11
14
  remoteSelectionMismatch,
12
15
  sameSyncInclude,
13
16
  snapshotSelectionInclude
14
- } from "./chunk-YQ6UW7IF.ts";
17
+ } from "./chunk-JAAKLL6M.ts";
15
18
 
16
- // src/backend-factory.ts
17
- var createSyncBackend = async (config) => {
18
- switch (config.backend.type) {
19
- case "s3": {
20
- const { S3SyncBackend } = await import("./s3-backend-552LXHE4.ts");
21
- return new S3SyncBackend(config.backend);
22
- }
23
- case "webdav": {
24
- const { WebDavSyncBackend } = await import("./webdav-backend-ZD5RHRIU.ts");
25
- return new WebDavSyncBackend(config.backend);
26
- }
27
- case "git": {
28
- const { GitSyncBackend } = await import("./git-backend-E3UOUUOM.ts");
29
- return new GitSyncBackend(config.backend);
30
- }
31
- }
32
- };
33
-
34
- // src/remote-snapshot.ts
19
+ // src/sync/remote-snapshot.ts
35
20
  async function readSnapshotForHead(backend, head, signal) {
36
21
  const snapshot = await backend.readSnapshot(head.snapshotRef, signal);
37
22
  if (signal?.aborted) {
@@ -71,11 +56,26 @@ function formatRemoteSelectionStatus(state) {
71
56
  function safeList(values) {
72
57
  return values.length > 0 ? values.map(safeTerminalText).join(", ") : "none";
73
58
  }
59
+ async function readRemoteSnapshot(backend, config, signal, options = {}) {
60
+ const head = await backend.readHead(signal);
61
+ if (!head) return { head: void 0, snapshot: void 0, selectionState: void 0 };
62
+ const snapshot = await readSnapshotForHead(backend, head, signal);
63
+ const selectionState = inspectRemoteSelection(config.include, snapshot);
64
+ if (!options.allowSelectionDifference && selectionState.kind === "different") {
65
+ const configIdentity = syncConfigReviewFingerprint(config);
66
+ throw remoteSelectionMismatch(config, selectionState.include, configIdentity);
67
+ }
68
+ return {
69
+ head,
70
+ snapshot: filterSnapshotForConfigPolicy(snapshot, config),
71
+ selectionState
72
+ };
73
+ }
74
74
 
75
75
  export {
76
- createSyncBackend,
77
76
  readSnapshotForHead,
78
77
  requireCompatibleRemoteSelection,
79
- formatRemoteSelectionStatus
78
+ formatRemoteSelectionStatus,
79
+ readRemoteSnapshot
80
80
  };
81
- //# sourceMappingURL=chunk-5LZALPCD.ts.map
81
+ //# sourceMappingURL=chunk-HUDQATCL.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/sync/remote-snapshot.ts"],
4
+ "sourcesContent": ["import type { RemoteHead, SyncBackend } from \"../backends/sync-backend.js\";\nimport { syncConfigReviewFingerprint } from \"../settings/config.js\";\nimport type { AnySyncConfig } from \"../settings/settings-types.js\";\nimport { filterSnapshotForConfigPolicy } from \"../snapshot/snapshot.js\";\nimport type { Snapshot } from \"../snapshot/snapshot-types.js\";\nimport { safeTerminalText } from \"../ui/terminal-text.js\";\nimport {\n\tinspectRemoteSelection,\n\ttype RemoteSelectionState,\n\tremoteSelectionMismatch,\n\tsameSyncInclude,\n\tsnapshotSelectionInclude,\n} from \"./sync-policy.js\";\n\nexport async function readSnapshotForHead(\n\tbackend: SyncBackend,\n\thead: RemoteHead,\n\tsignal?: AbortSignal,\n) {\n\tconst snapshot = await backend.readSnapshot(head.snapshotRef, signal);\n\tif (signal?.aborted) {\n\t\tthrow signal.reason instanceof Error\n\t\t\t? signal.reason\n\t\t\t: new DOMException(\"The operation was aborted\", \"AbortError\");\n\t}\n\tif (snapshot.id !== head.snapshotId) {\n\t\tthrow new Error(\n\t\t\t`Remote head ${head.snapshotId} resolved to unexpected snapshot ${snapshot.id}.`,\n\t\t);\n\t}\n\tif (head.selection) {\n\t\tconst snapshotInclude = snapshotSelectionInclude(snapshot);\n\t\tif (!snapshotInclude || !sameSyncInclude(head.selection.include, snapshotInclude)) {\n\t\t\tthrow new Error(\"Remote head selection does not match its immutable snapshot.\");\n\t\t}\n\t}\n\treturn snapshot;\n}\n\nexport function requireCompatibleRemoteSelection(config: AnySyncConfig, snapshot: Snapshot) {\n\tconst state = inspectRemoteSelection(config.include, snapshot);\n\tif (state.kind === \"different\") {\n\t\tthrow remoteSelectionMismatch(config, state.include, syncConfigReviewFingerprint(config));\n\t}\n}\n\nexport function formatRemoteSelectionStatus(state: RemoteSelectionState | undefined) {\n\tif (!state) return \"remote included content: unavailable (remote is empty)\";\n\tif (state.kind === \"same\") return \"remote included content: matches this setup\";\n\tif (state.kind === \"legacy\") {\n\t\treturn `remote included content: unavailable in legacy snapshot (${state.discovered.length} path${state.discovered.length === 1 ? \"\" : \"s\"} discovered, partial)`;\n\t}\n\treturn [\n\t\t\"remote included content: differs from this setup\",\n\t\t`remote-only selection: ${safeList(state.remoteOnly)}`,\n\t\t`local-only selection: ${safeList(state.localOnly)}`,\n\t].join(\"\\n\");\n}\n\nfunction safeList(values: readonly string[]) {\n\treturn values.length > 0 ? values.map(safeTerminalText).join(\", \") : \"none\";\n}\n\nexport async function readRemoteSnapshot(\n\tbackend: SyncBackend,\n\tconfig: AnySyncConfig,\n\tsignal?: AbortSignal,\n\toptions: { allowSelectionDifference?: boolean } = {},\n) {\n\tconst head = await backend.readHead(signal);\n\tif (!head) return { head: undefined, snapshot: undefined, selectionState: undefined };\n\tconst snapshot = await readSnapshotForHead(backend, head, signal);\n\tconst selectionState = inspectRemoteSelection(config.include, snapshot);\n\tif (!options.allowSelectionDifference && selectionState.kind === \"different\") {\n\t\tconst configIdentity = syncConfigReviewFingerprint(config);\n\t\tthrow remoteSelectionMismatch(config, selectionState.include, configIdentity);\n\t}\n\treturn {\n\t\thead,\n\t\tsnapshot: filterSnapshotForConfigPolicy(snapshot, config),\n\t\tselectionState,\n\t};\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;AAcA,eAAsB,oBACrB,SACA,MACA,QACC;AACD,QAAM,WAAW,MAAM,QAAQ,aAAa,KAAK,aAAa,MAAM;AACpE,MAAI,QAAQ,SAAS;AACpB,UAAM,OAAO,kBAAkB,QAC5B,OAAO,SACP,IAAI,aAAa,6BAA6B,YAAY;AAAA,EAC9D;AACA,MAAI,SAAS,OAAO,KAAK,YAAY;AACpC,UAAM,IAAI;AAAA,MACT,eAAe,KAAK,UAAU,oCAAoC,SAAS,EAAE;AAAA,IAC9E;AAAA,EACD;AACA,MAAI,KAAK,WAAW;AACnB,UAAM,kBAAkB,yBAAyB,QAAQ;AACzD,QAAI,CAAC,mBAAmB,CAAC,gBAAgB,KAAK,UAAU,SAAS,eAAe,GAAG;AAClF,YAAM,IAAI,MAAM,8DAA8D;AAAA,IAC/E;AAAA,EACD;AACA,SAAO;AACR;AAEO,SAAS,iCAAiC,QAAuB,UAAoB;AAC3F,QAAM,QAAQ,uBAAuB,OAAO,SAAS,QAAQ;AAC7D,MAAI,MAAM,SAAS,aAAa;AAC/B,UAAM,wBAAwB,QAAQ,MAAM,SAAS,4BAA4B,MAAM,CAAC;AAAA,EACzF;AACD;AAEO,SAAS,4BAA4B,OAAyC;AACpF,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,MAAM,SAAS,OAAQ,QAAO;AAClC,MAAI,MAAM,SAAS,UAAU;AAC5B,WAAO,4DAA4D,MAAM,WAAW,MAAM,QAAQ,MAAM,WAAW,WAAW,IAAI,KAAK,GAAG;AAAA,EAC3I;AACA,SAAO;AAAA,IACN;AAAA,IACA,0BAA0B,SAAS,MAAM,UAAU,CAAC;AAAA,IACpD,yBAAyB,SAAS,MAAM,SAAS,CAAC;AAAA,EACnD,EAAE,KAAK,IAAI;AACZ;AAEA,SAAS,SAAS,QAA2B;AAC5C,SAAO,OAAO,SAAS,IAAI,OAAO,IAAI,gBAAgB,EAAE,KAAK,IAAI,IAAI;AACtE;AAEA,eAAsB,mBACrB,SACA,QACA,QACA,UAAkD,CAAC,GAClD;AACD,QAAM,OAAO,MAAM,QAAQ,SAAS,MAAM;AAC1C,MAAI,CAAC,KAAM,QAAO,EAAE,MAAM,QAAW,UAAU,QAAW,gBAAgB,OAAU;AACpF,QAAM,WAAW,MAAM,oBAAoB,SAAS,MAAM,MAAM;AAChE,QAAM,iBAAiB,uBAAuB,OAAO,SAAS,QAAQ;AACtE,MAAI,CAAC,QAAQ,4BAA4B,eAAe,SAAS,aAAa;AAC7E,UAAM,iBAAiB,4BAA4B,MAAM;AACzD,UAAM,wBAAwB,QAAQ,eAAe,SAAS,cAAc;AAAA,EAC7E;AACA,SAAO;AAAA,IACN;AAAA,IACA,UAAU,8BAA8B,UAAU,MAAM;AAAA,IACxD;AAAA,EACD;AACD;",
6
+ "names": []
7
+ }
@@ -2,22 +2,22 @@
2
2
  // @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
3
3
  import {
4
4
  withLock
5
- } from "./chunk-WSOBYHIZ.ts";
5
+ } from "./chunk-ETTRJDNP.ts";
6
+ import {
7
+ stateDir
8
+ } from "./chunk-ZI5MXMEX.ts";
6
9
  import {
7
10
  sessionStorageRoot
8
- } from "./chunk-HVUSHMVL.ts";
11
+ } from "./chunk-THMF4BVL.ts";
9
12
  import {
10
13
  agentDir
11
- } from "./chunk-5T442OP3.ts";
12
- import {
13
- stateDir
14
- } from "./chunk-F5QL2GPY.ts";
14
+ } from "./chunk-T3HH3UEJ.ts";
15
15
  import {
16
16
  assertWithinRoot,
17
17
  isPathInside
18
- } from "./chunk-YQ6UW7IF.ts";
18
+ } from "./chunk-JAAKLL6M.ts";
19
19
 
20
- // src/snapshot-transaction.ts
20
+ // src/snapshot/snapshot-transaction.ts
21
21
  import { randomUUID } from "node:crypto";
22
22
  import fs from "node:fs/promises";
23
23
  import path from "node:path";
@@ -200,4 +200,4 @@ export {
200
200
  recoverSnapshotTransactionsOnStartup,
201
201
  recoverPendingSnapshotTransactions
202
202
  };
203
- //# sourceMappingURL=chunk-X7SQ2EQM.ts.map
203
+ //# sourceMappingURL=chunk-IJ6FJFA5.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/snapshot/snapshot-transaction.ts"],
4
+ "sourcesContent": ["import { randomUUID } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { assertWithinRoot, isPathInside } from \"../paths.js\";\nimport { withLock } from \"../state/lock.js\";\nimport { stateDir } from \"../state/state-directory.js\";\nimport { agentDir } from \"./session-paths.js\";\nimport { sessionStorageRoot } from \"./snapshot-paths.js\";\nimport type { SnapshotApplyPlan } from \"./snapshot-types.js\";\n\nconst JOURNAL_VERSION = 1;\n\ninterface TransactionEntry {\n\ttarget: string;\n\tbackupName: string;\n\tkind: \"missing\" | \"file\" | \"directory\" | \"symlink\";\n\tlinkTarget?: string;\n}\n\ninterface TransactionJournal {\n\tversion: number;\n\troot: string;\n\tsessionRoot?: string;\n\tentries: TransactionEntry[];\n}\n\nexport async function applySnapshotTransaction(\n\tplan: SnapshotApplyPlan,\n\toptions: { sessionDir?: string } = {},\n) {\n\tawait recoverPendingSnapshotTransactions();\n\tconst transaction = await prepareTransaction(plan, options.sessionDir);\n\ttry {\n\t\tfor (const target of plan.deletes) {\n\t\t\tawait fs.rm(target, { force: true, recursive: true });\n\t\t}\n\t\tfor (const item of plan.writes) {\n\t\t\tawait fs.mkdir(path.dirname(item.target), { recursive: true });\n\t\t\tawait fs.writeFile(item.target, item.content);\n\t\t}\n\t\tawait fs.rm(transaction.directory, { recursive: true, force: true });\n\t} catch (error) {\n\t\ttry {\n\t\t\tawait restoreTransaction(transaction.directory, transaction.journal);\n\t\t} catch (recoveryError) {\n\t\t\tthrow new AggregateError(\n\t\t\t\t[error, recoveryError],\n\t\t\t\t`Snapshot apply failed and automatic recovery also failed. Transaction retained at ${transaction.directory}.`,\n\t\t\t);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nexport async function recoverSnapshotTransactionsOnStartup() {\n\tif (!(await pendingTransactionEntries()).some((entry) => entry.isDirectory())) return;\n\tawait withLock(\"recovery\", recoverPendingSnapshotTransactions, { reclaimStale: true });\n}\n\nexport async function recoverPendingSnapshotTransactions() {\n\tconst directory = transactionRoot();\n\tconst entries = await pendingTransactionEntries();\n\tfor (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {\n\t\tif (!entry.isDirectory()) continue;\n\t\tconst transactionDirectory = path.join(directory, entry.name);\n\t\tconst journalPath = path.join(transactionDirectory, \"journal.json\");\n\t\tlet journal: TransactionJournal;\n\t\ttry {\n\t\t\tjournal = JSON.parse(await fs.readFile(journalPath, \"utf8\")) as TransactionJournal;\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\tawait fs.rm(transactionDirectory, { recursive: true, force: true });\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow new Error(`Cannot recover malformed pi-sync transaction: ${journalPath}`, {\n\t\t\t\tcause: error,\n\t\t\t});\n\t\t}\n\t\tawait restoreTransaction(transactionDirectory, journal);\n\t}\n}\n\nasync function pendingTransactionEntries() {\n\ttry {\n\t\treturn await fs.readdir(transactionRoot(), { withFileTypes: true });\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n\t\tthrow error;\n\t}\n}\n\nasync function prepareTransaction(plan: SnapshotApplyPlan, sessionDir?: string) {\n\tconst root = path.resolve(agentDir());\n\tconst sessionRoot = sessionDir ? path.resolve(sessionStorageRoot(root, sessionDir)) : undefined;\n\tconst directory = path.join(transactionRoot(), randomUUID());\n\tconst backupDirectory = path.join(directory, \"before\");\n\tawait fs.mkdir(backupDirectory, { recursive: true, mode: 0o700 });\n\tconst targets = [...new Set([...plan.deletes, ...plan.writes.map((item) => item.target)])].sort();\n\tconst entries: TransactionEntry[] = [];\n\tfor (let index = 0; index < targets.length; index += 1) {\n\t\tconst target = targets[index];\n\t\tif (!target) continue;\n\t\tassertAllowedTarget(root, sessionRoot, target);\n\t\tconst backupName = `${index}`;\n\t\tconst backupPath = path.join(backupDirectory, backupName);\n\t\ttry {\n\t\t\tconst stat = await fs.lstat(target);\n\t\t\tif (stat.isSymbolicLink()) {\n\t\t\t\tentries.push({\n\t\t\t\t\ttarget,\n\t\t\t\t\tbackupName,\n\t\t\t\t\tkind: \"symlink\",\n\t\t\t\t\tlinkTarget: await fs.readlink(target),\n\t\t\t\t});\n\t\t\t} else if (stat.isDirectory()) {\n\t\t\t\tawait fs.cp(target, backupPath, {\n\t\t\t\t\trecursive: true,\n\t\t\t\t\tdereference: false,\n\t\t\t\t\tpreserveTimestamps: true,\n\t\t\t\t});\n\t\t\t\tentries.push({ target, backupName, kind: \"directory\" });\n\t\t\t} else if (stat.isFile()) {\n\t\t\t\tawait fs.copyFile(target, backupPath);\n\t\t\t\tawait fs.chmod(backupPath, stat.mode);\n\t\t\t\tentries.push({ target, backupName, kind: \"file\" });\n\t\t\t} else {\n\t\t\t\tthrow new Error(`Unsupported existing snapshot target: ${target}`);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst code = (error as NodeJS.ErrnoException).code;\n\t\t\tif (code !== \"ENOENT\" && code !== \"ENOTDIR\") throw error;\n\t\t\tentries.push({ target, backupName, kind: \"missing\" });\n\t\t}\n\t}\n\tconst journal: TransactionJournal = {\n\t\tversion: JOURNAL_VERSION,\n\t\troot,\n\t\tsessionRoot,\n\t\tentries,\n\t};\n\tawait fs.writeFile(\n\t\tpath.join(directory, \"journal.json\"),\n\t\t`${JSON.stringify(journal, null, \"\\t\")}\\n`,\n\t\t{\n\t\t\tmode: 0o600,\n\t\t},\n\t);\n\treturn { directory, journal };\n}\n\nasync function restoreTransaction(directory: string, journal: TransactionJournal) {\n\tvalidateJournal(directory, journal);\n\tfor (const entry of [...journal.entries].sort(\n\t\t(left, right) => right.target.length - left.target.length,\n\t)) {\n\t\tawait fs.rm(entry.target, { recursive: true, force: true });\n\t}\n\tfor (const entry of journal.entries) {\n\t\tif (entry.kind === \"missing\") continue;\n\t\tawait fs.mkdir(path.dirname(entry.target), { recursive: true });\n\t\tconst backupPath = path.join(directory, \"before\", entry.backupName);\n\t\tassertWithinRoot(directory, backupPath);\n\t\tif (entry.kind === \"file\") await fs.copyFile(backupPath, entry.target);\n\t\telse if (entry.kind === \"directory\") {\n\t\t\tawait fs.cp(backupPath, entry.target, {\n\t\t\t\trecursive: true,\n\t\t\t\tdereference: false,\n\t\t\t\tpreserveTimestamps: true,\n\t\t\t});\n\t\t} else if (entry.kind === \"symlink\" && entry.linkTarget !== undefined) {\n\t\t\tawait fs.symlink(entry.linkTarget, entry.target);\n\t\t}\n\t}\n\tawait fs.rm(directory, { recursive: true, force: true });\n}\n\nfunction validateJournal(directory: string, journal: TransactionJournal) {\n\tif (journal.version !== JOURNAL_VERSION || !Array.isArray(journal.entries)) {\n\t\tthrow new Error(`Unsupported pi-sync transaction journal: ${directory}`);\n\t}\n\tconst expectedRoot = path.resolve(agentDir());\n\tif (path.resolve(journal.root) !== expectedRoot) {\n\t\tthrow new Error(`Transaction root no longer matches the Pi agent directory: ${directory}`);\n\t}\n\tfor (const entry of journal.entries) {\n\t\tif (\n\t\t\t!entry ||\n\t\t\ttypeof entry.target !== \"string\" ||\n\t\t\ttypeof entry.backupName !== \"string\" ||\n\t\t\t!/^\\d+$/u.test(entry.backupName)\n\t\t) {\n\t\t\tthrow new Error(`Invalid pi-sync transaction entry: ${directory}`);\n\t\t}\n\t\tassertAllowedTarget(journal.root, journal.sessionRoot, entry.target);\n\t}\n}\n\nfunction assertAllowedTarget(root: string, sessionRoot: string | undefined, target: string) {\n\tconst resolved = path.resolve(target);\n\tif (isPathInside(root, resolved)) {\n\t\tassertWithinRoot(root, resolved);\n\t\treturn;\n\t}\n\tif (sessionRoot && isPathInside(sessionRoot, resolved)) {\n\t\tassertWithinRoot(sessionRoot, resolved);\n\t\treturn;\n\t}\n\tthrow new Error(`Transaction target is outside configured roots: ${target}`);\n}\n\nfunction transactionRoot() {\n\treturn path.join(stateDir(), \"transactions\");\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -48,7 +48,7 @@ function trimSlashes(value) {
48
48
  return value.replace(/^\/+|\/+$/g, "");
49
49
  }
50
50
 
51
- // src/sync-policy.ts
51
+ // src/sync/sync-policy.ts
52
52
  import path2 from "node:path";
53
53
  var BUILT_IN_SYNC_ROOTS = [
54
54
  "settings.json",
@@ -391,4 +391,4 @@ export {
391
391
  normalizeSyncFiles,
392
392
  normalizeExtraFiles
393
393
  };
394
- //# sourceMappingURL=chunk-YQ6UW7IF.ts.map
394
+ //# sourceMappingURL=chunk-JAAKLL6M.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/paths.ts", "../../src/sync/sync-policy.ts"],
4
+ "sourcesContent": ["import path from \"node:path\";\n\nexport function isDeniedPath(relativePath: string) {\n\tconst normalized = toPosix(relativePath);\n\tconst lower = normalized.toLowerCase();\n\tconst segments = lower.split(\"/\");\n\tconst base = path.posix.basename(lower);\n\treturn (\n\t\tsegments.includes(\"node_modules\") ||\n\t\tsegments.includes(\".git\") ||\n\t\tsegments.includes(\".pisync\") ||\n\t\tsegments.includes(\"pi-sync\") ||\n\t\tsegments.includes(\".pi-sync-state-migration.lock\") ||\n\t\tbase === \".env\" ||\n\t\tbase.startsWith(\".env.\") ||\n\t\tbase.endsWith(\".env\") ||\n\t\tbase.includes(\"secret\") ||\n\t\tbase.includes(\"token\") ||\n\t\tbase === \"pi-sync.json\" ||\n\t\tbase.startsWith(\"pi-sync.json.\") ||\n\t\tbase.startsWith(\".pi-sync.json.\") ||\n\t\tbase === \"pi-sync.local.json\" ||\n\t\tbase.startsWith(\"pi-sync.local.json.\") ||\n\t\tbase.startsWith(\".pi-sync.local.json.\")\n\t);\n}\n\nexport function isPathInside(parent: string, child: string) {\n\tconst relative = path.relative(path.resolve(parent), path.resolve(child));\n\treturn relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative));\n}\n\nexport function safeJoin(root: string, relativePath: string) {\n\tconst target = path.resolve(root, relativePath);\n\tassertWithinRoot(root, target, relativePath);\n\treturn target;\n}\n\nexport function assertWithinRoot(root: string, target: string, label = target) {\n\tconst resolvedRoot = path.resolve(root);\n\tconst resolvedTarget = path.resolve(target);\n\tif (resolvedTarget !== resolvedRoot && !resolvedTarget.startsWith(`${resolvedRoot}${path.sep}`)) {\n\t\tthrow new Error(`Unsafe path in snapshot: ${label}`);\n\t}\n}\n\nexport function encodeKey(key: string) {\n\treturn key.split(\"/\").map(encodeURIComponent).join(\"/\");\n}\n\nexport function posixJoin(...parts: string[]) {\n\treturn parts\n\t\t.map((part) => trimSlashes(part))\n\t\t.filter(Boolean)\n\t\t.join(\"/\");\n}\n\nexport function parentPaths(relativePath: string) {\n\tconst results: string[] = [];\n\tlet index = relativePath.lastIndexOf(\"/\");\n\twhile (index > 0) {\n\t\tresults.push(relativePath.slice(0, index));\n\t\tindex = relativePath.lastIndexOf(\"/\", index - 1);\n\t}\n\treturn results;\n}\n\nexport function toPosix(value: string) {\n\treturn value.split(path.sep).join(\"/\");\n}\n\nfunction trimSlashes(value: string) {\n\treturn value.replace(/^\\/+|\\/+$/g, \"\");\n}\n\nexport function safeName(value: string) {\n\treturn value.replace(/[^A-Za-z0-9._-]/g, \"_\");\n}\n", "import path from \"node:path\";\nimport { isDeniedPath, toPosix } from \"../paths.js\";\nimport type { Snapshot, SnapshotSelection } from \"../snapshot/snapshot-types.js\";\n\nexport const BUILT_IN_SYNC_ROOTS = [\n\t\"settings.json\",\n\t\"keybindings.json\",\n\t\"models.json\",\n\t\"AGENTS.md\",\n\t\"APPEND_SYSTEM.md\",\n\t\"skills\",\n\t\"prompts\",\n\t\"themes\",\n\t\"extensions\",\n] as const;\n\nexport const DEFAULT_SYNC_INCLUDE = [...BUILT_IN_SYNC_ROOTS] as const;\nexport type BuiltInSyncFile = (typeof BUILT_IN_SYNC_ROOTS)[number];\nconst SNAPSHOT_SELECTION_VERSION = 1;\nconst MAX_SYNC_INCLUDE_ITEMS = 1_024;\nconst MAX_SYNC_INCLUDE_PATH_BYTES = 4_096;\nconst MAX_SYNC_INCLUDE_TOTAL_BYTES = 256 * 1_024;\n\ninterface IncludePathNode {\n\tselected?: string;\n\tchildren: Map<string, IncludePathNode>;\n}\n\nconst BUILT_IN_BY_LOWER = new Map<string, BuiltInSyncFile>(\n\tBUILT_IN_SYNC_ROOTS.map((fileName) => [fileName.toLowerCase(), fileName]),\n);\nconst TOP_LEVEL_FILE_PATHS = new Map<string, string>(\n\tBUILT_IN_SYNC_ROOTS.filter((fileName) => fileName.includes(\".\")).map((fileName) => [\n\t\tfileName.toLowerCase(),\n\t\tfileName,\n\t]),\n);\nconst TOP_LEVEL_DIRS = new Set<string>(\n\tBUILT_IN_SYNC_ROOTS.filter((fileName) => !fileName.includes(\".\")),\n);\nconst RESERVED_TOP_LEVEL_NAMES = new Set<string>([...BUILT_IN_BY_LOWER.keys(), \"sessions\"]);\n\nexport function normalizeSyncInclude(value: unknown): string[] {\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(\"Invalid pi-sync settings: sync.include must be an array.\");\n\t}\n\tif (value.length > MAX_SYNC_INCLUDE_ITEMS) {\n\t\tthrow new Error(\n\t\t\t`Invalid pi-sync settings: sync.include has too many items; limit: ${MAX_SYNC_INCLUDE_ITEMS}.`,\n\t\t);\n\t}\n\tconst result: string[] = [];\n\tconst seen = new Set<string>();\n\tconst pathRoot: IncludePathNode = { children: new Map() };\n\tlet totalBytes = 0;\n\tfor (const item of value) {\n\t\tif (typeof item !== \"string\") {\n\t\t\tthrow new Error(\"Invalid pi-sync settings: sync.include items must be strings.\");\n\t\t}\n\t\tconst itemBytes = Buffer.byteLength(item, \"utf8\");\n\t\tif (itemBytes > MAX_SYNC_INCLUDE_PATH_BYTES) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid pi-sync settings: sync.include item is too long; limit: ${MAX_SYNC_INCLUDE_PATH_BYTES} bytes.`,\n\t\t\t);\n\t\t}\n\t\ttotalBytes += itemBytes;\n\t\tif (totalBytes > MAX_SYNC_INCLUDE_TOTAL_BYTES) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid pi-sync settings: sync.include is too large; limit: ${MAX_SYNC_INCLUDE_TOTAL_BYTES} bytes.`,\n\t\t\t);\n\t\t}\n\t\tconst trimmed = item.trim();\n\t\tconst builtIn = BUILT_IN_BY_LOWER.get(trimmed.toLowerCase());\n\t\tconst normalized = builtIn ?? (trimmed.toLowerCase() === \"sessions\" ? \"sessions\" : trimmed);\n\t\tif (!builtIn && normalized !== \"sessions\") validateAgentRelativeInclude(normalized);\n\t\tconst identity = normalized.toLowerCase();\n\t\tif (seen.has(identity)) {\n\t\t\tthrow new Error(`Invalid pi-sync settings: duplicate sync.include item: ${item}`);\n\t\t}\n\t\taddIncludePath(pathRoot, identity, item);\n\t\tseen.add(identity);\n\t\tresult.push(normalized);\n\t}\n\treturn result;\n}\n\nfunction addIncludePath(root: IncludePathNode, identity: string, source: string) {\n\tlet node = root;\n\tfor (const segment of identity.split(\"/\")) {\n\t\tif (node.selected !== undefined) throwOverlappingInclude(source);\n\t\tlet child = node.children.get(segment);\n\t\tif (!child) {\n\t\t\tchild = { children: new Map() };\n\t\t\tnode.children.set(segment, child);\n\t\t}\n\t\tnode = child;\n\t}\n\tif (node.children.size > 0) throwOverlappingInclude(source);\n\tnode.selected = identity;\n}\n\nfunction throwOverlappingInclude(item: string): never {\n\tthrow new Error(\n\t\t`Invalid pi-sync settings: overlapping sync.include items are ambiguous: ${item}`,\n\t);\n}\n\nfunction validateAgentRelativeInclude(value: string) {\n\tconst normalized = toPosix(value);\n\tconst topLevel = normalized.split(\"/\")[0]?.toLowerCase();\n\tif (\n\t\t!normalized ||\n\t\tnormalized === \".\" ||\n\t\tnormalized === \"..\" ||\n\t\tnormalized.startsWith(\"../\") ||\n\t\tpath.posix.isAbsolute(normalized) ||\n\t\tnormalized.includes(\"\\\\\") ||\n\t\tpath.posix.normalize(normalized) !== normalized ||\n\t\t// biome-ignore lint/suspicious/noControlCharactersInRegex: Include paths cannot contain controls.\n\t\t/[\\u0000-\\u001f\\u007f-\\u009f]/u.test(normalized)\n\t) {\n\t\tthrow new Error(\n\t\t\t`Invalid pi-sync settings: sync.include item must be a safe agent-relative path: ${value}`,\n\t\t);\n\t}\n\tif (isDeniedPath(normalized)) {\n\t\tthrow new Error(`Invalid pi-sync settings: ${value} cannot be synced.`);\n\t}\n\tif (topLevel && RESERVED_TOP_LEVEL_NAMES.has(topLevel)) {\n\t\tthrow new Error(\n\t\t\t`Invalid pi-sync settings: use the canonical ${topLevel} root instead of a nested sync.include path.`,\n\t\t);\n\t}\n}\n\nexport function portableSnapshotSelection(value: unknown): SnapshotSelection {\n\tconst selection = value as Partial<SnapshotSelection> | null;\n\tif (\n\t\t!selection ||\n\t\ttypeof selection !== \"object\" ||\n\t\tArray.isArray(selection) ||\n\t\tselection.version !== SNAPSHOT_SELECTION_VERSION ||\n\t\t!Object.hasOwn(selection, \"include\") ||\n\t\tObject.keys(selection).some((key) => key !== \"version\" && key !== \"include\")\n\t) {\n\t\tthrow new Error(\"Invalid snapshot selection policy.\");\n\t}\n\treturn {\n\t\tversion: SNAPSHOT_SELECTION_VERSION,\n\t\tinclude: normalizeSyncInclude(selection.include),\n\t};\n}\n\nexport function snapshotSelectionInclude(snapshot: Pick<Snapshot, \"selection\">) {\n\treturn snapshot.selection === undefined\n\t\t? undefined\n\t\t: portableSnapshotSelection(snapshot.selection).include;\n}\n\nexport function selectionForSnapshot(include: unknown): SnapshotSelection {\n\treturn { version: SNAPSHOT_SELECTION_VERSION, include: normalizeSyncInclude(include) };\n}\n\nexport function sameSyncInclude(left: unknown, right: unknown) {\n\tconst normalizedLeft = normalizeSyncInclude(left);\n\tconst normalizedRight = normalizeSyncInclude(right);\n\treturn (\n\t\tnormalizedLeft.length === normalizedRight.length &&\n\t\tnormalizedLeft.every((item, index) => item === normalizedRight[index])\n\t);\n}\n\nexport function compareSyncInclude(local: unknown, remote: unknown) {\n\tconst localInclude = normalizeSyncInclude(local);\n\tconst remoteInclude = normalizeSyncInclude(remote);\n\tconst localSet = new Set(localInclude);\n\tconst remoteSet = new Set(remoteInclude);\n\treturn {\n\t\tsame: sameSyncInclude(localInclude, remoteInclude),\n\t\tremoteOnly: remoteInclude.filter((item) => !localSet.has(item)),\n\t\tlocalOnly: localInclude.filter((item) => !remoteSet.has(item)),\n\t};\n}\n\nexport type RemoteSelectionState =\n\t| { kind: \"legacy\"; discovered: string[] }\n\t| { kind: \"same\"; include: string[] }\n\t| {\n\t\t\tkind: \"different\";\n\t\t\tinclude: string[];\n\t\t\tremoteOnly: string[];\n\t\t\tlocalOnly: string[];\n\t };\n\nexport function inspectRemoteSelection(\n\tlocalInclude: unknown,\n\tsnapshot: Pick<Snapshot, \"selection\" | \"files\">,\n): RemoteSelectionState {\n\tconst remoteInclude = snapshotSelectionInclude(snapshot);\n\tif (!remoteInclude) {\n\t\treturn { kind: \"legacy\", discovered: discoverLegacySnapshotInclude(snapshot) };\n\t}\n\tconst comparison = compareSyncInclude(localInclude, remoteInclude);\n\treturn comparison.same\n\t\t? { kind: \"same\", include: remoteInclude }\n\t\t: { kind: \"different\", include: remoteInclude, ...comparison };\n}\n\nexport interface RemoteSelectionDecision {\n\tsetupName: string;\n\tconfigIdentity: string;\n\tlocalInclude: readonly string[];\n\tremoteInclude: readonly string[];\n}\n\nexport class RemoteSelectionMismatchError extends Error {\n\treadonly decision: RemoteSelectionDecision;\n\treadonly setupName: string;\n\treadonly localInclude: string[];\n\treadonly remoteInclude: string[];\n\n\tconstructor(\n\t\tsetupName: string,\n\t\tlocalInclude: unknown,\n\t\tremoteInclude: unknown,\n\t\tconfigIdentity = JSON.stringify([setupName, normalizeSyncInclude(localInclude)]),\n\t) {\n\t\tconst local = normalizeSyncInclude(localInclude);\n\t\tconst remote = normalizeSyncInclude(remoteInclude);\n\t\tsuper(formatRemoteSelectionMismatch(setupName, local, remote));\n\t\tthis.name = \"RemoteSelectionMismatchError\";\n\t\tthis.setupName = setupName;\n\t\tthis.localInclude = local;\n\t\tthis.remoteInclude = remote;\n\t\tthis.decision = {\n\t\t\tsetupName,\n\t\t\tconfigIdentity,\n\t\t\tlocalInclude: [...local],\n\t\t\tremoteInclude: [...remote],\n\t\t};\n\t}\n}\n\nexport function remoteSelectionMismatch(\n\tconfig: { setupName: string; include: unknown },\n\tremoteInclude: unknown,\n\tconfigIdentity?: string,\n) {\n\treturn new RemoteSelectionMismatchError(\n\t\tconfig.setupName,\n\t\tconfig.include,\n\t\tremoteInclude,\n\t\tconfigIdentity,\n\t);\n}\n\nexport function formatRemoteSelectionMismatch(\n\tsetupName: string,\n\tlocalInclude: readonly string[],\n\tremoteInclude: readonly string[],\n) {\n\tconst comparison = compareSyncInclude(localInclude, remoteInclude);\n\tconst lines = [\n\t\t`Synced content differs for sync setup \u201C${stripTerminalControls(setupName)}\u201D.`,\n\t\t`Remote-only: ${comparison.remoteOnly.join(\", \") || \"none\"}`,\n\t\t`This-device-only: ${comparison.localOnly.join(\", \") || \"none\"}`,\n\t];\n\tif (comparison.remoteOnly.length === 0 && comparison.localOnly.length === 0) {\n\t\tlines.push(\n\t\t\t\"Only ordering differs.\",\n\t\t\t`Remote order: ${remoteInclude.join(\", \") || \"none\"}`,\n\t\t\t`This device order: ${localInclude.join(\", \") || \"none\"}`,\n\t\t);\n\t}\n\tlines.push(\"Run /sync in TUI to review both content lists and choose what happens next.\");\n\treturn lines.join(\"\\n\");\n}\n\nfunction stripTerminalControls(value: string) {\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: Escape untrusted terminal controls.\n\treturn value.replace(/[\\u0000-\\u001f\\u007f-\\u009f]/gu, \"?\");\n}\n\nexport function discoverLegacySnapshotInclude(snapshot: Pick<Snapshot, \"files\">) {\n\tconst builtIns = new Set<BuiltInSyncFile>();\n\tconst custom = new Set<string>();\n\tlet sessions = false;\n\tfor (const file of snapshot.files) {\n\t\tconst normalized = toPosix(file.path);\n\t\tif (\n\t\t\t!normalized ||\n\t\t\tfile.path.includes(\"\\\\\") ||\n\t\t\tnormalized.length > 4096 ||\n\t\t\tnormalized.startsWith(\"../\") ||\n\t\t\tpath.posix.isAbsolute(normalized) ||\n\t\t\tpath.posix.normalize(normalized) !== normalized ||\n\t\t\t// biome-ignore lint/suspicious/noControlCharactersInRegex: Ignore unsafe legacy paths.\n\t\t\t/[\\u0000-\\u001f\\u007f-\\u009f]/u.test(normalized) ||\n\t\t\tisDeniedPath(normalized)\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst [topLevel, ...rest] = normalized.split(\"/\");\n\t\tif (!topLevel) continue;\n\t\tif (topLevel === \"sessions\" && rest.length > 0) {\n\t\t\tsessions = true;\n\t\t\tcontinue;\n\t\t}\n\t\tconst builtIn = BUILT_IN_BY_LOWER.get(topLevel.toLowerCase());\n\t\tif (\n\t\t\tbuiltIn &&\n\t\t\t((TOP_LEVEL_DIRS.has(builtIn) && rest.length > 0) ||\n\t\t\t\t(!TOP_LEVEL_DIRS.has(builtIn) && rest.length === 0))\n\t\t) {\n\t\t\tbuiltIns.add(builtIn);\n\t\t\tcontinue;\n\t\t}\n\t\tif (isSafeCustomIncludePath(topLevel)) custom.add(topLevel);\n\t}\n\treturn [\n\t\t...BUILT_IN_SYNC_ROOTS.filter((item) => builtIns.has(item)),\n\t\t...[...custom].sort((left, right) => left.localeCompare(right)),\n\t\t...(sessions ? [\"sessions\"] : []),\n\t];\n}\n\nexport function syncIncludeSelection(value: unknown) {\n\tconst include = normalizeSyncInclude(value);\n\tconst builtIns = include.filter((item): item is BuiltInSyncFile =>\n\t\tBUILT_IN_BY_LOWER.has(item.toLowerCase()),\n\t);\n\tconst custom = include.filter(\n\t\t(item) => item !== \"sessions\" && !BUILT_IN_BY_LOWER.has(item.toLowerCase()),\n\t);\n\treturn { include, builtIns, custom, sessions: include.includes(\"sessions\") };\n}\n\nexport function customIncludePathsByLower(value: unknown) {\n\treturn new Map(\n\t\tsyncIncludeSelection(value).custom.map((relativePath) => [\n\t\t\trelativePath.toLowerCase(),\n\t\t\trelativePath,\n\t\t]),\n\t);\n}\n\nexport interface SyncSelectionConfig {\n\tinclude?: unknown;\n\tsyncFiles?: unknown;\n\tsyncSessions?: boolean;\n\textraFiles?: unknown;\n}\n\nexport function includeFromSelectionConfig(config: SyncSelectionConfig) {\n\tif (config.include !== undefined) return normalizeSyncInclude(config.include);\n\treturn [\n\t\t...normalizeSyncFiles(config.syncFiles),\n\t\t...normalizeExtraFiles(config.extraFiles),\n\t\t...(config.syncSessions ? [\"sessions\"] : []),\n\t];\n}\n\nexport function isConfiguredSnapshotPath(\n\trelativePath: string,\n\tconfig: SyncSelectionConfig,\n\t_legacyExtraFiles?: Set<string>,\n) {\n\tconst normalized = toPosix(relativePath);\n\tconst selection = syncIncludeSelection(includeFromSelectionConfig(config));\n\tif (normalized.startsWith(\"sessions/\")) return selection.sessions;\n\tconst lower = normalized.toLowerCase();\n\tif (!normalized.includes(\"/\")) {\n\t\tconst builtIn = BUILT_IN_BY_LOWER.get(lower);\n\t\tif (builtIn) return selection.builtIns.includes(builtIn) && !TOP_LEVEL_DIRS.has(builtIn);\n\t}\n\tconst topLevel = normalized.slice(0, normalized.indexOf(\"/\"));\n\tif (selection.builtIns.includes(topLevel as BuiltInSyncFile) && TOP_LEVEL_DIRS.has(topLevel)) {\n\t\treturn true;\n\t}\n\treturn selection.custom.some((candidate) => {\n\t\tconst candidateLower = candidate.toLowerCase();\n\t\treturn lower === candidateLower || lower.startsWith(`${candidateLower}/`);\n\t});\n}\n\nexport function canonicalSnapshotPathForConfig(\n\trelativePath: string,\n\tincludePaths: Map<string, string>,\n) {\n\tconst normalized = toPosix(relativePath);\n\tconst lower = normalized.toLowerCase();\n\treturn TOP_LEVEL_FILE_PATHS.get(lower) ?? includePaths.get(lower) ?? normalized;\n}\n\nexport function isPreservableUnmanagedSnapshotPath(relativePath: string) {\n\tconst normalized = toPosix(relativePath);\n\tif (!normalized || isDeniedPath(normalized)) return false;\n\tif (normalized.startsWith(\"sessions/\")) return normalized.endsWith(\".jsonl\");\n\tif (!normalized.includes(\"/\")) {\n\t\tconst lower = normalized.toLowerCase();\n\t\treturn TOP_LEVEL_FILE_PATHS.has(lower) || !RESERVED_TOP_LEVEL_NAMES.has(lower);\n\t}\n\treturn true;\n}\n\nexport function isSafeCustomIncludePath(relativePath: string) {\n\ttry {\n\t\tvalidateAgentRelativeInclude(relativePath);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport function isBuiltInTopLevelFile(fileName: string) {\n\treturn TOP_LEVEL_FILE_PATHS.has(path.posix.basename(fileName).toLowerCase());\n}\n\n/** Compatibility projection for backend-neutral output while callers move to sync.include. */\nexport function normalizeSyncFiles(value: unknown): BuiltInSyncFile[] {\n\tif (value === undefined) return [...DEFAULT_SYNC_INCLUDE];\n\tif (Array.isArray(value)) {\n\t\treturn normalizeSyncInclude(value).filter((item): item is BuiltInSyncFile =>\n\t\t\tBUILT_IN_BY_LOWER.has(item.toLowerCase()),\n\t\t);\n\t}\n\tthrow new Error(\"Invalid pi-sync settings: expected an include array.\");\n}\n\n/** Compatibility projection for old rendering helpers; v3 persistence never writes this split field. */\nexport function normalizeExtraFiles(value: unknown) {\n\tif (!Array.isArray(value)) return [];\n\treturn value.filter(\n\t\t(item): item is string => typeof item === \"string\" && isSafeCustomIncludePath(item),\n\t);\n}\n\nexport const extraFilePathsByLower = customIncludePathsByLower;\nexport const isSafeExtraFileName = isSafeCustomIncludePath;\nexport const selectedSyncFileSet = (value: unknown) => new Set(normalizeSyncFiles(value));\n"],
5
+ "mappings": ";;;;AAAA,OAAO,UAAU;AAEV,SAAS,aAAa,cAAsB;AAClD,QAAM,aAAa,QAAQ,YAAY;AACvC,QAAM,QAAQ,WAAW,YAAY;AACrC,QAAM,WAAW,MAAM,MAAM,GAAG;AAChC,QAAM,OAAO,KAAK,MAAM,SAAS,KAAK;AACtC,SACC,SAAS,SAAS,cAAc,KAChC,SAAS,SAAS,MAAM,KACxB,SAAS,SAAS,SAAS,KAC3B,SAAS,SAAS,SAAS,KAC3B,SAAS,SAAS,+BAA+B,KACjD,SAAS,UACT,KAAK,WAAW,OAAO,KACvB,KAAK,SAAS,MAAM,KACpB,KAAK,SAAS,QAAQ,KACtB,KAAK,SAAS,OAAO,KACrB,SAAS,kBACT,KAAK,WAAW,eAAe,KAC/B,KAAK,WAAW,gBAAgB,KAChC,SAAS,wBACT,KAAK,WAAW,qBAAqB,KACrC,KAAK,WAAW,sBAAsB;AAExC;AAEO,SAAS,aAAa,QAAgB,OAAe;AAC3D,QAAM,WAAW,KAAK,SAAS,KAAK,QAAQ,MAAM,GAAG,KAAK,QAAQ,KAAK,CAAC;AACxE,SAAO,aAAa,MAAO,CAAC,SAAS,WAAW,IAAI,KAAK,CAAC,KAAK,WAAW,QAAQ;AACnF;AAEO,SAAS,SAAS,MAAc,cAAsB;AAC5D,QAAM,SAAS,KAAK,QAAQ,MAAM,YAAY;AAC9C,mBAAiB,MAAM,QAAQ,YAAY;AAC3C,SAAO;AACR;AAEO,SAAS,iBAAiB,MAAc,QAAgB,QAAQ,QAAQ;AAC9E,QAAM,eAAe,KAAK,QAAQ,IAAI;AACtC,QAAM,iBAAiB,KAAK,QAAQ,MAAM;AAC1C,MAAI,mBAAmB,gBAAgB,CAAC,eAAe,WAAW,GAAG,YAAY,GAAG,KAAK,GAAG,EAAE,GAAG;AAChG,UAAM,IAAI,MAAM,4BAA4B,KAAK,EAAE;AAAA,EACpD;AACD;AAEO,SAAS,UAAU,KAAa;AACtC,SAAO,IAAI,MAAM,GAAG,EAAE,IAAI,kBAAkB,EAAE,KAAK,GAAG;AACvD;AAEO,SAAS,aAAa,OAAiB;AAC7C,SAAO,MACL,IAAI,CAAC,SAAS,YAAY,IAAI,CAAC,EAC/B,OAAO,OAAO,EACd,KAAK,GAAG;AACX;AAEO,SAAS,YAAY,cAAsB;AACjD,QAAM,UAAoB,CAAC;AAC3B,MAAI,QAAQ,aAAa,YAAY,GAAG;AACxC,SAAO,QAAQ,GAAG;AACjB,YAAQ,KAAK,aAAa,MAAM,GAAG,KAAK,CAAC;AACzC,YAAQ,aAAa,YAAY,KAAK,QAAQ,CAAC;AAAA,EAChD;AACA,SAAO;AACR;AAEO,SAAS,QAAQ,OAAe;AACtC,SAAO,MAAM,MAAM,KAAK,GAAG,EAAE,KAAK,GAAG;AACtC;AAEA,SAAS,YAAY,OAAe;AACnC,SAAO,MAAM,QAAQ,cAAc,EAAE;AACtC;;;ACzEA,OAAOA,WAAU;AAIV,IAAM,sBAAsB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAEO,IAAM,uBAAuB,CAAC,GAAG,mBAAmB;AAE3D,IAAM,6BAA6B;AACnC,IAAM,yBAAyB;AAC/B,IAAM,8BAA8B;AACpC,IAAM,+BAA+B,MAAM;AAO3C,IAAM,oBAAoB,IAAI;AAAA,EAC7B,oBAAoB,IAAI,CAAC,aAAa,CAAC,SAAS,YAAY,GAAG,QAAQ,CAAC;AACzE;AACA,IAAM,uBAAuB,IAAI;AAAA,EAChC,oBAAoB,OAAO,CAAC,aAAa,SAAS,SAAS,GAAG,CAAC,EAAE,IAAI,CAAC,aAAa;AAAA,IAClF,SAAS,YAAY;AAAA,IACrB;AAAA,EACD,CAAC;AACF;AACA,IAAM,iBAAiB,IAAI;AAAA,EAC1B,oBAAoB,OAAO,CAAC,aAAa,CAAC,SAAS,SAAS,GAAG,CAAC;AACjE;AACA,IAAM,2BAA2B,oBAAI,IAAY,CAAC,GAAG,kBAAkB,KAAK,GAAG,UAAU,CAAC;AAEnF,SAAS,qBAAqB,OAA0B;AAC9D,MAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;AAC1B,UAAM,IAAI,MAAM,0DAA0D;AAAA,EAC3E;AACA,MAAI,MAAM,SAAS,wBAAwB;AAC1C,UAAM,IAAI;AAAA,MACT,qEAAqE,sBAAsB;AAAA,IAC5F;AAAA,EACD;AACA,QAAM,SAAmB,CAAC;AAC1B,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,WAA4B,EAAE,UAAU,oBAAI,IAAI,EAAE;AACxD,MAAI,aAAa;AACjB,aAAW,QAAQ,OAAO;AACzB,QAAI,OAAO,SAAS,UAAU;AAC7B,YAAM,IAAI,MAAM,+DAA+D;AAAA,IAChF;AACA,UAAM,YAAY,OAAO,WAAW,MAAM,MAAM;AAChD,QAAI,YAAY,6BAA6B;AAC5C,YAAM,IAAI;AAAA,QACT,mEAAmE,2BAA2B;AAAA,MAC/F;AAAA,IACD;AACA,kBAAc;AACd,QAAI,aAAa,8BAA8B;AAC9C,YAAM,IAAI;AAAA,QACT,+DAA+D,4BAA4B;AAAA,MAC5F;AAAA,IACD;AACA,UAAM,UAAU,KAAK,KAAK;AAC1B,UAAM,UAAU,kBAAkB,IAAI,QAAQ,YAAY,CAAC;AAC3D,UAAM,aAAa,YAAY,QAAQ,YAAY,MAAM,aAAa,aAAa;AACnF,QAAI,CAAC,WAAW,eAAe,WAAY,8BAA6B,UAAU;AAClF,UAAM,WAAW,WAAW,YAAY;AACxC,QAAI,KAAK,IAAI,QAAQ,GAAG;AACvB,YAAM,IAAI,MAAM,0DAA0D,IAAI,EAAE;AAAA,IACjF;AACA,mBAAe,UAAU,UAAU,IAAI;AACvC,SAAK,IAAI,QAAQ;AACjB,WAAO,KAAK,UAAU;AAAA,EACvB;AACA,SAAO;AACR;AAEA,SAAS,eAAe,MAAuB,UAAkB,QAAgB;AAChF,MAAI,OAAO;AACX,aAAW,WAAW,SAAS,MAAM,GAAG,GAAG;AAC1C,QAAI,KAAK,aAAa,OAAW,yBAAwB,MAAM;AAC/D,QAAI,QAAQ,KAAK,SAAS,IAAI,OAAO;AACrC,QAAI,CAAC,OAAO;AACX,cAAQ,EAAE,UAAU,oBAAI,IAAI,EAAE;AAC9B,WAAK,SAAS,IAAI,SAAS,KAAK;AAAA,IACjC;AACA,WAAO;AAAA,EACR;AACA,MAAI,KAAK,SAAS,OAAO,EAAG,yBAAwB,MAAM;AAC1D,OAAK,WAAW;AACjB;AAEA,SAAS,wBAAwB,MAAqB;AACrD,QAAM,IAAI;AAAA,IACT,2EAA2E,IAAI;AAAA,EAChF;AACD;AAEA,SAAS,6BAA6B,OAAe;AACpD,QAAM,aAAa,QAAQ,KAAK;AAChC,QAAM,WAAW,WAAW,MAAM,GAAG,EAAE,CAAC,GAAG,YAAY;AACvD,MACC,CAAC,cACD,eAAe,OACf,eAAe,QACf,WAAW,WAAW,KAAK,KAC3BC,MAAK,MAAM,WAAW,UAAU,KAChC,WAAW,SAAS,IAAI,KACxBA,MAAK,MAAM,UAAU,UAAU,MAAM;AAAA,EAErC,gCAAgC,KAAK,UAAU,GAC9C;AACD,UAAM,IAAI;AAAA,MACT,mFAAmF,KAAK;AAAA,IACzF;AAAA,EACD;AACA,MAAI,aAAa,UAAU,GAAG;AAC7B,UAAM,IAAI,MAAM,6BAA6B,KAAK,oBAAoB;AAAA,EACvE;AACA,MAAI,YAAY,yBAAyB,IAAI,QAAQ,GAAG;AACvD,UAAM,IAAI;AAAA,MACT,+CAA+C,QAAQ;AAAA,IACxD;AAAA,EACD;AACD;AAEO,SAAS,0BAA0B,OAAmC;AAC5E,QAAM,YAAY;AAClB,MACC,CAAC,aACD,OAAO,cAAc,YACrB,MAAM,QAAQ,SAAS,KACvB,UAAU,YAAY,8BACtB,CAAC,OAAO,OAAO,WAAW,SAAS,KACnC,OAAO,KAAK,SAAS,EAAE,KAAK,CAAC,QAAQ,QAAQ,aAAa,QAAQ,SAAS,GAC1E;AACD,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACrD;AACA,SAAO;AAAA,IACN,SAAS;AAAA,IACT,SAAS,qBAAqB,UAAU,OAAO;AAAA,EAChD;AACD;AAEO,SAAS,yBAAyB,UAAuC;AAC/E,SAAO,SAAS,cAAc,SAC3B,SACA,0BAA0B,SAAS,SAAS,EAAE;AAClD;AAEO,SAAS,qBAAqB,SAAqC;AACzE,SAAO,EAAE,SAAS,4BAA4B,SAAS,qBAAqB,OAAO,EAAE;AACtF;AAEO,SAAS,gBAAgB,MAAe,OAAgB;AAC9D,QAAM,iBAAiB,qBAAqB,IAAI;AAChD,QAAM,kBAAkB,qBAAqB,KAAK;AAClD,SACC,eAAe,WAAW,gBAAgB,UAC1C,eAAe,MAAM,CAAC,MAAM,UAAU,SAAS,gBAAgB,KAAK,CAAC;AAEvE;AAEO,SAAS,mBAAmB,OAAgB,QAAiB;AACnE,QAAM,eAAe,qBAAqB,KAAK;AAC/C,QAAM,gBAAgB,qBAAqB,MAAM;AACjD,QAAM,WAAW,IAAI,IAAI,YAAY;AACrC,QAAM,YAAY,IAAI,IAAI,aAAa;AACvC,SAAO;AAAA,IACN,MAAM,gBAAgB,cAAc,aAAa;AAAA,IACjD,YAAY,cAAc,OAAO,CAAC,SAAS,CAAC,SAAS,IAAI,IAAI,CAAC;AAAA,IAC9D,WAAW,aAAa,OAAO,CAAC,SAAS,CAAC,UAAU,IAAI,IAAI,CAAC;AAAA,EAC9D;AACD;AAYO,SAAS,uBACf,cACA,UACuB;AACvB,QAAM,gBAAgB,yBAAyB,QAAQ;AACvD,MAAI,CAAC,eAAe;AACnB,WAAO,EAAE,MAAM,UAAU,YAAY,8BAA8B,QAAQ,EAAE;AAAA,EAC9E;AACA,QAAM,aAAa,mBAAmB,cAAc,aAAa;AACjE,SAAO,WAAW,OACf,EAAE,MAAM,QAAQ,SAAS,cAAc,IACvC,EAAE,MAAM,aAAa,SAAS,eAAe,GAAG,WAAW;AAC/D;AASO,IAAM,+BAAN,cAA2C,MAAM;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,YACC,WACA,cACA,eACA,iBAAiB,KAAK,UAAU,CAAC,WAAW,qBAAqB,YAAY,CAAC,CAAC,GAC9E;AACD,UAAM,QAAQ,qBAAqB,YAAY;AAC/C,UAAM,SAAS,qBAAqB,aAAa;AACjD,UAAM,8BAA8B,WAAW,OAAO,MAAM,CAAC;AAC7D,SAAK,OAAO;AACZ,SAAK,YAAY;AACjB,SAAK,eAAe;AACpB,SAAK,gBAAgB;AACrB,SAAK,WAAW;AAAA,MACf;AAAA,MACA;AAAA,MACA,cAAc,CAAC,GAAG,KAAK;AAAA,MACvB,eAAe,CAAC,GAAG,MAAM;AAAA,IAC1B;AAAA,EACD;AACD;AAEO,SAAS,wBACf,QACA,eACA,gBACC;AACD,SAAO,IAAI;AAAA,IACV,OAAO;AAAA,IACP,OAAO;AAAA,IACP;AAAA,IACA;AAAA,EACD;AACD;AAEO,SAAS,8BACf,WACA,cACA,eACC;AACD,QAAM,aAAa,mBAAmB,cAAc,aAAa;AACjE,QAAM,QAAQ;AAAA,IACb,+CAA0C,sBAAsB,SAAS,CAAC;AAAA,IAC1E,gBAAgB,WAAW,WAAW,KAAK,IAAI,KAAK,MAAM;AAAA,IAC1D,qBAAqB,WAAW,UAAU,KAAK,IAAI,KAAK,MAAM;AAAA,EAC/D;AACA,MAAI,WAAW,WAAW,WAAW,KAAK,WAAW,UAAU,WAAW,GAAG;AAC5E,UAAM;AAAA,MACL;AAAA,MACA,iBAAiB,cAAc,KAAK,IAAI,KAAK,MAAM;AAAA,MACnD,sBAAsB,aAAa,KAAK,IAAI,KAAK,MAAM;AAAA,IACxD;AAAA,EACD;AACA,QAAM,KAAK,6EAA6E;AACxF,SAAO,MAAM,KAAK,IAAI;AACvB;AAEA,SAAS,sBAAsB,OAAe;AAE7C,SAAO,MAAM,QAAQ,kCAAkC,GAAG;AAC3D;AAEO,SAAS,8BAA8B,UAAmC;AAChF,QAAM,WAAW,oBAAI,IAAqB;AAC1C,QAAM,SAAS,oBAAI,IAAY;AAC/B,MAAI,WAAW;AACf,aAAW,QAAQ,SAAS,OAAO;AAClC,UAAM,aAAa,QAAQ,KAAK,IAAI;AACpC,QACC,CAAC,cACD,KAAK,KAAK,SAAS,IAAI,KACvB,WAAW,SAAS,QACpB,WAAW,WAAW,KAAK,KAC3BA,MAAK,MAAM,WAAW,UAAU,KAChCA,MAAK,MAAM,UAAU,UAAU,MAAM;AAAA,IAErC,gCAAgC,KAAK,UAAU,KAC/C,aAAa,UAAU,GACtB;AACD;AAAA,IACD;AACA,UAAM,CAAC,UAAU,GAAG,IAAI,IAAI,WAAW,MAAM,GAAG;AAChD,QAAI,CAAC,SAAU;AACf,QAAI,aAAa,cAAc,KAAK,SAAS,GAAG;AAC/C,iBAAW;AACX;AAAA,IACD;AACA,UAAM,UAAU,kBAAkB,IAAI,SAAS,YAAY,CAAC;AAC5D,QACC,YACE,eAAe,IAAI,OAAO,KAAK,KAAK,SAAS,KAC7C,CAAC,eAAe,IAAI,OAAO,KAAK,KAAK,WAAW,IACjD;AACD,eAAS,IAAI,OAAO;AACpB;AAAA,IACD;AACA,QAAI,wBAAwB,QAAQ,EAAG,QAAO,IAAI,QAAQ;AAAA,EAC3D;AACA,SAAO;AAAA,IACN,GAAG,oBAAoB,OAAO,CAAC,SAAS,SAAS,IAAI,IAAI,CAAC;AAAA,IAC1D,GAAG,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,MAAM,UAAU,KAAK,cAAc,KAAK,CAAC;AAAA,IAC9D,GAAI,WAAW,CAAC,UAAU,IAAI,CAAC;AAAA,EAChC;AACD;AAEO,SAAS,qBAAqB,OAAgB;AACpD,QAAM,UAAU,qBAAqB,KAAK;AAC1C,QAAM,WAAW,QAAQ;AAAA,IAAO,CAAC,SAChC,kBAAkB,IAAI,KAAK,YAAY,CAAC;AAAA,EACzC;AACA,QAAM,SAAS,QAAQ;AAAA,IACtB,CAAC,SAAS,SAAS,cAAc,CAAC,kBAAkB,IAAI,KAAK,YAAY,CAAC;AAAA,EAC3E;AACA,SAAO,EAAE,SAAS,UAAU,QAAQ,UAAU,QAAQ,SAAS,UAAU,EAAE;AAC5E;AAEO,SAAS,0BAA0B,OAAgB;AACzD,SAAO,IAAI;AAAA,IACV,qBAAqB,KAAK,EAAE,OAAO,IAAI,CAAC,iBAAiB;AAAA,MACxD,aAAa,YAAY;AAAA,MACzB;AAAA,IACD,CAAC;AAAA,EACF;AACD;AASO,SAAS,2BAA2B,QAA6B;AACvE,MAAI,OAAO,YAAY,OAAW,QAAO,qBAAqB,OAAO,OAAO;AAC5E,SAAO;AAAA,IACN,GAAG,mBAAmB,OAAO,SAAS;AAAA,IACtC,GAAG,oBAAoB,OAAO,UAAU;AAAA,IACxC,GAAI,OAAO,eAAe,CAAC,UAAU,IAAI,CAAC;AAAA,EAC3C;AACD;AAEO,SAAS,yBACf,cACA,QACA,mBACC;AACD,QAAM,aAAa,QAAQ,YAAY;AACvC,QAAM,YAAY,qBAAqB,2BAA2B,MAAM,CAAC;AACzE,MAAI,WAAW,WAAW,WAAW,EAAG,QAAO,UAAU;AACzD,QAAM,QAAQ,WAAW,YAAY;AACrC,MAAI,CAAC,WAAW,SAAS,GAAG,GAAG;AAC9B,UAAM,UAAU,kBAAkB,IAAI,KAAK;AAC3C,QAAI,QAAS,QAAO,UAAU,SAAS,SAAS,OAAO,KAAK,CAAC,eAAe,IAAI,OAAO;AAAA,EACxF;AACA,QAAM,WAAW,WAAW,MAAM,GAAG,WAAW,QAAQ,GAAG,CAAC;AAC5D,MAAI,UAAU,SAAS,SAAS,QAA2B,KAAK,eAAe,IAAI,QAAQ,GAAG;AAC7F,WAAO;AAAA,EACR;AACA,SAAO,UAAU,OAAO,KAAK,CAAC,cAAc;AAC3C,UAAM,iBAAiB,UAAU,YAAY;AAC7C,WAAO,UAAU,kBAAkB,MAAM,WAAW,GAAG,cAAc,GAAG;AAAA,EACzE,CAAC;AACF;AAEO,SAAS,+BACf,cACA,cACC;AACD,QAAM,aAAa,QAAQ,YAAY;AACvC,QAAM,QAAQ,WAAW,YAAY;AACrC,SAAO,qBAAqB,IAAI,KAAK,KAAK,aAAa,IAAI,KAAK,KAAK;AACtE;AAEO,SAAS,mCAAmC,cAAsB;AACxE,QAAM,aAAa,QAAQ,YAAY;AACvC,MAAI,CAAC,cAAc,aAAa,UAAU,EAAG,QAAO;AACpD,MAAI,WAAW,WAAW,WAAW,EAAG,QAAO,WAAW,SAAS,QAAQ;AAC3E,MAAI,CAAC,WAAW,SAAS,GAAG,GAAG;AAC9B,UAAM,QAAQ,WAAW,YAAY;AACrC,WAAO,qBAAqB,IAAI,KAAK,KAAK,CAAC,yBAAyB,IAAI,KAAK;AAAA,EAC9E;AACA,SAAO;AACR;AAEO,SAAS,wBAAwB,cAAsB;AAC7D,MAAI;AACH,iCAA6B,YAAY;AACzC,WAAO;AAAA,EACR,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAOO,SAAS,mBAAmB,OAAmC;AACrE,MAAI,UAAU,OAAW,QAAO,CAAC,GAAG,oBAAoB;AACxD,MAAI,MAAM,QAAQ,KAAK,GAAG;AACzB,WAAO,qBAAqB,KAAK,EAAE;AAAA,MAAO,CAAC,SAC1C,kBAAkB,IAAI,KAAK,YAAY,CAAC;AAAA,IACzC;AAAA,EACD;AACA,QAAM,IAAI,MAAM,sDAAsD;AACvE;AAGO,SAAS,oBAAoB,OAAgB;AACnD,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO,CAAC;AACnC,SAAO,MAAM;AAAA,IACZ,CAAC,SAAyB,OAAO,SAAS,YAAY,wBAAwB,IAAI;AAAA,EACnF;AACD;",
6
+ "names": ["path", "path"]
7
+ }