@narumitw/pi-sync 0.51.1 → 0.52.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 (181) hide show
  1. package/README.md +30 -5
  2. package/dist/chunks/{chunk-7KHKWL3Q.ts → chunk-2CBF6SMT.ts} +8 -7
  3. package/dist/chunks/chunk-2CBF6SMT.ts.map +7 -0
  4. package/dist/chunks/{chunk-YFADF5ZQ.ts → chunk-2DI6PDZV.ts} +5 -1
  5. package/dist/chunks/chunk-2DI6PDZV.ts.map +7 -0
  6. package/dist/chunks/{chunk-4MPIH46N.ts → chunk-3B4Z2SKV.ts} +40 -6
  7. package/dist/chunks/chunk-3B4Z2SKV.ts.map +7 -0
  8. package/dist/chunks/{chunk-YPXGU5OR.ts → chunk-4QVPTIZB.ts} +16 -6
  9. package/dist/chunks/{chunk-YPXGU5OR.ts.map → chunk-4QVPTIZB.ts.map} +2 -2
  10. package/dist/chunks/{chunk-UA7RXOIT.ts → chunk-4SBTAVFL.ts} +6 -6
  11. package/dist/chunks/{chunk-6546TS54.ts → chunk-5RZPINUQ.ts} +7 -4
  12. package/dist/chunks/{chunk-6546TS54.ts.map → chunk-5RZPINUQ.ts.map} +2 -2
  13. package/dist/chunks/chunk-76NB4R7U.ts +107 -0
  14. package/dist/chunks/chunk-76NB4R7U.ts.map +7 -0
  15. package/dist/chunks/{chunk-DM4X4FIA.ts → chunk-7D6RDCVC.ts} +8 -10
  16. package/dist/chunks/{chunk-DM4X4FIA.ts.map → chunk-7D6RDCVC.ts.map} +2 -2
  17. package/dist/chunks/chunk-A6HU4ERM.ts +124 -0
  18. package/dist/chunks/chunk-A6HU4ERM.ts.map +7 -0
  19. package/dist/chunks/chunk-AMJRUXFT.ts +17 -0
  20. package/dist/chunks/chunk-AMJRUXFT.ts.map +7 -0
  21. package/dist/chunks/chunk-ANWNQIR7.ts +1013 -0
  22. package/dist/chunks/chunk-ANWNQIR7.ts.map +7 -0
  23. package/dist/chunks/{chunk-T56RNRZJ.ts → chunk-BDB4IDM3.ts} +19 -10
  24. package/dist/chunks/chunk-BDB4IDM3.ts.map +7 -0
  25. package/dist/chunks/{chunk-2RZSQWBH.ts → chunk-CHSIAC4N.ts} +5 -5
  26. package/dist/chunks/{chunk-2RZSQWBH.ts.map → chunk-CHSIAC4N.ts.map} +2 -2
  27. package/dist/chunks/chunk-HUZ5CR4W.ts +110 -0
  28. package/dist/chunks/chunk-HUZ5CR4W.ts.map +7 -0
  29. package/dist/chunks/{chunk-QLSN7AGX.ts → chunk-IJ6VV6FN.ts} +81 -19
  30. package/dist/chunks/chunk-IJ6VV6FN.ts.map +7 -0
  31. package/dist/chunks/chunk-NLS2NV3K.ts +1746 -0
  32. package/dist/chunks/chunk-NLS2NV3K.ts.map +7 -0
  33. package/dist/chunks/chunk-ORQRHEEO.ts +362 -0
  34. package/dist/chunks/chunk-ORQRHEEO.ts.map +7 -0
  35. package/dist/chunks/chunk-QE7NZZWT.ts +380 -0
  36. package/dist/chunks/chunk-QE7NZZWT.ts.map +7 -0
  37. package/dist/chunks/{chunk-HGJH56KR.ts → chunk-QHSQCK7L.ts} +4 -4
  38. package/dist/chunks/chunk-QOFMXPM5.ts +735 -0
  39. package/dist/chunks/chunk-QOFMXPM5.ts.map +7 -0
  40. package/dist/chunks/{chunk-DZUSTGQV.ts → chunk-UQ3ALBIB.ts} +4 -4
  41. package/dist/chunks/{chunk-Q32CQWPM.ts → chunk-V6VZ5TLG.ts} +13 -6
  42. package/dist/chunks/chunk-V6VZ5TLG.ts.map +7 -0
  43. package/dist/chunks/{chunk-E42GXD6D.ts → chunk-WKPX2SIC.ts} +4 -4
  44. package/dist/chunks/chunk-ZW5ZLGBI.ts +154 -0
  45. package/dist/chunks/chunk-ZW5ZLGBI.ts.map +7 -0
  46. package/dist/chunks/{chunk-6NT3YFGY.ts → chunk-ZX2Q6THB.ts} +8 -3
  47. package/dist/chunks/{chunk-6NT3YFGY.ts.map → chunk-ZX2Q6THB.ts.map} +2 -2
  48. package/dist/chunks/conflict-artifacts-BB2FZMIE.ts +27 -0
  49. package/dist/chunks/conflict-review-4WPGCXFB.ts +175 -0
  50. package/dist/chunks/conflict-review-4WPGCXFB.ts.map +7 -0
  51. package/dist/chunks/{file-selection-RAVHRYFG.ts → file-selection-WZX7HAY2.ts} +8 -7
  52. package/dist/chunks/{file-selection-RAVHRYFG.ts.map → file-selection-WZX7HAY2.ts.map} +1 -1
  53. package/dist/chunks/{git-backend-IAANDL3K.ts → git-backend-HLGKUQJT.ts} +19 -7
  54. package/dist/chunks/git-backend-HLGKUQJT.ts.map +7 -0
  55. package/dist/chunks/local-fields-6DGYOOIK.ts +22 -0
  56. package/dist/chunks/manager-result-dispatcher-VIDT3QBU.ts +23 -0
  57. package/dist/chunks/{manager-ui-763NEKJY.ts → manager-ui-HSMHS3OK.ts} +236 -57
  58. package/dist/chunks/manager-ui-HSMHS3OK.ts.map +7 -0
  59. package/dist/chunks/merge-baseline-store-EVQAAML6.ts +22 -0
  60. package/dist/chunks/remote-selection-ui-SMF5CLFO.ts +22 -0
  61. package/dist/chunks/{s3-backend-XGXUOPUN.ts → s3-backend-DJ2NT3SZ.ts} +16 -9
  62. package/dist/chunks/s3-backend-DJ2NT3SZ.ts.map +7 -0
  63. package/dist/chunks/{setup-switch-5G2VNJHL.ts → setup-switch-KTUWL6DE.ts} +9 -6
  64. package/dist/chunks/{setup-wizard-YIEWGX32.ts → setup-wizard-GSDPDFC4.ts} +10 -7
  65. package/dist/chunks/{setup-wizard-YIEWGX32.ts.map → setup-wizard-GSDPDFC4.ts.map} +1 -1
  66. package/dist/chunks/{snapshot-JL5DFSXI.ts → snapshot-THS5EY2L.ts} +6 -9
  67. package/dist/chunks/snapshot-THS5EY2L.ts.map +7 -0
  68. package/dist/chunks/{sync-inspection-P3ENJLNF.ts → sync-inspection-VBJF2FWC.ts} +10 -10
  69. package/dist/chunks/sync-inspection-VBJF2FWC.ts.map +7 -0
  70. package/dist/chunks/{sync-operations-AR76EB6C.ts → sync-operations-RHNECOOG.ts} +309 -508
  71. package/dist/chunks/sync-operations-RHNECOOG.ts.map +7 -0
  72. package/dist/chunks/{sync-state-VHJUZ4YG.ts → sync-state-CFL4RRRB.ts} +6 -6
  73. package/dist/chunks/sync-state-CFL4RRRB.ts.map +7 -0
  74. package/dist/chunks/{webdav-backend-3EGUONAT.ts → webdav-backend-ETQGSI6I.ts} +13 -7
  75. package/dist/chunks/webdav-backend-ETQGSI6I.ts.map +7 -0
  76. package/dist/index.ts +142 -43
  77. package/dist/index.ts.map +2 -2
  78. package/docs/content-merge-audit.md +156 -0
  79. package/docs/merge-implementation-audit.md +312 -0
  80. package/docs/settings-merge-audit.md +153 -0
  81. package/docs/settings.md +36 -0
  82. package/package.json +5 -5
  83. package/src/backends/git/git-backend.ts +2 -0
  84. package/src/backends/git/git-storage.ts +8 -3
  85. package/src/backends/s3/s3-backend.ts +2 -1
  86. package/src/backends/webdav/webdav-backend.ts +2 -1
  87. package/src/commands/command-execution.ts +10 -0
  88. package/src/commands/command.ts +4 -0
  89. package/src/settings/config.ts +12 -0
  90. package/src/settings/settings-management.ts +2 -2
  91. package/src/settings/settings-types.ts +14 -1
  92. package/src/settings/settings-validation.ts +24 -3
  93. package/src/snapshot/session-paths.ts +34 -7
  94. package/src/snapshot/snapshot-apply.ts +81 -32
  95. package/src/snapshot/snapshot-case-replacement.ts +80 -0
  96. package/src/snapshot/snapshot-codec.ts +6 -1
  97. package/src/snapshot/snapshot-root-transition.ts +154 -0
  98. package/src/snapshot/snapshot-transaction-plan.ts +72 -0
  99. package/src/snapshot/snapshot-transaction.ts +844 -102
  100. package/src/snapshot/snapshot-types.ts +5 -0
  101. package/src/state/json-file.ts +32 -5
  102. package/src/state/merge-baseline-store.ts +182 -0
  103. package/src/state/mutation-directory-sync.ts +34 -0
  104. package/src/state/state-types.ts +4 -0
  105. package/src/state/sync-state-store.ts +14 -1
  106. package/src/sync/automatic-sync.ts +11 -3
  107. package/src/sync/conflict-artifact-ownership.ts +63 -0
  108. package/src/sync/conflict-artifacts.ts +195 -0
  109. package/src/sync/conflict-groups.ts +68 -0
  110. package/src/sync/conflict-preview.ts +56 -0
  111. package/src/sync/conflict-resolution.ts +93 -0
  112. package/src/sync/conflict-retention.ts +72 -0
  113. package/src/sync/conflict-review.ts +95 -0
  114. package/src/sync/content-conflicts.ts +73 -0
  115. package/src/sync/field-migration.ts +39 -0
  116. package/src/sync/file-merge-planner.ts +143 -0
  117. package/src/sync/json-document.ts +184 -0
  118. package/src/sync/local-fields.ts +134 -0
  119. package/src/sync/merge-apply.ts +428 -0
  120. package/src/sync/merge-journal.ts +219 -0
  121. package/src/sync/merged-sync.ts +723 -0
  122. package/src/sync/partial-progress.ts +54 -0
  123. package/src/sync/remote-snapshot.ts +6 -0
  124. package/src/sync/session-merge.ts +194 -0
  125. package/src/sync/settings-conflicts.ts +52 -0
  126. package/src/sync/settings-merge.ts +57 -0
  127. package/src/sync/startup-check.ts +90 -5
  128. package/src/sync/sync-inspection.ts +1 -0
  129. package/src/sync/sync-local.ts +61 -0
  130. package/src/sync/sync-mutations.ts +256 -114
  131. package/src/sync/sync-queries.ts +6 -2
  132. package/src/sync/sync-state.ts +14 -5
  133. package/src/sync/text-merge.ts +133 -0
  134. package/src/sync-extension.ts +8 -0
  135. package/src/ui/manager-state.ts +12 -1
  136. package/src/ui/manager-ui.ts +10 -0
  137. package/src/ui/merge-review.ts +28 -0
  138. package/src/ui/remote-selection-ui.ts +1 -0
  139. package/src/ui/settings-ui.ts +183 -2
  140. package/src/ui/setup/setup-actions.ts +2 -2
  141. package/src/ui/setup/setup-switcher.ts +2 -1
  142. package/src/ui/storage-connections-ui.ts +11 -7
  143. package/src/ui/sync-attention.ts +3 -0
  144. package/src/ui/sync-format.ts +16 -12
  145. package/src/ui/sync-setups-ui.ts +1 -0
  146. package/src/ui/terminal-text.ts +9 -2
  147. package/dist/chunks/chunk-4AIHXRKE.ts +0 -197
  148. package/dist/chunks/chunk-4AIHXRKE.ts.map +0 -7
  149. package/dist/chunks/chunk-4MPIH46N.ts.map +0 -7
  150. package/dist/chunks/chunk-7KHKWL3Q.ts.map +0 -7
  151. package/dist/chunks/chunk-EEJPYTWE.ts +0 -348
  152. package/dist/chunks/chunk-EEJPYTWE.ts.map +0 -7
  153. package/dist/chunks/chunk-IVXEDPAD.ts +0 -370
  154. package/dist/chunks/chunk-IVXEDPAD.ts.map +0 -7
  155. package/dist/chunks/chunk-MSSW6F3R.ts +0 -12
  156. package/dist/chunks/chunk-MSSW6F3R.ts.map +0 -7
  157. package/dist/chunks/chunk-OVDTF5CH.ts +0 -90
  158. package/dist/chunks/chunk-OVDTF5CH.ts.map +0 -7
  159. package/dist/chunks/chunk-Q32CQWPM.ts.map +0 -7
  160. package/dist/chunks/chunk-QLSN7AGX.ts.map +0 -7
  161. package/dist/chunks/chunk-SWUXA3MH.ts +0 -18
  162. package/dist/chunks/chunk-SWUXA3MH.ts.map +0 -7
  163. package/dist/chunks/chunk-T56RNRZJ.ts.map +0 -7
  164. package/dist/chunks/chunk-YFADF5ZQ.ts.map +0 -7
  165. package/dist/chunks/git-backend-IAANDL3K.ts.map +0 -7
  166. package/dist/chunks/manager-result-dispatcher-OTUMQM26.ts +0 -23
  167. package/dist/chunks/manager-ui-763NEKJY.ts.map +0 -7
  168. package/dist/chunks/remote-selection-ui-TZTJF32Y.ts +0 -22
  169. package/dist/chunks/s3-backend-XGXUOPUN.ts.map +0 -7
  170. package/dist/chunks/sync-operations-AR76EB6C.ts.map +0 -7
  171. package/dist/chunks/webdav-backend-3EGUONAT.ts.map +0 -7
  172. /package/dist/chunks/{chunk-UA7RXOIT.ts.map → chunk-4SBTAVFL.ts.map} +0 -0
  173. /package/dist/chunks/{chunk-HGJH56KR.ts.map → chunk-QHSQCK7L.ts.map} +0 -0
  174. /package/dist/chunks/{chunk-DZUSTGQV.ts.map → chunk-UQ3ALBIB.ts.map} +0 -0
  175. /package/dist/chunks/{chunk-E42GXD6D.ts.map → chunk-WKPX2SIC.ts.map} +0 -0
  176. /package/dist/chunks/{manager-result-dispatcher-OTUMQM26.ts.map → conflict-artifacts-BB2FZMIE.ts.map} +0 -0
  177. /package/dist/chunks/{remote-selection-ui-TZTJF32Y.ts.map → local-fields-6DGYOOIK.ts.map} +0 -0
  178. /package/dist/chunks/{setup-switch-5G2VNJHL.ts.map → manager-result-dispatcher-VIDT3QBU.ts.map} +0 -0
  179. /package/dist/chunks/{snapshot-JL5DFSXI.ts.map → merge-baseline-store-EVQAAML6.ts.map} +0 -0
  180. /package/dist/chunks/{sync-inspection-P3ENJLNF.ts.map → remote-selection-ui-SMF5CLFO.ts.map} +0 -0
  181. /package/dist/chunks/{sync-state-VHJUZ4YG.ts.map → setup-switch-KTUWL6DE.ts.map} +0 -0
@@ -0,0 +1,124 @@
1
+ // @generated by scripts/build-runtime.mjs; do not edit.
2
+ // @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
3
+ import {
4
+ regenerateSnapshotIdentity
5
+ } from "./chunk-QOFMXPM5.ts";
6
+ import {
7
+ jsonEqual,
8
+ parseSettingsDocument,
9
+ renderSettingsMembers
10
+ } from "./chunk-ZW5ZLGBI.ts";
11
+
12
+ // src/sync/local-fields.ts
13
+ import { createHash } from "node:crypto";
14
+ function normalizeLocalFields(value) {
15
+ if (value === void 0) return [];
16
+ if (!Array.isArray(value) || value.length > 128 || value.some(
17
+ (field) => typeof field !== "string" || !field || field.length > 128 || /[\p{Cc}\p{Cf}\p{Cs}]/u.test(field) || ["__proto__", "constructor", "prototype"].includes(field)
18
+ ))
19
+ throw new Error("localFields must be at most 128 safe settings.json root field names.");
20
+ if (new Set(value).size !== value.length) throw new Error("Duplicate localFields rule.");
21
+ for (const [first, second] of [
22
+ ["defaultProvider", "defaultModel"],
23
+ ["skills", "enableSkillCommands"],
24
+ ["queueMode", "steeringMode"],
25
+ ["websockets", "transport"],
26
+ ["enableAnalytics", "trackingId"]
27
+ ]) {
28
+ if (value.includes(first) !== value.includes(second))
29
+ throw new Error("Coupled settings fields must be excluded together.");
30
+ }
31
+ return [...value].sort();
32
+ }
33
+ function validateSnapshotFieldPolicy(snapshot) {
34
+ if (snapshot.version !== 1 && snapshot.version !== 2 && snapshot.version !== 3)
35
+ throw new Error("Unsupported snapshot format.");
36
+ if (snapshot.version === 1 && snapshot.localFields !== void 0)
37
+ throw new Error("Portable field policy requires snapshot version 2.");
38
+ if (snapshot.version === 2 || snapshot.version === 3) {
39
+ if (snapshot.version === 2 && snapshot.localFields === void 0)
40
+ throw new Error("Snapshot version 2 requires explicit localFields rules.");
41
+ normalizeLocalFields(snapshot.localFields);
42
+ }
43
+ }
44
+ function validatePortableSnapshot(snapshot) {
45
+ validateSnapshotFieldPolicy(snapshot);
46
+ if (snapshot.version !== 2 && snapshot.version !== 3 || !snapshot.localFields?.length) return;
47
+ try {
48
+ for (const entry of snapshot.files) {
49
+ if (typeof entry?.path !== "string" || entry.path.toLowerCase() !== "settings.json") continue;
50
+ const document = parseSettingsDocument(Buffer.from(entry.contentBase64, "base64"));
51
+ if (snapshot.localFields.some((field) => document.root.members.has(field))) throw new Error("Excluded field.");
52
+ }
53
+ } catch {
54
+ throw new Error("Portable snapshot settings.json retains excluded fields or has an unsupported document.");
55
+ }
56
+ }
57
+ function sameLocalFields(left, right) {
58
+ if (left === void 0 || right === void 0) return left === right;
59
+ return JSON.stringify(normalizeLocalFields(left)) === JSON.stringify(normalizeLocalFields(right));
60
+ }
61
+ function file(content) {
62
+ return {
63
+ path: "settings.json",
64
+ contentBase64: content.toString("base64"),
65
+ sha256: createHash("sha256").update(content).digest("hex")
66
+ };
67
+ }
68
+ function portableSnapshot(snapshot, fields) {
69
+ if (fields === void 0) return snapshot;
70
+ const excluded = new Set(normalizeLocalFields(fields));
71
+ const files = snapshot.files.map((entry) => {
72
+ if (entry.path !== "settings.json" || excluded.size === 0) return entry;
73
+ const document = parseSettingsDocument(Buffer.from(entry.contentBase64, "base64"));
74
+ const object = /* @__PURE__ */ Object.create(null);
75
+ for (const key of [...document.root.members.keys()].sort())
76
+ if (!excluded.has(key)) object[key] = document.root.members.get(key)?.node.value;
77
+ const canonical = (value) => Array.isArray(value) ? value.map(canonical) : value && typeof value === "object" ? Object.fromEntries(
78
+ Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, child]) => [key, canonical(child)])
79
+ ) : value;
80
+ return file(Buffer.from(`${JSON.stringify(canonical(object))}
81
+ `));
82
+ });
83
+ return regenerateSnapshotIdentity({ ...snapshot, version: 2, localFields: [...excluded], files });
84
+ }
85
+ function overlayLocalFields(portable, local, fields) {
86
+ if (!fields?.length) return portable;
87
+ const target = portable.files.find((entry) => entry.path === "settings.json");
88
+ const current = local.files.find((entry) => entry.path === "settings.json");
89
+ if (!target && !current) return portable;
90
+ const original = current ? parseSettingsDocument(Buffer.from(current.contentBase64, "base64")) : parseSettingsDocument(Buffer.from("{}"));
91
+ if (!target) {
92
+ if (fields.some((field) => original.root.members.has(field)))
93
+ throw new Error("Portable settings deletion requires manual review; local-only fields were preserved.");
94
+ return portable;
95
+ }
96
+ const incoming = parseSettingsDocument(Buffer.from(target.contentBase64, "base64"));
97
+ const selected = /* @__PURE__ */ new Map();
98
+ const excluded = new Set(fields);
99
+ for (const [key, member] of incoming.root.members) {
100
+ if (excluded.has(key)) continue;
101
+ const existing = original.root.members.get(key);
102
+ selected.set(
103
+ key,
104
+ existing && jsonEqual(existing.node.value, member.node.value) ? { document: original, node: existing.node } : { document: incoming, node: member.node }
105
+ );
106
+ }
107
+ for (const [key, member] of original.root.members)
108
+ if (excluded.has(key)) selected.set(key, { document: original, node: member.node });
109
+ const content = renderSettingsMembers(original, selected);
110
+ return regenerateSnapshotIdentity({
111
+ ...portable,
112
+ files: portable.files.map((entry) => entry.path === "settings.json" ? file(content) : entry)
113
+ });
114
+ }
115
+
116
+ export {
117
+ normalizeLocalFields,
118
+ validateSnapshotFieldPolicy,
119
+ validatePortableSnapshot,
120
+ sameLocalFields,
121
+ portableSnapshot,
122
+ overlayLocalFields
123
+ };
124
+ //# sourceMappingURL=chunk-A6HU4ERM.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/sync/local-fields.ts"],
4
+ "sourcesContent": ["import { createHash } from \"node:crypto\";\nimport { regenerateSnapshotIdentity } from \"../snapshot/snapshot.js\";\nimport type { Snapshot, SnapshotFile } from \"../snapshot/snapshot-types.js\";\nimport {\n type JsonDocument,\n type JsonNode,\n jsonEqual,\n parseSettingsDocument,\n renderSettingsMembers,\n} from \"./json-document.js\";\n\n/** Initial rules select whole root fields of the global settings.json only. */\nexport function normalizeLocalFields(value: unknown): string[] {\n if (value === undefined) return [];\n if (\n !Array.isArray(value) ||\n value.length > 128 ||\n value.some(\n (field) =>\n typeof field !== \"string\" ||\n !field ||\n field.length > 128 ||\n /[\\p{Cc}\\p{Cf}\\p{Cs}]/u.test(field) ||\n [\"__proto__\", \"constructor\", \"prototype\"].includes(field),\n )\n )\n throw new Error(\"localFields must be at most 128 safe settings.json root field names.\");\n if (new Set(value).size !== value.length) throw new Error(\"Duplicate localFields rule.\");\n for (const [first, second] of [\n [\"defaultProvider\", \"defaultModel\"],\n [\"skills\", \"enableSkillCommands\"],\n [\"queueMode\", \"steeringMode\"],\n [\"websockets\", \"transport\"],\n [\"enableAnalytics\", \"trackingId\"],\n ]) {\n if (value.includes(first) !== value.includes(second))\n throw new Error(\"Coupled settings fields must be excluded together.\");\n }\n return [...value].sort();\n}\n/** Shared transport contract: older snapshots cannot carry portable policy metadata. */\nexport function validateSnapshotFieldPolicy(snapshot: Pick<Snapshot, \"version\" | \"localFields\">) {\n if (snapshot.version !== 1 && snapshot.version !== 2 && snapshot.version !== 3)\n throw new Error(\"Unsupported snapshot format.\");\n if (snapshot.version === 1 && snapshot.localFields !== undefined)\n throw new Error(\"Portable field policy requires snapshot version 2.\");\n if (snapshot.version === 2 || snapshot.version === 3) {\n if (snapshot.version === 2 && snapshot.localFields === undefined)\n throw new Error(\"Snapshot version 2 requires explicit localFields rules.\");\n normalizeLocalFields(snapshot.localFields);\n }\n}\n/** Portable transport images must not carry values declared machine-local. Physical journal images are different. */\nexport function validatePortableSnapshot(snapshot: Snapshot) {\n validateSnapshotFieldPolicy(snapshot);\n if ((snapshot.version !== 2 && snapshot.version !== 3) || !snapshot.localFields?.length) return;\n try {\n for (const entry of snapshot.files) {\n if (typeof entry?.path !== \"string\" || entry.path.toLowerCase() !== \"settings.json\") continue;\n const document = parseSettingsDocument(Buffer.from(entry.contentBase64, \"base64\"));\n if (snapshot.localFields.some((field) => document.root.members.has(field))) throw new Error(\"Excluded field.\");\n }\n } catch {\n throw new Error(\"Portable snapshot settings.json retains excluded fields or has an unsupported document.\");\n }\n}\nexport function sameLocalFields(left: unknown, right: unknown) {\n if (left === undefined || right === undefined) return left === right;\n return JSON.stringify(normalizeLocalFields(left)) === JSON.stringify(normalizeLocalFields(right));\n}\nfunction file(content: Buffer): SnapshotFile {\n return {\n path: \"settings.json\",\n contentBase64: content.toString(\"base64\"),\n sha256: createHash(\"sha256\").update(content).digest(\"hex\"),\n };\n}\nexport function portableSnapshot(snapshot: Snapshot, fields: string[] | undefined): Snapshot {\n if (fields === undefined) return snapshot;\n const excluded = new Set(normalizeLocalFields(fields));\n const files = snapshot.files.map((entry) => {\n if (entry.path !== \"settings.json\" || excluded.size === 0) return entry;\n const document = parseSettingsDocument(Buffer.from(entry.contentBase64, \"base64\"));\n const object = Object.create(null);\n for (const key of [...document.root.members.keys()].sort())\n if (!excluded.has(key)) object[key] = document.root.members.get(key)?.node.value;\n const canonical = (value: unknown): unknown =>\n Array.isArray(value)\n ? value.map(canonical)\n : value && typeof value === \"object\"\n ? Object.fromEntries(\n Object.entries(value)\n .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))\n .map(([key, child]) => [key, canonical(child)]),\n )\n : value;\n return file(Buffer.from(`${JSON.stringify(canonical(object))}\\n`));\n });\n return regenerateSnapshotIdentity({ ...snapshot, version: 2, localFields: [...excluded], files });\n}\nexport function overlayLocalFields(portable: Snapshot, local: Snapshot, fields: string[] | undefined): Snapshot {\n if (!fields?.length) return portable;\n const target = portable.files.find((entry) => entry.path === \"settings.json\");\n const current = local.files.find((entry) => entry.path === \"settings.json\");\n if (!target && !current) return portable;\n const original = current\n ? parseSettingsDocument(Buffer.from(current.contentBase64, \"base64\"))\n : parseSettingsDocument(Buffer.from(\"{}\"));\n if (!target) {\n if (fields.some((field) => original.root.members.has(field)))\n throw new Error(\"Portable settings deletion requires manual review; local-only fields were preserved.\");\n return portable;\n }\n const incoming = parseSettingsDocument(Buffer.from(target.contentBase64, \"base64\"));\n const selected = new Map<string, { document: JsonDocument; node: JsonNode }>();\n const excluded = new Set(fields);\n for (const [key, member] of incoming.root.members) {\n if (excluded.has(key)) continue;\n const existing = original.root.members.get(key);\n selected.set(\n key,\n existing && jsonEqual(existing.node.value, member.node.value)\n ? { document: original, node: existing.node }\n : { document: incoming, node: member.node },\n );\n }\n for (const [key, member] of original.root.members)\n if (excluded.has(key)) selected.set(key, { document: original, node: member.node });\n const content = renderSettingsMembers(original, selected);\n return regenerateSnapshotIdentity({\n ...portable,\n files: portable.files.map((entry) => (entry.path === \"settings.json\" ? file(content) : entry)),\n });\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,17 @@
1
+ // @generated by scripts/build-runtime.mjs; do not edit.
2
+ // @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
3
+
4
+ // src/ui/terminal-text.ts
5
+ var TERMINAL_CONTROLS = /[\u0000-\u001f\u007f-\u009f\u061c\u200b-\u200f\u202a-\u202e\u2060-\u2069\ufeff]/gu;
6
+ function safeTerminalText(value) {
7
+ return value.replace(TERMINAL_CONTROLS, "?");
8
+ }
9
+ function snapshotPathLabel(value) {
10
+ return value.replace(TERMINAL_CONTROLS, (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`);
11
+ }
12
+
13
+ export {
14
+ safeTerminalText,
15
+ snapshotPathLabel
16
+ };
17
+ //# sourceMappingURL=chunk-AMJRUXFT.ts.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/ui/terminal-text.ts"],
4
+ "sourcesContent": ["// biome-ignore lint/suspicious/noControlCharactersInRegex: Terminal and display-direction controls.\nconst TERMINAL_CONTROLS = /[\\u0000-\\u001f\\u007f-\\u009f\\u061c\\u200b-\\u200f\\u202a-\\u202e\\u2060-\\u2069\\ufeff]/gu;\n\nexport function safeTerminalText(value: string) {\n return value.replace(TERMINAL_CONTROLS, \"?\");\n}\n\n/** Escape before joining a multiline review; raw filesystem paths remain unchanged. */\nexport function snapshotPathLabel(value: string) {\n return value.replace(TERMINAL_CONTROLS, (character) => `\\\\u${character.charCodeAt(0).toString(16).padStart(4, \"0\")}`);\n}\n"],
5
+ "mappings": ";;;;AACA,IAAM,oBAAoB;AAEnB,SAAS,iBAAiB,OAAe;AAC9C,SAAO,MAAM,QAAQ,mBAAmB,GAAG;AAC7C;AAGO,SAAS,kBAAkB,OAAe;AAC/C,SAAO,MAAM,QAAQ,mBAAmB,CAAC,cAAc,MAAM,UAAU,WAAW,CAAC,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE;AACtH;",
6
+ "names": []
7
+ }